File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / quagga / pimd / pim_igmp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:09:11 2016 UTC (7 years, 8 months ago) by misho
Branches: quagga, MAIN
CVS tags: v1_0_20160315, HEAD
quagga 1.0.20160315

    1: /*
    2:   PIM for Quagga
    3:   Copyright (C) 2008  Everton da Silva Marques
    4: 
    5:   This program is free software; you can redistribute it and/or modify
    6:   it under the terms of the GNU General Public License as published by
    7:   the Free Software Foundation; either version 2 of the License, or
    8:   (at your option) any later version.
    9: 
   10:   This program is distributed in the hope that it will be useful, but
   11:   WITHOUT ANY WARRANTY; without even the implied warranty of
   12:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   13:   General Public License for more details.
   14:   
   15:   You should have received a copy of the GNU General Public License
   16:   along with this program; see the file COPYING; if not, write to the
   17:   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   18:   MA 02110-1301 USA
   19:   
   20:   $QuaggaId: $Format:%an, %ai, %h$ $
   21: */
   22: 
   23: #include <zebra.h>
   24: 
   25: #include "memory.h"
   26: 
   27: #include "pimd.h"
   28: #include "pim_igmp.h"
   29: #include "pim_igmpv3.h"
   30: #include "pim_iface.h"
   31: #include "pim_sock.h"
   32: #include "pim_mroute.h"
   33: #include "pim_str.h"
   34: #include "pim_util.h"
   35: #include "pim_time.h"
   36: #include "pim_zebra.h"
   37: 
   38: #define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE        (1)
   39: #define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE        (2)
   40: #define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
   41: #define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
   42: #define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES      (5)
   43: #define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES      (6)
   44: 
   45: static void group_timer_off(struct igmp_group *group);
   46: 
   47: static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
   48: 					     struct in_addr group_addr);
   49: 
   50: static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, 
   51:                           uint32_t pim_options)
   52: {
   53:   int fd;
   54:   int join = 0;
   55:   struct in_addr group;
   56: 
   57:   fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */);
   58:   if (fd < 0)
   59:     return -1;
   60: 
   61:   if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
   62:     if (inet_aton(PIM_ALL_ROUTERS, &group)) {
   63:       if (!pim_socket_join(fd, group, ifaddr, ifindex))
   64: 	++join;
   65:     }
   66:     else {
   67:       zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
   68: 		__FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
   69: 		PIM_ALL_ROUTERS, errno, safe_strerror(errno));
   70:     }
   71:   }
   72: 
   73:   /*
   74:     IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
   75:     IGMP routers must receive general queries for querier election.
   76:   */
   77:   if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
   78:     if (!pim_socket_join(fd, group, ifaddr, ifindex))
   79:       ++join;
   80:   }
   81:   else {
   82:     zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
   83: 	      __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
   84: 	      PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
   85:   }
   86: 
   87:   if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
   88:     if (!pim_socket_join(fd, group, ifaddr, ifindex)) {
   89:       ++join;
   90:     }
   91:   }
   92:   else {
   93:       zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
   94: 		__FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
   95: 		PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
   96:   }    
   97: 
   98:   if (!join) {
   99:     zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
  100: 	     fd, inet_ntoa(ifaddr));
  101:     close(fd);
  102:     fd = -1;
  103:   }
  104: 
  105:   return fd;
  106: }
  107: 
  108: #undef IGMP_SOCK_DUMP
  109: 
  110: #ifdef IGMP_SOCK_DUMP
  111: static void igmp_sock_dump(array_t *igmp_sock_array)
  112: {
  113:   int size = array_size(igmp_sock_array);
  114:   for (int i = 0; i < size; ++i) {
  115:     
  116:     struct igmp_sock *igmp = array_get(igmp_sock_array, i);
  117:     
  118:     zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
  119: 	       __FILE__, __PRETTY_FUNCTION__,
  120: 	       i, size,
  121: 	       inet_ntoa(igmp->ifaddr),
  122: 	       igmp->fd);
  123:   }
  124: }
  125: #endif
  126: 
  127: struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
  128: 					      struct in_addr ifaddr)
  129: {
  130:   struct listnode  *sock_node;
  131:   struct igmp_sock *igmp;
  132: 
  133: #ifdef IGMP_SOCK_DUMP
  134:   igmp_sock_dump(igmp_sock_list);
  135: #endif
  136: 
  137:   for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
  138:     if (ifaddr.s_addr == igmp->ifaddr.s_addr)
  139:       return igmp;
  140: 
  141:   return 0;
  142: }
  143: 
  144: struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
  145: 					 int fd)
  146: {
  147:   struct listnode  *sock_node;
  148:   struct igmp_sock *igmp;
  149: 
  150:   for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
  151:     if (fd == igmp->fd)
  152:       return igmp;
  153: 
  154:   return 0;
  155: }
  156: 
  157: static int pim_igmp_other_querier_expire(struct thread *t)
  158: {
  159:   struct igmp_sock *igmp;
  160: 
  161:   zassert(t);
  162:   igmp = THREAD_ARG(t);
  163:   zassert(igmp);
  164: 
  165:   zassert(igmp->t_other_querier_timer);
  166:   zassert(!igmp->t_igmp_query_timer);
  167: 
  168:   if (PIM_DEBUG_IGMP_TRACE) {
  169:     char ifaddr_str[100];
  170:     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  171:     zlog_debug("%s: Querier %s resuming",
  172: 	       __PRETTY_FUNCTION__,
  173: 	       ifaddr_str);
  174:   }
  175: 
  176:   igmp->t_other_querier_timer = 0;
  177: 
  178:   /*
  179:     We are the current querier, then
  180:     re-start sending general queries.
  181:   */
  182:   pim_igmp_general_query_on(igmp);
  183: 
  184:   return 0;
  185: }
  186: 
  187: void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
  188: {
  189:   long other_querier_present_interval_msec;
  190:   struct pim_interface *pim_ifp;
  191: 
  192:   zassert(igmp);
  193:   zassert(igmp->interface);
  194:   zassert(igmp->interface->info);
  195: 
  196:   pim_ifp = igmp->interface->info;
  197: 
  198:   if (igmp->t_other_querier_timer) {
  199:     /*
  200:       There is other querier present already,
  201:       then reset the other-querier-present timer.
  202:     */
  203: 
  204:     if (PIM_DEBUG_IGMP_TRACE) {
  205:       char ifaddr_str[100];
  206:       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  207:       zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
  208: 		 ifaddr_str);
  209:     }
  210: 
  211:     THREAD_OFF(igmp->t_other_querier_timer);
  212:     zassert(!igmp->t_other_querier_timer);
  213:   }
  214:   else {
  215:     /*
  216:       We are the current querier, then stop sending general queries:
  217:       igmp->t_igmp_query_timer = 0;
  218:     */
  219:     pim_igmp_general_query_off(igmp);
  220:   }
  221: 
  222:   /*
  223:     Since this socket is starting the other-querier-present timer,
  224:     there should not be periodic query timer for this socket.
  225:    */
  226:   zassert(!igmp->t_igmp_query_timer);
  227: 
  228:   /*
  229:     RFC 3376: 8.5. Other Querier Present Interval
  230: 
  231:     The Other Querier Present Interval is the length of time that must
  232:     pass before a multicast router decides that there is no longer
  233:     another multicast router which should be the querier.  This value
  234:     MUST be ((the Robustness Variable) times (the Query Interval)) plus
  235:     (one half of one Query Response Interval).
  236: 
  237:     other_querier_present_interval_msec = \
  238:       igmp->querier_robustness_variable * \
  239:       1000 * igmp->querier_query_interval + \
  240:       100 * (pim_ifp->query_max_response_time_dsec >> 1);
  241:   */
  242:   other_querier_present_interval_msec =
  243:     PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
  244: 		       igmp->querier_query_interval,
  245: 		       pim_ifp->igmp_query_max_response_time_dsec);
  246:   
  247:   if (PIM_DEBUG_IGMP_TRACE) {
  248:     char ifaddr_str[100];
  249:     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  250:     zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
  251: 	       ifaddr_str,
  252: 	       other_querier_present_interval_msec / 1000,
  253: 	       other_querier_present_interval_msec % 1000);
  254:   }
  255:   
  256:   THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer,
  257: 		       pim_igmp_other_querier_expire,
  258: 		       igmp, other_querier_present_interval_msec);
  259: }
  260: 
  261: void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
  262: {
  263:   zassert(igmp);
  264: 
  265:   if (PIM_DEBUG_IGMP_TRACE) {
  266:     if (igmp->t_other_querier_timer) {
  267:       char ifaddr_str[100];
  268:       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  269:       zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
  270: 		 ifaddr_str, igmp->fd, igmp->interface->name);
  271:     }
  272:   }
  273:   THREAD_OFF(igmp->t_other_querier_timer);
  274:   zassert(!igmp->t_other_querier_timer);
  275: }
  276: 
  277: static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
  278: 			   int max_resp_code,
  279: 			   struct in_addr from, const char *from_str,
  280: 			   char *igmp_msg, int igmp_msg_len)
  281: {
  282:   struct interface     *ifp;
  283:   struct pim_interface *pim_ifp;
  284:   uint8_t               resv_s_qrv = 0;
  285:   uint8_t               s_flag = 0;
  286:   uint8_t               qrv = 0;
  287:   struct in_addr        group_addr;
  288:   uint16_t              recv_checksum;
  289:   uint16_t              checksum;
  290:   int                   i;
  291: 
  292:   //group_addr = *(struct in_addr *)(igmp_msg + 4);
  293:   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
  294: 
  295:   ifp = igmp->interface;
  296:   pim_ifp = ifp->info;
  297: 
  298:   recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
  299: 
  300:   /* for computing checksum */
  301:   *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
  302: 
  303:   checksum = in_cksum(igmp_msg, igmp_msg_len);
  304:   if (checksum != recv_checksum) {
  305:     zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
  306: 	      query_version, from_str, ifp->name, recv_checksum, checksum);
  307:     return -1;
  308:   }
  309: 
  310:   if (PIM_DEBUG_IGMP_PACKETS) {
  311:     char group_str[100];
  312:     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  313:     zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
  314: 	       query_version, from_str, ifp->name,
  315: 	       igmp_msg_len, checksum, group_str);
  316:   }
  317: 
  318:   /*
  319:     RFC 3376: 6.6.2. Querier Election
  320: 
  321:     When a router receives a query with a lower IP address, it sets
  322:     the Other-Querier-Present timer to Other Querier Present Interval
  323:     and ceases to send queries on the network if it was the previously
  324:     elected querier.
  325:    */
  326:   if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
  327:     
  328:     if (PIM_DEBUG_IGMP_TRACE) {
  329:       char ifaddr_str[100];
  330:       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  331:       zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
  332: 		 ifp->name,
  333: 		 ifaddr_str, ntohl(igmp->ifaddr.s_addr),
  334: 		 from_str, ntohl(from.s_addr));
  335:     }
  336: 
  337:     pim_igmp_other_querier_timer_on(igmp);
  338:   }
  339: 
  340:   if (query_version == 3) {
  341:     /*
  342:       RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
  343: 
  344:       Routers adopt the QRV value from the most recently received Query
  345:       as their own [Robustness Variable] value, unless that most
  346:       recently received QRV was zero, in which case the receivers use
  347:       the default [Robustness Variable] value specified in section 8.1
  348:       or a statically configured value.
  349:     */
  350:     resv_s_qrv = igmp_msg[8];
  351:     qrv = 7 & resv_s_qrv;
  352:     igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
  353:   }
  354: 
  355:   /*
  356:     RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
  357: 
  358:     Multicast routers that are not the current querier adopt the QQI
  359:     value from the most recently received Query as their own [Query
  360:     Interval] value, unless that most recently received QQI was zero,
  361:     in which case the receiving routers use the default.
  362:   */
  363:   if (igmp->t_other_querier_timer && query_version == 3) {
  364:     /* other querier present */
  365:     uint8_t  qqic;
  366:     uint16_t qqi;
  367:     qqic = igmp_msg[9];
  368:     qqi = igmp_msg_decode8to16(qqic);
  369:     igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
  370: 
  371:     if (PIM_DEBUG_IGMP_TRACE) {
  372:       char ifaddr_str[100];
  373:       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  374:       zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
  375: 		 ifaddr_str,
  376: 		 qqi ? "recv-non-default" : "default",
  377: 		 igmp->querier_query_interval,
  378: 		 qqic,
  379: 		 from_str);
  380:     }
  381:   }
  382: 
  383:   /*
  384:     RFC 3376: 6.6.1. Timer Updates
  385: 
  386:     When a router sends or receives a query with a clear Suppress
  387:     Router-Side Processing flag, it must update its timers to reflect
  388:     the correct timeout values for the group or sources being queried.
  389: 
  390:     General queries don't trigger timer update.
  391:   */
  392:   if (query_version == 3) {
  393:     s_flag = (1 << 3) & resv_s_qrv;
  394:   }
  395:   else {
  396:     /* Neither V1 nor V2 have this field. Pimd should really go into
  397:      * a compatibility mode here and run as V2 (or V1) but it doesn't
  398:      * so for now, lets just set the flag to suppress these timer updates.
  399:      */
  400:     s_flag = 1;
  401:   }
  402:   
  403:   if (!s_flag) {
  404:     /* s_flag is clear */
  405: 
  406:     if (PIM_INADDR_IS_ANY(group_addr)) {
  407:       /* this is a general query */
  408: 
  409:       /* log that general query should have the s_flag set */
  410:       zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
  411: 		query_version, from_str, ifp->name);
  412:     }
  413:     else {
  414:       struct igmp_group *group;
  415: 
  416:       /* this is a non-general query: perform timer updates */
  417: 
  418:       group = find_group_by_addr(igmp, group_addr);
  419:       if (group) {
  420: 	int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
  421: 
  422: 	/*
  423: 	  RFC 3376: 6.6.1. Timer Updates
  424: 	  Query Q(G,A): Source Timer for sources in A are lowered to LMQT
  425: 	  Query Q(G): Group Timer is lowered to LMQT
  426: 	*/
  427: 	if (recv_num_sources < 1) {
  428: 	  /* Query Q(G): Group Timer is lowered to LMQT */
  429: 
  430: 	  igmp_group_timer_lower_to_lmqt(group);
  431: 	}
  432: 	else {
  433: 	  /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
  434: 
  435: 	  /* Scan sources in query and lower their timers to LMQT */
  436: 	  struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
  437: 	  for (i = 0; i < recv_num_sources; ++i) {
  438: 	    //struct in_addr src_addr = sources[i];
  439: 	    //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
  440: 	    struct in_addr src_addr;
  441: 	    struct igmp_source *src;
  442:             memcpy(&src_addr, sources + i, sizeof(struct in_addr));
  443: 	    src = igmp_find_source_by_addr(group, src_addr);
  444: 	    if (src) {
  445: 	      igmp_source_timer_lower_to_lmqt(src);
  446: 	    }
  447: 	  }
  448: 	}
  449: 
  450:       }
  451:       else {
  452: 	char group_str[100];
  453: 	pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
  454: 	zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
  455: 		  query_version, from_str, ifp->name, group_str);
  456:       }
  457:     }
  458:   } /* s_flag is clear: timer updates */
  459:   
  460:   return 0;
  461: }
  462: 
  463: static int igmp_v3_report(struct igmp_sock *igmp,
  464: 			  struct in_addr from, const char *from_str,
  465: 			  char *igmp_msg, int igmp_msg_len)
  466: {
  467:   uint16_t          recv_checksum;
  468:   uint16_t          checksum;
  469:   int               num_groups;
  470:   uint8_t          *group_record;
  471:   uint8_t          *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
  472:   struct interface *ifp = igmp->interface;
  473:   int               i;
  474: 
  475:   if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
  476:     zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
  477: 	      from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
  478:     return -1;
  479:   }
  480: 
  481:   recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
  482: 
  483:   /* for computing checksum */
  484:   *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
  485: 
  486:   checksum = in_cksum(igmp_msg, igmp_msg_len);
  487:   if (checksum != recv_checksum) {
  488:     zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
  489: 	      from_str, ifp->name, recv_checksum, checksum);
  490:     return -1;
  491:   }
  492: 
  493:   num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
  494:   if (num_groups < 1) {
  495:     zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
  496: 	      from_str, ifp->name);
  497:     return -1;
  498:   }
  499: 
  500:   if (PIM_DEBUG_IGMP_PACKETS) {
  501:     zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
  502: 	       from_str, ifp->name, igmp_msg_len, checksum, num_groups);
  503:   }
  504: 
  505:   group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
  506: 
  507:   /* Scan groups */
  508:   for (i = 0; i < num_groups; ++i) {
  509:     struct in_addr  rec_group;
  510:     uint8_t        *sources;
  511:     uint8_t        *src;
  512:     int             rec_type;
  513:     int             rec_auxdatalen;
  514:     int             rec_num_sources;
  515:     int             j;
  516: 
  517:     if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
  518:       zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
  519: 		from_str, ifp->name);
  520:       return -1;
  521:     }
  522: 
  523:     rec_type        = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
  524:     rec_auxdatalen  = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
  525:     rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
  526: 
  527:     //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
  528:     memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
  529: 
  530:     if (PIM_DEBUG_IGMP_PACKETS) {
  531:       zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
  532: 		 from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
  533:     }
  534: 
  535:     /* Scan sources */
  536:     
  537:     sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
  538: 
  539:     for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
  540: 
  541:       if ((src + 4) > report_pastend) {
  542: 	zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
  543: 		  from_str, ifp->name);
  544: 	return -1;
  545:       }
  546: 
  547:       if (PIM_DEBUG_IGMP_PACKETS) {
  548: 	char src_str[200];
  549: 
  550: 	if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
  551: 	  sprintf(src_str, "<source?>");
  552: 	
  553: 	zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
  554: 		   from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
  555:       }
  556:     } /* for (sources) */
  557: 
  558:     switch (rec_type) {
  559:     case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
  560:       igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  561:       break;
  562:     case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
  563:       igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  564:       break;
  565:     case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
  566:       igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  567:       break;
  568:     case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
  569:       igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  570:       break;
  571:     case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
  572:       igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  573:       break;
  574:     case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
  575:       igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
  576:       break;
  577:     default:
  578:       zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
  579: 		from_str, ifp->name, rec_type);
  580:     }
  581: 
  582:     group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
  583: 
  584:   } /* for (group records) */
  585: 
  586:   return 0;
  587: }
  588: 
  589: static void on_trace(const char *label,
  590: 		     struct interface *ifp, struct in_addr from)
  591: {
  592:   if (PIM_DEBUG_IGMP_TRACE) {
  593:     char from_str[100];
  594:     pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
  595:     zlog_debug("%s: from %s on %s",
  596: 	       label, from_str, ifp->name);
  597:   }
  598: }
  599: 
  600: static int igmp_v2_report(struct igmp_sock *igmp,
  601: 			  struct in_addr from, const char *from_str,
  602: 			  char *igmp_msg, int igmp_msg_len)
  603: {
  604:   struct interface *ifp = igmp->interface;
  605:   struct igmp_group *group;
  606:   struct in_addr group_addr;
  607: 
  608:   on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
  609: 
  610:   if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
  611:     zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
  612: 	      from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
  613:     return -1;
  614:   }
  615: 
  616:   if (PIM_DEBUG_IGMP_TRACE) {
  617:     zlog_warn("%s %s: FIXME WRITEME",
  618: 	      __FILE__, __PRETTY_FUNCTION__);
  619:   }
  620: 
  621:   //group_addr = *(struct in_addr *)(igmp_msg + 4);
  622:   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
  623: 
  624:   /* non-existant group is created as INCLUDE {empty} */
  625:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
  626:   if (!group) {
  627:     return -1;
  628:   }
  629: 
  630:   group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
  631: 
  632:   return 0;
  633: }
  634: 
  635: static int igmp_v2_leave(struct igmp_sock *igmp,
  636: 			 struct in_addr from, const char *from_str,
  637: 			 char *igmp_msg, int igmp_msg_len)
  638: {
  639:   struct interface *ifp = igmp->interface;
  640: 
  641:   on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
  642: 
  643:   if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
  644:     zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
  645: 	      from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
  646:     return -1;
  647:   }
  648: 
  649:   if (PIM_DEBUG_IGMP_TRACE) {
  650:     zlog_warn("%s %s: FIXME WRITEME",
  651: 	      __FILE__, __PRETTY_FUNCTION__);
  652:   }
  653: 
  654:   return 0;
  655: }
  656: 
  657: static int igmp_v1_report(struct igmp_sock *igmp,
  658: 			  struct in_addr from, const char *from_str,
  659: 			  char *igmp_msg, int igmp_msg_len)
  660: {
  661:   struct interface *ifp = igmp->interface;
  662:   struct igmp_group *group;
  663:   struct in_addr group_addr;
  664: 
  665:   on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
  666: 
  667:   if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
  668:     zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
  669: 	      from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
  670:     return -1;
  671:   }
  672: 
  673:   if (PIM_DEBUG_IGMP_TRACE) {
  674:     zlog_warn("%s %s: FIXME WRITEME",
  675: 	      __FILE__, __PRETTY_FUNCTION__);
  676:   }
  677: 
  678:   //group_addr = *(struct in_addr *)(igmp_msg + 4);
  679:   memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
  680: 
  681:   /* non-existant group is created as INCLUDE {empty} */
  682:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
  683:   if (!group) {
  684:     return -1;
  685:   }
  686: 
  687:   group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
  688: 
  689:   return 0;
  690: }
  691: 
  692: int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
  693: {
  694:   struct ip *ip_hdr;
  695:   size_t ip_hlen; /* ip header length in bytes */
  696:   char *igmp_msg;
  697:   int igmp_msg_len;
  698:   int msg_type;
  699:   char from_str[100];
  700:   char to_str[100];
  701:     
  702:   if (len < sizeof(*ip_hdr)) {
  703:     zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
  704: 	      len, sizeof(*ip_hdr));
  705:     return -1;
  706:   }
  707: 
  708:   ip_hdr = (struct ip *) buf;
  709: 
  710:   pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str));
  711:   pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str));
  712: 
  713:   ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
  714: 
  715:   if (PIM_DEBUG_IGMP_PACKETS) {
  716:     zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
  717: 	       from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
  718:   }
  719: 
  720:   if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
  721:     zlog_warn("IP packet protocol=%d is not IGMP=%d",
  722: 	      ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
  723:     return -1;
  724:   }
  725: 
  726:   if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
  727:     zlog_warn("IP packet header size=%zu shorter than minimum=%d",
  728: 	      ip_hlen, PIM_IP_HEADER_MIN_LEN);
  729:     return -1;
  730:   }
  731:   if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
  732:     zlog_warn("IP packet header size=%zu greater than maximum=%d",
  733: 	      ip_hlen, PIM_IP_HEADER_MAX_LEN);
  734:     return -1;
  735:   }
  736: 
  737:   igmp_msg = buf + ip_hlen;
  738:   msg_type = *igmp_msg;
  739:   igmp_msg_len = len - ip_hlen;
  740: 
  741:   if (PIM_DEBUG_IGMP_PACKETS) {
  742:     zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
  743: 	       from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
  744: 	       igmp_msg_len);
  745:   }
  746: 
  747:   if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
  748:     zlog_warn("IGMP message size=%d shorter than minimum=%d",
  749: 	      igmp_msg_len, PIM_IGMP_MIN_LEN);
  750:     return -1;
  751:   }
  752: 
  753:   switch (msg_type) {
  754:   case PIM_IGMP_MEMBERSHIP_QUERY:
  755:     {
  756:       int max_resp_code = igmp_msg[1];
  757:       int query_version;
  758: 
  759:       /*
  760: 	RFC 3376: 7.1. Query Version Distinctions
  761: 	IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
  762: 	IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
  763: 	IGMPv3 Query: length >= 12 octets
  764:       */
  765: 
  766:       if (igmp_msg_len == 8) {
  767: 	query_version = max_resp_code ? 2 : 1;
  768:       }
  769:       else if (igmp_msg_len >= 12) {
  770: 	query_version = 3;
  771:       }
  772:       else {
  773: 	zlog_warn("Unknown IGMP query version");
  774: 	return -1;
  775:       }
  776: 
  777:       return recv_igmp_query(igmp, query_version, max_resp_code,
  778: 			     ip_hdr->ip_src, from_str,
  779: 			     igmp_msg, igmp_msg_len);
  780:     }
  781: 
  782:   case PIM_IGMP_V3_MEMBERSHIP_REPORT:
  783:     return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
  784: 			  igmp_msg, igmp_msg_len);
  785: 
  786:   case PIM_IGMP_V2_MEMBERSHIP_REPORT:
  787:     return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
  788: 			  igmp_msg, igmp_msg_len);
  789: 
  790:   case PIM_IGMP_V1_MEMBERSHIP_REPORT:
  791:     return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
  792: 			  igmp_msg, igmp_msg_len);
  793: 
  794:   case PIM_IGMP_V2_LEAVE_GROUP:
  795:     return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
  796: 			 igmp_msg, igmp_msg_len);
  797:   }
  798: 
  799:   zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
  800: 
  801:   return -1;
  802: }
  803: 
  804: static int pim_igmp_general_query(struct thread *t);
  805: 
  806: void pim_igmp_general_query_on(struct igmp_sock *igmp)
  807: {
  808:   struct pim_interface *pim_ifp;
  809:   int startup_mode;
  810:   int query_interval;
  811: 
  812:   zassert(igmp);
  813:   zassert(igmp->interface);
  814: 
  815:   /*
  816:     Since this socket is starting as querier,
  817:     there should not exist a timer for other-querier-present.
  818:    */
  819:   zassert(!igmp->t_other_querier_timer);
  820:   pim_ifp = igmp->interface->info;
  821:   zassert(pim_ifp);
  822: 
  823:   /*
  824:     RFC 3376: 8.6. Startup Query Interval
  825: 
  826:     The Startup Query Interval is the interval between General Queries
  827:     sent by a Querier on startup.  Default: 1/4 the Query Interval.
  828:   */
  829:   startup_mode = igmp->startup_query_count > 0;
  830:   if (startup_mode) {
  831:     --igmp->startup_query_count;
  832: 
  833:     /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
  834:     query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
  835:   }
  836:   else {
  837:     query_interval = igmp->querier_query_interval;
  838:   }
  839: 
  840:   if (PIM_DEBUG_IGMP_TRACE) {
  841:     char ifaddr_str[100];
  842:     pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  843:     zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
  844: 	       ifaddr_str,
  845: 	       query_interval,
  846: 	       startup_mode ? "startup" : "non-startup",
  847: 	       igmp->fd);
  848:   }
  849:   igmp->t_igmp_query_timer = 0;
  850:   zassert(!igmp->t_igmp_query_timer);
  851:   THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
  852: 		  pim_igmp_general_query,
  853: 		  igmp, query_interval);
  854: }
  855: 
  856: void pim_igmp_general_query_off(struct igmp_sock *igmp)
  857: {
  858:   zassert(igmp);
  859: 
  860:   if (PIM_DEBUG_IGMP_TRACE) {
  861:     if (igmp->t_igmp_query_timer) {
  862:       char ifaddr_str[100];
  863:       pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
  864:       zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
  865: 		 ifaddr_str, igmp->fd, igmp->interface->name);
  866:     }
  867:   }
  868:   THREAD_OFF(igmp->t_igmp_query_timer);
  869:   zassert(!igmp->t_igmp_query_timer);
  870: }
  871: 
  872: /* Issue IGMP general query */
  873: static int pim_igmp_general_query(struct thread *t)
  874: {
  875:   char   query_buf[PIM_IGMP_BUFSIZE_WRITE];
  876:   struct igmp_sock *igmp;
  877:   struct in_addr dst_addr;
  878:   struct in_addr group_addr;
  879:   struct pim_interface *pim_ifp;
  880: 
  881:   zassert(t);
  882: 
  883:   igmp = THREAD_ARG(t);
  884: 
  885:   zassert(igmp);
  886:   zassert(igmp->interface);
  887:   zassert(igmp->interface->info);
  888: 
  889:   pim_ifp = igmp->interface->info;
  890: 
  891:   /*
  892:     RFC3376: 4.1.12. IP Destination Addresses for Queries
  893: 
  894:     In IGMPv3, General Queries are sent with an IP destination address
  895:     of 224.0.0.1, the all-systems multicast address.  Group-Specific
  896:     and Group-and-Source-Specific Queries are sent with an IP
  897:     destination address equal to the multicast address of interest.
  898:   */
  899: 
  900:   dst_addr.s_addr   = htonl(INADDR_ALLHOSTS_GROUP);
  901:   group_addr.s_addr = PIM_NET_INADDR_ANY;
  902: 
  903:   if (PIM_DEBUG_IGMP_TRACE) {
  904:     char querier_str[100];
  905:     char dst_str[100];
  906:     pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
  907: 		   sizeof(querier_str));
  908:     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
  909:     zlog_debug("Querier %s issuing IGMP general query to %s on %s",
  910: 	       querier_str, dst_str, igmp->interface->name);
  911:   }
  912: 
  913:   pim_igmp_send_membership_query(0 /* igmp_group */,
  914: 				 igmp->fd,
  915: 				 igmp->interface->name,
  916: 				 query_buf,
  917: 				 sizeof(query_buf),
  918: 				 0 /* num_sources */,
  919: 				 dst_addr,
  920: 				 group_addr,
  921: 				 pim_ifp->igmp_query_max_response_time_dsec,
  922: 				 1 /* s_flag: always set for general queries */,
  923: 				 igmp->querier_robustness_variable,
  924: 				 igmp->querier_query_interval);
  925: 
  926:   pim_igmp_general_query_on(igmp);
  927: 
  928:   return 0;
  929: }
  930: 
  931: static int pim_igmp_read(struct thread *t);
  932: 
  933: static void igmp_read_on(struct igmp_sock *igmp)
  934: {
  935:   zassert(igmp);
  936: 
  937:   if (PIM_DEBUG_IGMP_TRACE) {
  938:     zlog_debug("Scheduling READ event on IGMP socket fd=%d",
  939: 	       igmp->fd);
  940:   }
  941:   igmp->t_igmp_read = 0;
  942:   zassert(!igmp->t_igmp_read);
  943:   THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
  944: }
  945: 
  946: static int pim_igmp_read(struct thread *t)
  947: {
  948:   struct igmp_sock *igmp;
  949:   int fd;
  950:   struct sockaddr_in from;
  951:   struct sockaddr_in to;
  952:   socklen_t fromlen = sizeof(from);
  953:   socklen_t tolen = sizeof(to);
  954:   uint8_t buf[PIM_IGMP_BUFSIZE_READ];
  955:   int len;
  956:   ifindex_t ifindex = -1;
  957:   int result = -1; /* defaults to bad */
  958: 
  959:   zassert(t);
  960: 
  961:   igmp = THREAD_ARG(t);
  962: 
  963:   zassert(igmp);
  964: 
  965:   fd = THREAD_FD(t);
  966: 
  967:   zassert(fd == igmp->fd);
  968: 
  969:   len = pim_socket_recvfromto(fd, buf, sizeof(buf),
  970: 			      &from, &fromlen,
  971: 			      &to, &tolen,
  972: 			      &ifindex);
  973:   if (len < 0) {
  974:     zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
  975: 	      fd, errno, safe_strerror(errno));
  976:     goto done;
  977:   }
  978: 
  979:   if (PIM_DEBUG_IGMP_PACKETS) {
  980:     char from_str[100];
  981:     char to_str[100];
  982:     
  983:     if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
  984:       sprintf(from_str, "<from?>");
  985:     if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
  986:       sprintf(to_str, "<to?>");
  987:     
  988:     zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
  989: 	       len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
  990:   }
  991: 
  992: #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
  993:   /* ifindex sanity check */
  994:   if (ifindex != (int) igmp->interface->ifindex) {
  995:     char from_str[100];
  996:     char to_str[100];
  997:     struct interface *ifp;
  998: 
  999:     if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
 1000:       sprintf(from_str, "<from?>");
 1001:     if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
 1002:       sprintf(to_str, "<to?>");
 1003: 
 1004:     ifp = if_lookup_by_index(ifindex);
 1005:     if (ifp) {
 1006:       zassert(ifindex == (int) ifp->ifindex);
 1007:     }
 1008: 
 1009: #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
 1010:     zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
 1011: 	      from_str, to_str, fd,
 1012: 	      ifindex, ifp ? ifp->name : "<if-notfound>",
 1013: 	      igmp->interface->ifindex, igmp->interface->name);
 1014: #endif
 1015:     goto done;
 1016:   }
 1017: #endif
 1018: 
 1019:   if (pim_igmp_packet(igmp, (char *)buf, len)) {
 1020:     goto done;
 1021:   }
 1022: 
 1023:   result = 0; /* good */
 1024: 
 1025:  done:
 1026:   igmp_read_on(igmp);
 1027: 
 1028:   return result;
 1029: }
 1030: 
 1031: static void sock_close(struct igmp_sock *igmp)
 1032: {
 1033:   pim_igmp_other_querier_timer_off(igmp);
 1034:   pim_igmp_general_query_off(igmp);
 1035: 
 1036:   if (PIM_DEBUG_IGMP_TRACE) {
 1037:     if (igmp->t_igmp_read) {
 1038:       zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
 1039: 		 inet_ntoa(igmp->ifaddr), igmp->fd,
 1040: 		 igmp->interface->name);
 1041:     }
 1042:   }
 1043:   THREAD_OFF(igmp->t_igmp_read);
 1044:   zassert(!igmp->t_igmp_read);
 1045:   
 1046:   if (close(igmp->fd)) {
 1047:     zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
 1048: 	     inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
 1049: 	     errno, safe_strerror(errno));
 1050:   }
 1051:   
 1052:   if (PIM_DEBUG_IGMP_TRACE) {
 1053:     zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
 1054: 	       inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
 1055:   }
 1056: }
 1057: 
 1058: void igmp_startup_mode_on(struct igmp_sock *igmp)
 1059: {
 1060:   struct pim_interface *pim_ifp;
 1061: 
 1062:   pim_ifp = igmp->interface->info;
 1063: 
 1064:   /*
 1065:     RFC 3376: 8.7. Startup Query Count
 1066: 
 1067:     The Startup Query Count is the number of Queries sent out on
 1068:     startup, separated by the Startup Query Interval.  Default: the
 1069:     Robustness Variable.
 1070:   */
 1071:   igmp->startup_query_count = igmp->querier_robustness_variable;
 1072: 
 1073:   /*
 1074:     Since we're (re)starting, reset QQI to default Query Interval
 1075:   */
 1076:   igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
 1077: }
 1078: 
 1079: static void igmp_group_free(struct igmp_group *group)
 1080: {
 1081:   zassert(!group->t_group_query_retransmit_timer);
 1082:   zassert(!group->t_group_timer);
 1083:   zassert(group->group_source_list);
 1084:   zassert(!listcount(group->group_source_list));
 1085: 
 1086:   list_free(group->group_source_list);
 1087: 
 1088:   XFREE(MTYPE_PIM_IGMP_GROUP, group);
 1089: }
 1090: 
 1091: static void igmp_group_delete(struct igmp_group *group)
 1092: {
 1093:   struct listnode *src_node;
 1094:   struct listnode *src_nextnode;
 1095:   struct igmp_source *src;
 1096: 
 1097:   if (PIM_DEBUG_IGMP_TRACE) {
 1098:     char group_str[100];
 1099:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1100:     zlog_debug("Deleting IGMP group %s from socket %d interface %s",
 1101: 	       group_str,
 1102: 	       group->group_igmp_sock->fd,
 1103: 	       group->group_igmp_sock->interface->name);
 1104:   }
 1105: 
 1106:   for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
 1107:     igmp_source_delete(src);
 1108:   }
 1109: 
 1110:   if (group->t_group_query_retransmit_timer) {
 1111:     THREAD_OFF(group->t_group_query_retransmit_timer);
 1112:     zassert(!group->t_group_query_retransmit_timer);
 1113:   }
 1114: 
 1115:   group_timer_off(group);
 1116:   listnode_delete(group->group_igmp_sock->igmp_group_list, group);
 1117:   igmp_group_free(group);
 1118: }
 1119: 
 1120: void igmp_group_delete_empty_include(struct igmp_group *group)
 1121: {
 1122:   zassert(!group->group_filtermode_isexcl);
 1123:   zassert(!listcount(group->group_source_list));
 1124: 
 1125:   igmp_group_delete(group);
 1126: }
 1127: 
 1128: void igmp_sock_free(struct igmp_sock *igmp)
 1129: {
 1130:   zassert(!igmp->t_igmp_read);
 1131:   zassert(!igmp->t_igmp_query_timer);
 1132:   zassert(!igmp->t_other_querier_timer);
 1133:   zassert(igmp->igmp_group_list);
 1134:   zassert(!listcount(igmp->igmp_group_list));
 1135: 
 1136:   list_free(igmp->igmp_group_list);
 1137: 
 1138:   XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
 1139: }
 1140: 
 1141: void igmp_sock_delete(struct igmp_sock *igmp)
 1142: {
 1143:   struct pim_interface *pim_ifp;
 1144:   struct listnode      *grp_node;
 1145:   struct listnode      *grp_nextnode;
 1146:   struct igmp_group    *grp;
 1147: 
 1148:   for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
 1149:     igmp_group_delete(grp);
 1150:   }
 1151: 
 1152:   sock_close(igmp);
 1153: 
 1154:   pim_ifp = igmp->interface->info;
 1155: 
 1156:   listnode_delete(pim_ifp->igmp_socket_list, igmp);
 1157: 
 1158:   igmp_sock_free(igmp);
 1159: }
 1160: 
 1161: static struct igmp_sock *igmp_sock_new(int fd,
 1162: 				       struct in_addr ifaddr,
 1163: 				       struct interface *ifp)
 1164: {
 1165:   struct pim_interface *pim_ifp;
 1166:   struct igmp_sock *igmp;
 1167: 
 1168:   pim_ifp = ifp->info;
 1169: 
 1170:   if (PIM_DEBUG_IGMP_TRACE) {
 1171:     zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
 1172: 	       fd, inet_ntoa(ifaddr), ifp->name);
 1173:   }
 1174: 
 1175:   igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
 1176:   if (!igmp) {
 1177:     zlog_warn("%s %s: XMALLOC() failure",
 1178:               __FILE__, __PRETTY_FUNCTION__);
 1179:     return 0;
 1180:   }
 1181: 
 1182:   igmp->igmp_group_list = list_new();
 1183:   if (!igmp->igmp_group_list) {
 1184:     zlog_err("%s %s: failure: igmp_group_list = list_new()",
 1185: 	     __FILE__, __PRETTY_FUNCTION__);
 1186:     return 0;
 1187:   }
 1188:   igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
 1189: 
 1190:   igmp->fd                          = fd;
 1191:   igmp->interface                   = ifp;
 1192:   igmp->ifaddr                      = ifaddr;
 1193:   igmp->t_igmp_read                 = 0;
 1194:   igmp->t_igmp_query_timer          = 0;
 1195:   igmp->t_other_querier_timer       = 0; /* no other querier present */
 1196:   igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
 1197:   igmp->sock_creation               = pim_time_monotonic_sec();
 1198: 
 1199:   /*
 1200:     igmp_startup_mode_on() will reset QQI:
 1201: 
 1202:     igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
 1203:   */
 1204:   igmp_startup_mode_on(igmp);
 1205: 
 1206:   igmp_read_on(igmp);
 1207:   pim_igmp_general_query_on(igmp);
 1208: 
 1209:   return igmp;
 1210: }
 1211: 
 1212: struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
 1213: 				    struct in_addr ifaddr,
 1214: 				    struct interface *ifp)
 1215: {
 1216:   struct pim_interface *pim_ifp;
 1217:   struct igmp_sock *igmp;
 1218:   int fd;
 1219: 
 1220:   pim_ifp = ifp->info;
 1221: 
 1222:   fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
 1223:   if (fd < 0) {
 1224:     zlog_warn("Could not open IGMP socket for %s on %s",
 1225: 	      inet_ntoa(ifaddr), ifp->name);
 1226:     return 0;
 1227:   }
 1228: 
 1229:   igmp = igmp_sock_new(fd, ifaddr, ifp);
 1230:   if (!igmp) {
 1231:     zlog_err("%s %s: igmp_sock_new() failure",
 1232: 	     __FILE__, __PRETTY_FUNCTION__);
 1233:     close(fd);
 1234:     return 0;
 1235:   }
 1236: 
 1237:   listnode_add(igmp_sock_list, igmp);
 1238: 
 1239: #ifdef IGMP_SOCK_DUMP
 1240:   igmp_sock_dump(igmp_sock_array);
 1241: #endif
 1242: 
 1243:   return igmp;
 1244: }
 1245: 
 1246: /*
 1247:   RFC 3376: 6.5. Switching Router Filter-Modes
 1248: 
 1249:   When a router's filter-mode for a group is EXCLUDE and the group
 1250:   timer expires, the router filter-mode for the group transitions to
 1251:   INCLUDE.
 1252: 
 1253:   A router uses source records with running source timers as its state
 1254:   for the switch to a filter-mode of INCLUDE.  If there are any source
 1255:   records with source timers greater than zero (i.e., requested to be
 1256:   forwarded), a router switches to filter-mode of INCLUDE using those
 1257:   source records.  Source records whose timers are zero (from the
 1258:   previous EXCLUDE mode) are deleted.
 1259:  */
 1260: static int igmp_group_timer(struct thread *t)
 1261: {
 1262:   struct igmp_group *group;
 1263: 
 1264:   zassert(t);
 1265:   group = THREAD_ARG(t);
 1266:   zassert(group);
 1267: 
 1268:   if (PIM_DEBUG_IGMP_TRACE) {
 1269:     char group_str[100];
 1270:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1271:     zlog_debug("%s: Timer for group %s on interface %s",
 1272: 	       __PRETTY_FUNCTION__,
 1273: 	       group_str, group->group_igmp_sock->interface->name);
 1274:   }
 1275: 
 1276:   zassert(group->group_filtermode_isexcl);
 1277: 
 1278:   group->t_group_timer = 0;
 1279:   group->group_filtermode_isexcl = 0;
 1280: 
 1281:   /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
 1282:   igmp_anysource_forward_stop(group);
 1283: 
 1284:   igmp_source_delete_expired(group->group_source_list);
 1285: 
 1286:   zassert(!group->t_group_timer);
 1287:   zassert(!group->group_filtermode_isexcl);
 1288: 
 1289:   /*
 1290:     RFC 3376: 6.2.2. Definition of Group Timers
 1291: 
 1292:     If there are no more source records for the group, delete group
 1293:     record.
 1294:   */
 1295:   if (listcount(group->group_source_list) < 1) {
 1296:     igmp_group_delete_empty_include(group);
 1297:   }
 1298: 
 1299:   return 0;
 1300: }
 1301: 
 1302: static void group_timer_off(struct igmp_group *group)
 1303: {
 1304:   if (!group->t_group_timer)
 1305:     return;
 1306: 
 1307:   if (PIM_DEBUG_IGMP_TRACE) {
 1308:     char group_str[100];
 1309:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1310:     zlog_debug("Cancelling TIMER event for group %s on %s",
 1311: 	       group_str, group->group_igmp_sock->interface->name);
 1312:   }
 1313:     
 1314:   THREAD_OFF(group->t_group_timer);
 1315:   zassert(!group->t_group_timer);
 1316: }
 1317: 
 1318: void igmp_group_timer_on(struct igmp_group *group,
 1319: 			 long interval_msec, const char *ifname)
 1320: {
 1321:   group_timer_off(group);
 1322: 
 1323:   if (PIM_DEBUG_IGMP_EVENTS) {
 1324:     char group_str[100];
 1325:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1326:     zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
 1327: 	       interval_msec / 1000,
 1328: 	       interval_msec % 1000,
 1329: 	       group_str, ifname);
 1330:   }
 1331: 
 1332:   /*
 1333:     RFC 3376: 6.2.2. Definition of Group Timers
 1334: 
 1335:     The group timer is only used when a group is in EXCLUDE mode and
 1336:     it represents the time for the *filter-mode* of the group to
 1337:     expire and switch to INCLUDE mode.
 1338:   */
 1339:   zassert(group->group_filtermode_isexcl);
 1340: 
 1341:   THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
 1342: 		       igmp_group_timer,
 1343: 		       group, interval_msec);
 1344: }
 1345: 
 1346: static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
 1347: 					     struct in_addr group_addr)
 1348: {
 1349:   struct igmp_group *group;
 1350:   struct listnode   *node;
 1351: 
 1352:   for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
 1353:     if (group_addr.s_addr == group->group_addr.s_addr)
 1354:       return group;
 1355: 
 1356:   return 0;
 1357: }
 1358: 
 1359: struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
 1360: 					  struct in_addr group_addr,
 1361: 					  const char *ifname)
 1362: {
 1363:   struct igmp_group *group;
 1364: 
 1365:   group = find_group_by_addr(igmp, group_addr);
 1366:   if (group) {
 1367:     return group;
 1368:   }
 1369: 
 1370:   /*
 1371:     Non-existant group is created as INCLUDE {empty}:
 1372: 
 1373:     RFC 3376 - 5.1. Action on Change of Interface State
 1374: 
 1375:     If no interface state existed for that multicast address before
 1376:     the change (i.e., the change consisted of creating a new
 1377:     per-interface record), or if no state exists after the change
 1378:     (i.e., the change consisted of deleting a per-interface record),
 1379:     then the "non-existent" state is considered to have a filter mode
 1380:     of INCLUDE and an empty source list.
 1381:   */
 1382: 
 1383:   group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
 1384:   if (!group) {
 1385:     zlog_warn("%s %s: XMALLOC() failure",
 1386: 	      __FILE__, __PRETTY_FUNCTION__);
 1387:     return 0; /* error, not found, could not create */
 1388:   }
 1389: 
 1390:   group->group_source_list = list_new();
 1391:   if (!group->group_source_list) {
 1392:     zlog_warn("%s %s: list_new() failure",
 1393: 	      __FILE__, __PRETTY_FUNCTION__);
 1394:     XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
 1395:     return 0; /* error, not found, could not initialize */
 1396:   }
 1397:   group->group_source_list->del = (void (*)(void *)) igmp_source_free;
 1398: 
 1399:   group->t_group_timer                         = 0;
 1400:   group->t_group_query_retransmit_timer        = 0;
 1401:   group->group_specific_query_retransmit_count = 0;
 1402:   group->group_addr                            = group_addr;
 1403:   group->group_igmp_sock                       = igmp;
 1404:   group->last_igmp_v1_report_dsec              = -1;
 1405:   group->last_igmp_v2_report_dsec              = -1;
 1406:   group->group_creation                        = pim_time_monotonic_sec();
 1407: 
 1408:   /* initialize new group as INCLUDE {empty} */
 1409:   group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
 1410: 
 1411:   listnode_add(igmp->igmp_group_list, group);
 1412: 
 1413:   if (PIM_DEBUG_IGMP_TRACE) {
 1414:     char group_str[100];
 1415:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1416:     zlog_debug("Creating new IGMP group %s on socket %d interface %s",
 1417: 	       group_str, group->group_igmp_sock->fd, ifname);
 1418:   }
 1419: 
 1420:   /*
 1421:     RFC 3376: 6.2.2. Definition of Group Timers
 1422: 
 1423:     The group timer is only used when a group is in EXCLUDE mode and
 1424:     it represents the time for the *filter-mode* of the group to
 1425:     expire and switch to INCLUDE mode.
 1426:   */
 1427:   zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
 1428:   zassert(!group->t_group_timer); /* group timer == 0 */
 1429: 
 1430:   /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
 1431:   igmp_anysource_forward_stop(group);
 1432: 
 1433:   return group;
 1434: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>