File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / quagga / pimd / pim_igmpv3.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: #include "log.h"
   25: #include "memory.h"
   26: 
   27: #include "pimd.h"
   28: #include "pim_iface.h"
   29: #include "pim_igmp.h"
   30: #include "pim_igmpv3.h"
   31: #include "pim_str.h"
   32: #include "pim_util.h"
   33: #include "pim_time.h"
   34: #include "pim_zebra.h"
   35: #include "pim_oil.h"
   36: 
   37: static void group_retransmit_timer_on(struct igmp_group *group);
   38: static long igmp_group_timer_remain_msec(struct igmp_group *group);
   39: static long igmp_source_timer_remain_msec(struct igmp_source *source);
   40: static void group_query_send(struct igmp_group *group);
   41: static void source_query_send_by_flag(struct igmp_group *group,
   42: 				      int num_sources_tosend);
   43: 
   44: static void on_trace(const char *label,
   45: 		     struct interface *ifp, struct in_addr from,
   46: 		     struct in_addr group_addr,
   47: 		     int num_sources, struct in_addr *sources)
   48: {
   49:   if (PIM_DEBUG_IGMP_TRACE) {
   50:     char from_str[100];
   51:     char group_str[100];
   52: 
   53:     pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
   54:     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
   55: 
   56:     zlog_debug("%s: from %s on %s: group=%s sources=%d",
   57: 	       label, from_str, ifp->name, group_str, num_sources);
   58:   }
   59: }
   60: 
   61: int igmp_group_compat_mode(const struct igmp_sock *igmp,
   62: 			   const struct igmp_group *group)
   63: {
   64:   struct pim_interface *pim_ifp;
   65:   int64_t               now_dsec;
   66:   long                  older_host_present_interval_dsec;
   67: 
   68:   zassert(igmp);
   69:   zassert(igmp->interface);
   70:   zassert(igmp->interface->info);
   71: 
   72:   pim_ifp = igmp->interface->info;
   73: 
   74:   /*
   75:     RFC 3376: 8.13. Older Host Present Interval
   76: 
   77:     This value MUST be ((the Robustness Variable) times (the Query
   78:     Interval)) plus (one Query Response Interval).
   79: 
   80:     older_host_present_interval_dsec = \
   81:       igmp->querier_robustness_variable * \
   82:       10 * igmp->querier_query_interval + \
   83:       pim_ifp->query_max_response_time_dsec;
   84:   */
   85:   older_host_present_interval_dsec =
   86:     PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
   87: 		       igmp->querier_query_interval,
   88: 		       pim_ifp->igmp_query_max_response_time_dsec);
   89: 
   90:   now_dsec = pim_time_monotonic_dsec();
   91:   if (now_dsec < 1) {
   92:     /* broken timer logged by pim_time_monotonic_dsec() */
   93:     return 3;
   94:   }
   95: 
   96:   if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
   97:     return 1; /* IGMPv1 */
   98: 
   99:   if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
  100:     return 2; /* IGMPv2 */
  101: 
  102:   return 3; /* IGMPv3 */
  103: }
  104: 
  105: void igmp_group_reset_gmi(struct igmp_group *group)
  106: {
  107:   long group_membership_interval_msec;
  108:   struct pim_interface *pim_ifp;
  109:   struct igmp_sock *igmp;
  110:   struct interface *ifp;
  111: 
  112:   igmp = group->group_igmp_sock;
  113:   ifp = igmp->interface;
  114:   pim_ifp = ifp->info;
  115: 
  116:   /*
  117:     RFC 3376: 8.4. Group Membership Interval
  118: 
  119:     The Group Membership Interval is the amount of time that must pass
  120:     before a multicast router decides there are no more members of a
  121:     group or a particular source on a network.
  122: 
  123:     This value MUST be ((the Robustness Variable) times (the Query
  124:     Interval)) plus (one Query Response Interval).
  125: 
  126:     group_membership_interval_msec = querier_robustness_variable *
  127:                                      (1000 * querier_query_interval) +
  128:                                      100 * query_response_interval_dsec;
  129:   */
  130:   group_membership_interval_msec =
  131:     PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
  132: 		      igmp->querier_query_interval,
  133: 		      pim_ifp->igmp_query_max_response_time_dsec);
  134: 
  135:   if (PIM_DEBUG_IGMP_TRACE) {
  136:     char group_str[100];
  137:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  138:     zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
  139: 	       group_str,
  140: 	       group_membership_interval_msec / 1000,
  141: 	       group_membership_interval_msec % 1000,
  142: 	       ifp->name);
  143:   }
  144: 
  145:   /*
  146:     RFC 3376: 6.2.2. Definition of Group Timers
  147: 
  148:     The group timer is only used when a group is in EXCLUDE mode and
  149:     it represents the time for the *filter-mode* of the group to
  150:     expire and switch to INCLUDE mode.
  151:   */
  152:   zassert(group->group_filtermode_isexcl);
  153: 
  154:   igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
  155: }
  156: 
  157: static int igmp_source_timer(struct thread *t)
  158: {
  159:   struct igmp_source *source;
  160:   struct igmp_group *group;
  161: 
  162:   zassert(t);
  163:   source = THREAD_ARG(t);
  164:   zassert(source);
  165: 
  166:   group = source->source_group;
  167: 
  168:   if (PIM_DEBUG_IGMP_TRACE) {
  169:     char group_str[100];
  170:     char source_str[100];
  171:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  172:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  173:     zlog_debug("%s: Source timer expired for group %s source %s on %s",
  174: 	       __PRETTY_FUNCTION__,
  175: 	       group_str, source_str,
  176: 	       group->group_igmp_sock->interface->name);
  177:   }
  178: 
  179:   zassert(source->t_source_timer);
  180:   source->t_source_timer = 0;
  181: 
  182:   /*
  183:     RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
  184: 
  185:     Group
  186:     Filter-Mode    Source Timer Value    Action
  187:     -----------    ------------------    ------
  188:     INCLUDE        TIMER == 0            Suggest to stop forwarding
  189:                                          traffic from source and
  190:                                          remove source record.  If
  191:                                          there are no more source
  192:                                          records for the group, delete
  193:                                          group record.
  194: 
  195:     EXCLUDE        TIMER == 0            Suggest to not forward
  196:                                          traffic from source
  197:                                          (DO NOT remove record)
  198: 
  199:     Source timer switched from (T > 0) to (T == 0): disable forwarding.
  200:    */
  201: 
  202:   zassert(!source->t_source_timer);
  203: 
  204:   if (group->group_filtermode_isexcl) {
  205:     /* EXCLUDE mode */
  206: 
  207:     igmp_source_forward_stop(source);
  208:   }
  209:   else {
  210:     /* INCLUDE mode */
  211: 
  212:     /* igmp_source_delete() will stop forwarding source */
  213:     igmp_source_delete(source);
  214: 
  215:     /*
  216:       If there are no more source records for the group, delete group
  217:       record.
  218:     */
  219:     if (!listcount(group->group_source_list)) {
  220:       igmp_group_delete_empty_include(group);
  221:     }
  222:   }
  223: 
  224:   return 0;
  225: }
  226: 
  227: static void source_timer_off(struct igmp_group *group,
  228: 			     struct igmp_source *source)
  229: {
  230:   if (!source->t_source_timer)
  231:     return;
  232:   
  233:   if (PIM_DEBUG_IGMP_TRACE) {
  234:     char group_str[100];
  235:     char source_str[100];
  236:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  237:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  238:     zlog_debug("Cancelling TIMER event for group %s source %s on %s",
  239: 	       group_str, source_str,
  240: 	       group->group_igmp_sock->interface->name);
  241:   }
  242: 
  243:   THREAD_OFF(source->t_source_timer);
  244:   zassert(!source->t_source_timer);
  245: }
  246: 
  247: static void igmp_source_timer_on(struct igmp_group *group,
  248: 				 struct igmp_source *source,
  249: 				 long interval_msec)
  250: {
  251:   source_timer_off(group, source);
  252: 
  253:   if (PIM_DEBUG_IGMP_EVENTS) {
  254:     char group_str[100];
  255:     char source_str[100];
  256:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  257:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  258:     zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
  259: 	       interval_msec / 1000,
  260: 	       interval_msec % 1000,
  261: 	       group_str, source_str,
  262: 	       group->group_igmp_sock->interface->name);
  263:   }
  264: 
  265:   THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
  266: 		       igmp_source_timer,
  267: 		       source, interval_msec);
  268:   zassert(source->t_source_timer);
  269: 
  270:   /*
  271:     RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
  272:     
  273:     Source timer switched from (T == 0) to (T > 0): enable forwarding.
  274:   */
  275:   igmp_source_forward_start(source);
  276: }
  277: 
  278: void igmp_source_reset_gmi(struct igmp_sock *igmp,
  279: 			   struct igmp_group *group,
  280: 			   struct igmp_source *source)
  281: {
  282:   long group_membership_interval_msec;
  283:   struct pim_interface *pim_ifp;
  284:   struct interface *ifp;
  285: 
  286:   ifp = igmp->interface;
  287:   pim_ifp = ifp->info;
  288: 
  289:   group_membership_interval_msec =
  290:     PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
  291: 		      igmp->querier_query_interval,
  292: 		      pim_ifp->igmp_query_max_response_time_dsec);
  293: 
  294:   if (PIM_DEBUG_IGMP_TRACE) {
  295:     char group_str[100];
  296:     char source_str[100];
  297: 
  298:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  299:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  300: 
  301:     zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
  302: 	       source_str,
  303: 	       group_membership_interval_msec / 1000,
  304: 	       group_membership_interval_msec % 1000,
  305: 	       group_str,
  306: 	       ifp->name);
  307:   }
  308: 
  309:   igmp_source_timer_on(group, source,
  310: 		       group_membership_interval_msec);
  311: }
  312: 
  313: static void source_mark_delete_flag(struct list *source_list)
  314: {
  315:   struct listnode    *src_node;
  316:   struct igmp_source *src;
  317: 
  318:   for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  319:     IGMP_SOURCE_DO_DELETE(src->source_flags);
  320:   }
  321: }
  322: 
  323: static void source_mark_send_flag(struct list *source_list)
  324: {
  325:   struct listnode    *src_node;
  326:   struct igmp_source *src;
  327: 
  328:   for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  329:     IGMP_SOURCE_DO_SEND(src->source_flags);
  330:   }
  331: }
  332: 
  333: static int source_mark_send_flag_by_timer(struct list *source_list)
  334: {
  335:   struct listnode    *src_node;
  336:   struct igmp_source *src;
  337:   int                 num_marked_sources = 0;
  338: 
  339:   for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  340:     /* Is source timer running? */
  341:     if (src->t_source_timer) {
  342:       IGMP_SOURCE_DO_SEND(src->source_flags);
  343:       ++num_marked_sources;
  344:     }
  345:     else {
  346:       IGMP_SOURCE_DONT_SEND(src->source_flags);
  347:     }
  348:   }
  349: 
  350:   return num_marked_sources;
  351: }
  352: 
  353: static void source_clear_send_flag(struct list *source_list)
  354: {
  355:   struct listnode    *src_node;
  356:   struct igmp_source *src;
  357: 
  358:   for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
  359:     IGMP_SOURCE_DONT_SEND(src->source_flags);
  360:   }
  361: }
  362: 
  363: /*
  364:   Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
  365: */
  366: static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
  367: {
  368:   zassert(group->group_filtermode_isexcl);
  369: 
  370:   if (listcount(group->group_source_list) < 1) {
  371:     igmp_anysource_forward_start(group);
  372:   }
  373: }
  374: 
  375: void igmp_source_free(struct igmp_source *source)
  376: {
  377:   /* make sure there is no source timer running */
  378:   zassert(!source->t_source_timer);
  379: 
  380:   XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
  381: }
  382: 
  383: static void source_channel_oil_detach(struct igmp_source *source)
  384: {
  385:   if (source->source_channel_oil) {
  386:     pim_channel_oil_del(source->source_channel_oil);
  387:     source->source_channel_oil = 0;
  388:   }
  389: }
  390: 
  391: /*
  392:   igmp_source_delete:       stop fowarding, and delete the source
  393:   igmp_source_forward_stop: stop fowarding, but keep the source
  394: */
  395: void igmp_source_delete(struct igmp_source *source)
  396: {
  397:   struct igmp_group *group;
  398: 
  399:   group = source->source_group;
  400: 
  401:   if (PIM_DEBUG_IGMP_TRACE) {
  402:     char group_str[100];
  403:     char source_str[100];
  404:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  405:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  406:     zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
  407: 	       source_str, group_str,
  408: 	       group->group_igmp_sock->fd,
  409: 	       group->group_igmp_sock->interface->name);
  410:   }
  411: 
  412:   source_timer_off(group, source);
  413:   igmp_source_forward_stop(source);
  414: 
  415:   /* sanity check that forwarding has been disabled */
  416:   if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
  417:     char group_str[100];
  418:     char source_str[100];
  419:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  420:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
  421:     zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
  422: 	      __PRETTY_FUNCTION__,
  423: 	      source_str, group_str,
  424: 	      group->group_igmp_sock->fd,
  425: 	      group->group_igmp_sock->interface->name);
  426:     /* warning only */
  427:   }
  428: 
  429:   source_channel_oil_detach(source);
  430: 
  431:   /*
  432:     notice that listnode_delete() can't be moved
  433:     into igmp_source_free() because the later is
  434:     called by list_delete_all_node()
  435:   */
  436:   listnode_delete(group->group_source_list, source);
  437: 
  438:   igmp_source_free(source);
  439: 
  440:   if (group->group_filtermode_isexcl) {
  441:     group_exclude_fwd_anysrc_ifempty(group);
  442:   }
  443: }
  444: 
  445: static void source_delete_by_flag(struct list *source_list)
  446: {
  447:   struct listnode    *src_node;
  448:   struct listnode    *src_nextnode;
  449:   struct igmp_source *src;
  450:   
  451:   for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
  452:     if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
  453:       igmp_source_delete(src);
  454: }
  455: 
  456: void igmp_source_delete_expired(struct list *source_list)
  457: {
  458:   struct listnode    *src_node;
  459:   struct listnode    *src_nextnode;
  460:   struct igmp_source *src;
  461:   
  462:   for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
  463:     if (!src->t_source_timer)
  464:       igmp_source_delete(src);
  465: }
  466: 
  467: struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
  468: 					     struct in_addr src_addr)
  469: {
  470:   struct listnode    *src_node;
  471:   struct igmp_source *src;
  472: 
  473:   for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
  474:     if (src_addr.s_addr == src->source_addr.s_addr)
  475:       return src;
  476: 
  477:   return 0;
  478: }
  479: 
  480: static struct igmp_source *source_new(struct igmp_group *group,
  481: 				      struct in_addr src_addr,
  482: 				      const char *ifname)
  483: {
  484:   struct igmp_source *src;
  485: 
  486:   if (PIM_DEBUG_IGMP_TRACE) {
  487:     char group_str[100];
  488:     char source_str[100];
  489:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
  490:     pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
  491:     zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
  492: 	       source_str, group_str,
  493: 	       group->group_igmp_sock->fd,
  494: 	       ifname);
  495:   }
  496: 
  497:   src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
  498:   if (!src) {
  499:     zlog_warn("%s %s: XMALLOC() failure",
  500: 	      __FILE__, __PRETTY_FUNCTION__);
  501:     return 0; /* error, not found, could not create */
  502:   }
  503:   
  504:   src->t_source_timer                = 0;
  505:   src->source_group                  = group; /* back pointer */
  506:   src->source_addr                   = src_addr;
  507:   src->source_creation               = pim_time_monotonic_sec();
  508:   src->source_flags                  = 0;
  509:   src->source_query_retransmit_count = 0;
  510:   src->source_channel_oil            = 0;
  511: 
  512:   listnode_add(group->group_source_list, src);
  513: 
  514:   zassert(!src->t_source_timer); /* source timer == 0 */
  515: 
  516:   /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
  517:   igmp_anysource_forward_stop(group);
  518: 
  519:   return src;
  520: }
  521: 
  522: static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
  523: 					      struct igmp_group *group,
  524: 					      struct in_addr src_addr,
  525: 					      const char *ifname)
  526: {
  527:   struct igmp_source *src;
  528: 
  529:   src = igmp_find_source_by_addr(group, src_addr);
  530:   if (src) {
  531:     return src;
  532:   }
  533: 
  534:   src = source_new(group, src_addr, ifname);
  535:   if (!src) {
  536:     return 0;
  537:   }
  538: 
  539:   return src;
  540: }
  541: 
  542: static void allow(struct igmp_sock *igmp, struct in_addr from,
  543: 		  struct in_addr group_addr,
  544: 		  int num_sources, struct in_addr *sources)
  545: {
  546:   struct interface *ifp = igmp->interface;
  547:   struct igmp_group *group;
  548:   int    i;
  549: 
  550:   /* non-existant group is created as INCLUDE {empty} */
  551:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
  552:   if (!group) {
  553:     return;
  554:   }
  555: 
  556:   /* scan received sources */
  557:   for (i = 0; i < num_sources; ++i) {
  558:     struct igmp_source *source;
  559:     struct in_addr     *src_addr;
  560: 
  561:     src_addr = sources + i;
  562: 
  563:     source = add_source_by_addr(igmp, group, *src_addr,	ifp->name);
  564:     if (!source) {
  565:       continue;
  566:     }
  567: 
  568:     /*
  569:       RFC 3376: 6.4.1. Reception of Current-State Records
  570: 
  571:       When receiving IS_IN reports for groups in EXCLUDE mode is
  572:       sources should be moved from set with (timers = 0) to set with
  573:       (timers > 0).
  574: 
  575:       igmp_source_reset_gmi() below, resetting the source timers to
  576:       GMI, accomplishes this.
  577:     */
  578:     igmp_source_reset_gmi(igmp, group, source);
  579: 
  580:   } /* scan received sources */
  581: }
  582: 
  583: void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
  584: 			struct in_addr group_addr,
  585: 			int num_sources, struct in_addr *sources)
  586: {
  587:   on_trace(__PRETTY_FUNCTION__,
  588: 	   igmp->interface, from, group_addr, num_sources, sources);
  589: 
  590:   allow(igmp, from, group_addr, num_sources, sources);
  591: }
  592: 
  593: static void isex_excl(struct igmp_group *group,
  594: 		      int num_sources, struct in_addr *sources)
  595: {
  596:   int     i;
  597: 
  598:   /* EXCLUDE mode */
  599:   zassert(group->group_filtermode_isexcl);
  600:   
  601:   /* E.1: set deletion flag for known sources (X,Y) */
  602:   source_mark_delete_flag(group->group_source_list);
  603: 
  604:   /* scan received sources (A) */
  605:   for (i = 0; i < num_sources; ++i) {
  606:     struct igmp_source *source;
  607:     struct in_addr     *src_addr;
  608: 
  609:     src_addr = sources + i;
  610: 
  611:     /* E.2: lookup reported source from (A) in (X,Y) */
  612:     source = igmp_find_source_by_addr(group, *src_addr);
  613:     if (source) {
  614:       /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
  615:       IGMP_SOURCE_DONT_DELETE(source->source_flags);
  616:     }
  617:     else {
  618:       /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
  619:       source = source_new(group, *src_addr,
  620: 			  group->group_igmp_sock->interface->name);
  621:       if (!source) {
  622: 	/* ugh, internal malloc failure, skip source */
  623: 	continue;
  624:       }
  625:       zassert(!source->t_source_timer); /* timer == 0 */
  626:       igmp_source_reset_gmi(group->group_igmp_sock, group, source);
  627:       zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
  628:     }
  629: 
  630:   } /* scan received sources */
  631: 
  632:   /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
  633:   source_delete_by_flag(group->group_source_list);
  634: }
  635: 
  636: static void isex_incl(struct igmp_group *group,
  637: 		      int num_sources, struct in_addr *sources)
  638: {
  639:   int i;
  640: 
  641:   /* INCLUDE mode */
  642:   zassert(!group->group_filtermode_isexcl);
  643:   
  644:   /* I.1: set deletion flag for known sources (A) */
  645:   source_mark_delete_flag(group->group_source_list);
  646: 
  647:   /* scan received sources (B) */
  648:   for (i = 0; i < num_sources; ++i) {
  649:     struct igmp_source *source;
  650:     struct in_addr     *src_addr;
  651: 
  652:     src_addr = sources + i;
  653: 
  654:     /* I.2: lookup reported source (B) */
  655:     source = igmp_find_source_by_addr(group, *src_addr);
  656:     if (source) {
  657:       /* I.3: if found, clear deletion flag (A*B) */
  658:       IGMP_SOURCE_DONT_DELETE(source->source_flags);
  659:     }
  660:     else {
  661:       /* I.4: if not found, create source with timer=0 (B-A) */
  662:       source = source_new(group, *src_addr,
  663: 			  group->group_igmp_sock->interface->name);
  664:       if (!source) {
  665: 	/* ugh, internal malloc failure, skip source */
  666: 	continue;
  667:       }
  668:       zassert(!source->t_source_timer); /* (B-A) timer=0 */
  669:     }
  670: 
  671:   } /* scan received sources */
  672: 
  673:   /* I.5: delete all sources marked with deletion flag (A-B) */
  674:   source_delete_by_flag(group->group_source_list);
  675: 
  676:   group->group_filtermode_isexcl = 1; /* boolean=true */
  677: 
  678:   zassert(group->group_filtermode_isexcl);
  679: 
  680:   group_exclude_fwd_anysrc_ifempty(group);
  681: }
  682: 
  683: void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
  684: 			struct in_addr group_addr,
  685: 			int num_sources, struct in_addr *sources)
  686: {
  687:   struct interface *ifp = igmp->interface;
  688:   struct igmp_group *group;
  689: 
  690:   on_trace(__PRETTY_FUNCTION__,
  691: 	   ifp, from, group_addr, num_sources, sources);
  692: 
  693:   /* non-existant group is created as INCLUDE {empty} */
  694:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
  695:   if (!group) {
  696:     return;
  697:   }
  698: 
  699:   if (group->group_filtermode_isexcl) {
  700:     /* EXCLUDE mode */
  701:     isex_excl(group, num_sources, sources);
  702:   }
  703:   else {
  704:     /* INCLUDE mode */
  705:     isex_incl(group, num_sources, sources);
  706:     zassert(group->group_filtermode_isexcl);
  707:   }
  708: 
  709:   zassert(group->group_filtermode_isexcl);
  710: 
  711:   igmp_group_reset_gmi(group);
  712: }
  713: 
  714: static void toin_incl(struct igmp_group *group,
  715: 		      int num_sources, struct in_addr *sources)
  716: {
  717:   struct igmp_sock *igmp = group->group_igmp_sock;
  718:   int num_sources_tosend = listcount(group->group_source_list);
  719:   int i;
  720: 
  721:   /* Set SEND flag for all known sources (A) */
  722:   source_mark_send_flag(group->group_source_list);
  723: 
  724:   /* Scan received sources (B) */
  725:   for (i = 0; i < num_sources; ++i) {
  726:     struct igmp_source *source;
  727:     struct in_addr     *src_addr;
  728: 
  729:     src_addr = sources + i;
  730: 
  731:     /* Lookup reported source (B) */
  732:     source = igmp_find_source_by_addr(group, *src_addr);
  733:     if (source) {
  734:       /* If found, clear SEND flag (A*B) */
  735:       IGMP_SOURCE_DONT_SEND(source->source_flags);
  736:       --num_sources_tosend;
  737:     }
  738:     else {
  739:       /* If not found, create new source */
  740:       source = source_new(group, *src_addr,
  741: 			  group->group_igmp_sock->interface->name);
  742:       if (!source) {
  743: 	/* ugh, internal malloc failure, skip source */
  744: 	continue;
  745:       }
  746:     }
  747: 
  748:     /* (B)=GMI */
  749:     igmp_source_reset_gmi(igmp, group, source);
  750:   }
  751: 
  752:   /* Send sources marked with SEND flag: Q(G,A-B) */
  753:   if (num_sources_tosend > 0) {
  754:     source_query_send_by_flag(group, num_sources_tosend);
  755:   }
  756: }
  757: 
  758: static void toin_excl(struct igmp_group *group,
  759: 		      int num_sources, struct in_addr *sources)
  760: {
  761:   struct igmp_sock *igmp = group->group_igmp_sock;
  762:   int num_sources_tosend;
  763:   int i;
  764: 
  765:   /* Set SEND flag for X (sources with timer > 0) */
  766:   num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
  767: 
  768:   /* Scan received sources (A) */
  769:   for (i = 0; i < num_sources; ++i) {
  770:     struct igmp_source *source;
  771:     struct in_addr     *src_addr;
  772: 
  773:     src_addr = sources + i;
  774: 
  775:     /* Lookup reported source (A) */
  776:     source = igmp_find_source_by_addr(group, *src_addr);
  777:     if (source) {
  778:       if (source->t_source_timer) {
  779: 	/* If found and timer running, clear SEND flag (X*A) */
  780: 	IGMP_SOURCE_DONT_SEND(source->source_flags);
  781: 	--num_sources_tosend;
  782:       }
  783:     }
  784:     else {
  785:       /* If not found, create new source */
  786:       source = source_new(group, *src_addr,
  787: 			  group->group_igmp_sock->interface->name);
  788:       if (!source) {
  789: 	/* ugh, internal malloc failure, skip source */
  790: 	continue;
  791:       }
  792:     }
  793: 
  794:     /* (A)=GMI */
  795:     igmp_source_reset_gmi(igmp, group, source);
  796:   }
  797: 
  798:   /* Send sources marked with SEND flag: Q(G,X-A) */
  799:   if (num_sources_tosend > 0) {
  800:     source_query_send_by_flag(group, num_sources_tosend);
  801:   }
  802: 
  803:   /* Send Q(G) */
  804:   group_query_send(group);
  805: }
  806: 
  807: void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
  808: 			struct in_addr group_addr,
  809: 			int num_sources, struct in_addr *sources)
  810: {
  811:   struct interface *ifp = igmp->interface;
  812:   struct igmp_group *group;
  813: 
  814:   on_trace(__PRETTY_FUNCTION__,
  815: 	   ifp, from, group_addr, num_sources, sources);
  816: 
  817:   /* non-existant group is created as INCLUDE {empty} */
  818:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
  819:   if (!group) {
  820:     return;
  821:   }
  822: 
  823:   if (group->group_filtermode_isexcl) {
  824:     /* EXCLUDE mode */
  825:     toin_excl(group, num_sources, sources);
  826:   }
  827:   else {
  828:     /* INCLUDE mode */
  829:     toin_incl(group, num_sources, sources);
  830:   }
  831: }
  832: 
  833: static void toex_incl(struct igmp_group *group,
  834: 		      int num_sources, struct in_addr *sources)
  835: {
  836:   int num_sources_tosend = 0;
  837:   int i;
  838: 
  839:   zassert(!group->group_filtermode_isexcl);
  840: 
  841:   /* Set DELETE flag for all known sources (A) */
  842:   source_mark_delete_flag(group->group_source_list);
  843: 
  844:   /* Clear off SEND flag from all known sources (A) */
  845:   source_clear_send_flag(group->group_source_list);
  846: 
  847:   /* Scan received sources (B) */
  848:   for (i = 0; i < num_sources; ++i) {
  849:     struct igmp_source *source;
  850:     struct in_addr     *src_addr;
  851: 
  852:     src_addr = sources + i;
  853: 
  854:     /* Lookup reported source (B) */
  855:     source = igmp_find_source_by_addr(group, *src_addr);
  856:     if (source) {
  857:       /* If found, clear deletion flag: (A*B) */
  858:       IGMP_SOURCE_DONT_DELETE(source->source_flags);
  859:       /* and set SEND flag (A*B) */
  860:       IGMP_SOURCE_DO_SEND(source->source_flags);
  861:       ++num_sources_tosend;
  862:     }
  863:     else {
  864:       /* If source not found, create source with timer=0: (B-A)=0 */
  865:       source = source_new(group, *src_addr,
  866: 			  group->group_igmp_sock->interface->name);
  867:       if (!source) {
  868: 	/* ugh, internal malloc failure, skip source */
  869: 	continue;
  870:       }
  871:       zassert(!source->t_source_timer); /* (B-A) timer=0 */
  872:     }
  873: 
  874:   } /* Scan received sources (B) */
  875: 
  876:   group->group_filtermode_isexcl = 1; /* boolean=true */
  877: 
  878:   /* Delete all sources marked with DELETE flag (A-B) */
  879:   source_delete_by_flag(group->group_source_list);
  880: 
  881:   /* Send sources marked with SEND flag: Q(G,A*B) */
  882:   if (num_sources_tosend > 0) {
  883:     source_query_send_by_flag(group, num_sources_tosend);
  884:   }
  885: 
  886:   zassert(group->group_filtermode_isexcl);
  887: 
  888:   group_exclude_fwd_anysrc_ifempty(group);
  889: }
  890: 
  891: static void toex_excl(struct igmp_group *group,
  892: 		      int num_sources, struct in_addr *sources)
  893: {
  894:   int num_sources_tosend = 0;
  895:   int i;
  896: 
  897:   /* set DELETE flag for all known sources (X,Y) */
  898:   source_mark_delete_flag(group->group_source_list);
  899: 
  900:   /* clear off SEND flag from all known sources (X,Y) */
  901:   source_clear_send_flag(group->group_source_list);
  902: 
  903:   /* scan received sources (A) */
  904:   for (i = 0; i < num_sources; ++i) {
  905:     struct igmp_source *source;
  906:     struct in_addr     *src_addr;
  907:     
  908:     src_addr = sources + i;
  909:     
  910:     /* lookup reported source (A) in known sources (X,Y) */
  911:     source = igmp_find_source_by_addr(group, *src_addr);
  912:     if (source) {
  913:       /* if found, clear off DELETE flag from reported source (A) */
  914:       IGMP_SOURCE_DONT_DELETE(source->source_flags);
  915:     }
  916:     else {
  917:       /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
  918:       long group_timer_msec;
  919:       source = source_new(group, *src_addr,
  920: 			  group->group_igmp_sock->interface->name);
  921:       if (!source) {
  922: 	/* ugh, internal malloc failure, skip source */
  923: 	continue;
  924:       }
  925: 
  926:       zassert(!source->t_source_timer); /* timer == 0 */
  927:       group_timer_msec = igmp_group_timer_remain_msec(group);
  928:       igmp_source_timer_on(group, source, group_timer_msec);
  929:       zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
  930: 
  931:       /* make sure source is created with DELETE flag unset */
  932:       zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
  933:     }
  934: 
  935:     /* make sure reported source has DELETE flag unset */
  936:     zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
  937: 
  938:     if (source->t_source_timer) {
  939:       /* if source timer>0 mark SEND flag: Q(G,A-Y) */
  940:       IGMP_SOURCE_DO_SEND(source->source_flags);
  941:       ++num_sources_tosend;
  942:     }
  943: 
  944:   } /* scan received sources (A) */
  945: 
  946:   /*
  947:     delete all sources marked with DELETE flag:
  948:     Delete (X-A)
  949:     Delete (Y-A)
  950:   */
  951:   source_delete_by_flag(group->group_source_list);
  952:  
  953:   /* send sources marked with SEND flag: Q(G,A-Y) */
  954:   if (num_sources_tosend > 0) {
  955:     source_query_send_by_flag(group, num_sources_tosend);
  956:   }
  957: }
  958: 
  959: void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
  960: 			struct in_addr group_addr,
  961: 			int num_sources, struct in_addr *sources)
  962: {
  963:   struct interface *ifp = igmp->interface;
  964:   struct igmp_group *group;
  965: 
  966:   on_trace(__PRETTY_FUNCTION__,
  967: 	   ifp, from, group_addr, num_sources, sources);
  968: 
  969:   /* non-existant group is created as INCLUDE {empty} */
  970:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
  971:   if (!group) {
  972:     return;
  973:   }
  974: 
  975:   if (group->group_filtermode_isexcl) {
  976:     /* EXCLUDE mode */
  977:     toex_excl(group, num_sources, sources);
  978:   }
  979:   else {
  980:     /* INCLUDE mode */
  981:     toex_incl(group, num_sources, sources);
  982:     zassert(group->group_filtermode_isexcl);
  983:   }
  984:   zassert(group->group_filtermode_isexcl);
  985: 
  986:   /* Group Timer=GMI */
  987:   igmp_group_reset_gmi(group);
  988: }
  989: 
  990: void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
  991: 			 struct in_addr group_addr,
  992: 			 int num_sources, struct in_addr *sources)
  993: {
  994:   on_trace(__PRETTY_FUNCTION__,
  995: 	   igmp->interface, from, group_addr, num_sources, sources);
  996: 
  997:   allow(igmp, from, group_addr, num_sources, sources);
  998: }
  999: 
 1000: /*
 1001:   RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
 1002: 
 1003:   When transmitting a group specific query, if the group timer is
 1004:   larger than LMQT, the "Suppress Router-Side Processing" bit is set
 1005:   in the query message.
 1006: */
 1007: static void group_retransmit_group(struct igmp_group *group)
 1008: {
 1009:   char                  query_buf[PIM_IGMP_BUFSIZE_WRITE];
 1010:   struct igmp_sock     *igmp;
 1011:   struct pim_interface *pim_ifp;
 1012:   long                  lmqc;      /* Last Member Query Count */
 1013:   long                  lmqi_msec; /* Last Member Query Interval */
 1014:   long                  lmqt_msec; /* Last Member Query Time */
 1015:   int                   s_flag;
 1016: 
 1017:   igmp = group->group_igmp_sock;
 1018:   pim_ifp = igmp->interface->info;
 1019: 
 1020:   lmqc      = igmp->querier_robustness_variable;
 1021:   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
 1022:   lmqt_msec = lmqc * lmqi_msec;
 1023: 
 1024:   /*
 1025:     RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
 1026:     
 1027:     When transmitting a group specific query, if the group timer is
 1028:     larger than LMQT, the "Suppress Router-Side Processing" bit is set
 1029:     in the query message.
 1030:   */
 1031:   s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
 1032: 
 1033:   if (PIM_DEBUG_IGMP_TRACE) {
 1034:     char group_str[100];
 1035:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1036:     zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
 1037: 	       group_str, igmp->interface->name, s_flag,
 1038: 	       group->group_specific_query_retransmit_count);
 1039:   }
 1040: 
 1041:   /*
 1042:     RFC3376: 4.1.12. IP Destination Addresses for Queries
 1043: 
 1044:     Group-Specific and Group-and-Source-Specific Queries are sent with
 1045:     an IP destination address equal to the multicast address of
 1046:     interest.
 1047:   */
 1048: 
 1049:   pim_igmp_send_membership_query(group,
 1050: 				 igmp->fd,
 1051: 				 igmp->interface->name,
 1052: 				 query_buf,
 1053: 				 sizeof(query_buf),
 1054: 				 0 /* num_sources_tosend */,
 1055: 				 group->group_addr /* dst_addr */,
 1056: 				 group->group_addr /* group_addr */,
 1057: 				 pim_ifp->igmp_specific_query_max_response_time_dsec,
 1058: 				 s_flag,
 1059: 				 igmp->querier_robustness_variable,
 1060: 				 igmp->querier_query_interval);
 1061: }
 1062: 
 1063: /*
 1064:   RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
 1065: 
 1066:   When building a group and source specific query for a group G, two
 1067:   separate query messages are sent for the group.  The first one has
 1068:   the "Suppress Router-Side Processing" bit set and contains all the
 1069:   sources with retransmission state and timers greater than LMQT.  The
 1070:   second has the "Suppress Router-Side Processing" bit clear and
 1071:   contains all the sources with retransmission state and timers lower
 1072:   or equal to LMQT.  If either of the two calculated messages does not
 1073:   contain any sources, then its transmission is suppressed.
 1074:  */
 1075: static int group_retransmit_sources(struct igmp_group *group,
 1076: 				    int send_with_sflag_set)
 1077: {
 1078:   struct igmp_sock     *igmp;
 1079:   struct pim_interface *pim_ifp;
 1080:   long                  lmqc;      /* Last Member Query Count */
 1081:   long                  lmqi_msec; /* Last Member Query Interval */
 1082:   long                  lmqt_msec; /* Last Member Query Time */
 1083:   char                  query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
 1084:   char                  query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
 1085:   int                   query_buf1_max_sources;
 1086:   int                   query_buf2_max_sources;
 1087:   struct in_addr       *source_addr1;
 1088:   struct in_addr       *source_addr2;
 1089:   int                   num_sources_tosend1;
 1090:   int                   num_sources_tosend2;
 1091:   struct listnode      *src_node;
 1092:   struct igmp_source   *src;
 1093:   int                   num_retransmit_sources_left = 0;
 1094:   
 1095:   query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
 1096:   query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
 1097:   
 1098:   source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
 1099:   source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
 1100: 
 1101:   igmp = group->group_igmp_sock;
 1102:   pim_ifp = igmp->interface->info;
 1103: 
 1104:   lmqc      = igmp->querier_robustness_variable;
 1105:   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
 1106:   lmqt_msec = lmqc * lmqi_msec;
 1107: 
 1108:   /* Scan all group sources */
 1109:   for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
 1110: 
 1111:     /* Source has retransmission state? */
 1112:     if (src->source_query_retransmit_count < 1)
 1113:       continue;
 1114: 
 1115:     if (--src->source_query_retransmit_count > 0) {
 1116:       ++num_retransmit_sources_left;
 1117:     }
 1118: 
 1119:     /* Copy source address into appropriate query buffer */
 1120:     if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
 1121:       *source_addr1 = src->source_addr;
 1122:       ++source_addr1;
 1123:     }
 1124:     else {
 1125:       *source_addr2 = src->source_addr;
 1126:       ++source_addr2;
 1127:     }
 1128: 
 1129:   }
 1130:   
 1131:   num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
 1132:   num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
 1133: 
 1134:   if (PIM_DEBUG_IGMP_TRACE) {
 1135:     char group_str[100];
 1136:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1137:     zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
 1138: 	       group_str, igmp->interface->name,
 1139: 	       num_sources_tosend1,
 1140: 	       num_sources_tosend2,
 1141: 	       send_with_sflag_set,
 1142: 	       num_retransmit_sources_left);
 1143:   }
 1144: 
 1145:   if (num_sources_tosend1 > 0) {
 1146:     /*
 1147:       Send group-and-source-specific query with s_flag set and all
 1148:       sources with timers greater than LMQT.
 1149:     */
 1150: 
 1151:     if (send_with_sflag_set) {
 1152: 
 1153:       query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
 1154:       if (num_sources_tosend1 > query_buf1_max_sources) {
 1155: 	char group_str[100];
 1156: 	pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1157: 	zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
 1158: 		  __PRETTY_FUNCTION__, group_str, igmp->interface->name,
 1159: 		  num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
 1160:       }
 1161:       else {
 1162: 	/*
 1163: 	  RFC3376: 4.1.12. IP Destination Addresses for Queries
 1164:       
 1165: 	  Group-Specific and Group-and-Source-Specific Queries are sent with
 1166: 	  an IP destination address equal to the multicast address of
 1167: 	  interest.
 1168: 	*/
 1169:     
 1170: 	pim_igmp_send_membership_query(group,
 1171: 				       igmp->fd,
 1172: 				       igmp->interface->name,
 1173: 				       query_buf1,
 1174: 				       sizeof(query_buf1),
 1175: 				       num_sources_tosend1,
 1176: 				       group->group_addr,
 1177: 				       group->group_addr,
 1178: 				       pim_ifp->igmp_specific_query_max_response_time_dsec,
 1179: 				       1 /* s_flag */,
 1180: 				       igmp->querier_robustness_variable,
 1181: 				       igmp->querier_query_interval);
 1182:     
 1183:       }
 1184: 
 1185:     } /* send_with_sflag_set */
 1186: 
 1187:   }
 1188: 
 1189:   if (num_sources_tosend2 > 0) {
 1190:     /*
 1191:       Send group-and-source-specific query with s_flag clear and all
 1192:       sources with timers lower or equal to LMQT.
 1193:     */
 1194:   
 1195:     query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
 1196:     if (num_sources_tosend2 > query_buf2_max_sources) {
 1197:       char group_str[100];
 1198:       pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1199:       zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
 1200: 		__PRETTY_FUNCTION__, group_str, igmp->interface->name,
 1201: 		num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
 1202:     }
 1203:     else {
 1204:       /*
 1205: 	RFC3376: 4.1.12. IP Destination Addresses for Queries
 1206: 
 1207: 	Group-Specific and Group-and-Source-Specific Queries are sent with
 1208: 	an IP destination address equal to the multicast address of
 1209: 	interest.
 1210:       */
 1211: 
 1212:       pim_igmp_send_membership_query(group,
 1213: 				     igmp->fd,
 1214: 				     igmp->interface->name,
 1215: 				     query_buf2,
 1216: 				     sizeof(query_buf2),
 1217: 				     num_sources_tosend2,
 1218: 				     group->group_addr,
 1219: 				     group->group_addr,
 1220: 				     pim_ifp->igmp_specific_query_max_response_time_dsec,
 1221: 				     0 /* s_flag */,
 1222: 				     igmp->querier_robustness_variable,
 1223: 				     igmp->querier_query_interval);
 1224: 
 1225:     }
 1226:   }
 1227: 
 1228:   return num_retransmit_sources_left;
 1229: }
 1230: 
 1231: static int igmp_group_retransmit(struct thread *t)
 1232: {
 1233:   struct igmp_group *group;
 1234:   int num_retransmit_sources_left;
 1235:   int send_with_sflag_set; /* boolean */
 1236: 
 1237:   zassert(t);
 1238:   group = THREAD_ARG(t);
 1239:   zassert(group);
 1240: 
 1241:   if (PIM_DEBUG_IGMP_TRACE) {
 1242:     char group_str[100];
 1243:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1244:     zlog_debug("group_retransmit_timer: group %s on %s",
 1245: 	       group_str, group->group_igmp_sock->interface->name);
 1246:   }
 1247: 
 1248:   /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
 1249:   if (group->group_specific_query_retransmit_count > 0) {
 1250: 
 1251:     /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
 1252:     group_retransmit_group(group);
 1253:     --group->group_specific_query_retransmit_count;
 1254: 
 1255:     /*
 1256:       RFC3376: 6.6.3.2
 1257:       If a group specific query is scheduled to be transmitted at the
 1258:       same time as a group and source specific query for the same group,
 1259:       then transmission of the group and source specific message with the
 1260:       "Suppress Router-Side Processing" bit set may be suppressed.
 1261:     */
 1262:     send_with_sflag_set = 0; /* boolean=false */
 1263:   }
 1264:   else {
 1265:     send_with_sflag_set = 1; /* boolean=true */
 1266:   }
 1267: 
 1268:   /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
 1269:   num_retransmit_sources_left = group_retransmit_sources(group,
 1270: 							 send_with_sflag_set);
 1271: 
 1272:   group->t_group_query_retransmit_timer = 0;
 1273: 
 1274:   /*
 1275:     Keep group retransmit timer running if there is any retransmit
 1276:     counter pending
 1277:   */
 1278:   if ((num_retransmit_sources_left > 0) ||
 1279:       (group->group_specific_query_retransmit_count > 0)) {
 1280:     group_retransmit_timer_on(group);
 1281:   }
 1282: 
 1283:   return 0;
 1284: }
 1285: 
 1286: /*
 1287:   group_retransmit_timer_on:
 1288:   if group retransmit timer isn't running, starts it;
 1289:   otherwise, do nothing
 1290: */
 1291: static void group_retransmit_timer_on(struct igmp_group *group)
 1292: {
 1293:   struct igmp_sock     *igmp;
 1294:   struct pim_interface *pim_ifp;
 1295:   long                  lmqi_msec; /* Last Member Query Interval */
 1296: 
 1297:   /* if group retransmit timer is running, do nothing */
 1298:   if (group->t_group_query_retransmit_timer) {
 1299:     return;
 1300:   }
 1301: 
 1302:   igmp = group->group_igmp_sock;
 1303:   pim_ifp = igmp->interface->info;
 1304: 
 1305:   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
 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("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
 1311: 	       lmqi_msec / 1000,
 1312: 	       lmqi_msec % 1000,
 1313: 	       group_str,
 1314: 	       igmp->interface->name);
 1315:   }
 1316: 
 1317:   THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
 1318: 		       igmp_group_retransmit,
 1319: 		       group, lmqi_msec);
 1320: }
 1321: 
 1322: static long igmp_group_timer_remain_msec(struct igmp_group *group)
 1323: {
 1324:   return pim_time_timer_remain_msec(group->t_group_timer);
 1325: }
 1326: 
 1327: static long igmp_source_timer_remain_msec(struct igmp_source *source)
 1328: {
 1329:   return pim_time_timer_remain_msec(source->t_source_timer);
 1330: }
 1331: 
 1332: /*
 1333:   RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
 1334: */
 1335: static void group_query_send(struct igmp_group *group)
 1336: {
 1337:   long              lmqc;    /* Last Member Query Count */
 1338: 
 1339:   lmqc = group->group_igmp_sock->querier_robustness_variable;
 1340: 
 1341:   /* lower group timer to lmqt */
 1342:   igmp_group_timer_lower_to_lmqt(group);
 1343: 
 1344:   /* reset retransmission counter */
 1345:   group->group_specific_query_retransmit_count = lmqc;
 1346: 
 1347:   /* immediately send group specific query (decrease retransmit counter by 1)*/
 1348:   group_retransmit_group(group);
 1349: 
 1350:   /* make sure group retransmit timer is running */
 1351:   group_retransmit_timer_on(group);
 1352: }
 1353: 
 1354: /*
 1355:   RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
 1356: */
 1357: static void source_query_send_by_flag(struct igmp_group *group,
 1358: 				      int num_sources_tosend)
 1359: {
 1360:   struct igmp_sock     *igmp;
 1361:   struct pim_interface *pim_ifp;
 1362:   struct listnode      *src_node;
 1363:   struct igmp_source   *src;
 1364:   long                  lmqc;      /* Last Member Query Count */
 1365:   long                  lmqi_msec; /* Last Member Query Interval */
 1366:   long                  lmqt_msec; /* Last Member Query Time */
 1367: 
 1368:   zassert(num_sources_tosend > 0);
 1369: 
 1370:   igmp = group->group_igmp_sock;
 1371:   pim_ifp = igmp->interface->info;
 1372: 
 1373:   lmqc      = igmp->querier_robustness_variable;
 1374:   lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
 1375:   lmqt_msec = lmqc * lmqi_msec;
 1376: 
 1377:   /*
 1378:     RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
 1379: 
 1380:     (...) for each of the sources in X of group G, with source timer larger
 1381:     than LMQT:
 1382:     o Set number of retransmissions for each source to [Last Member
 1383:     Query Count].
 1384:     o Lower source timer to LMQT.
 1385:   */
 1386:   for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
 1387:     if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
 1388:       /* source "src" in X of group G */
 1389:       if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
 1390: 	src->source_query_retransmit_count = lmqc;
 1391: 	igmp_source_timer_lower_to_lmqt(src);
 1392:       }
 1393:     }
 1394:   }
 1395: 
 1396:   /* send group-and-source specific queries */
 1397:   group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
 1398: 
 1399:   /* make sure group retransmit timer is running */
 1400:   group_retransmit_timer_on(group);
 1401: }
 1402: 
 1403: static void block_excl(struct igmp_group *group,
 1404: 		       int num_sources, struct in_addr *sources)
 1405: {
 1406:   int num_sources_tosend = 0;
 1407:   int i;
 1408: 
 1409:   /* 1. clear off SEND flag from all known sources (X,Y) */
 1410:   source_clear_send_flag(group->group_source_list);
 1411: 
 1412:   /* 2. scan received sources (A) */
 1413:   for (i = 0; i < num_sources; ++i) {
 1414:     struct igmp_source *source;
 1415:     struct in_addr     *src_addr;
 1416:     
 1417:     src_addr = sources + i;
 1418:     
 1419:     /* lookup reported source (A) in known sources (X,Y) */
 1420:     source = igmp_find_source_by_addr(group, *src_addr);
 1421:     if (!source) {
 1422:       /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
 1423:       long group_timer_msec;
 1424:       source = source_new(group, *src_addr,
 1425: 			  group->group_igmp_sock->interface->name);
 1426:       if (!source) {
 1427: 	/* ugh, internal malloc failure, skip source */
 1428: 	continue;
 1429:       }
 1430: 
 1431:       zassert(!source->t_source_timer); /* timer == 0 */
 1432:       group_timer_msec = igmp_group_timer_remain_msec(group);
 1433:       igmp_source_timer_on(group, source, group_timer_msec);
 1434:       zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
 1435:     }
 1436: 
 1437:     if (source->t_source_timer) {
 1438:       /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
 1439:       IGMP_SOURCE_DO_SEND(source->source_flags);
 1440:       ++num_sources_tosend;
 1441:     }
 1442:   }
 1443:  
 1444:   /* 5. send sources marked with SEND flag: Q(G,A-Y) */
 1445:   if (num_sources_tosend > 0) {
 1446:     source_query_send_by_flag(group, num_sources_tosend);
 1447:   }
 1448: }
 1449: 
 1450: static void block_incl(struct igmp_group *group,
 1451: 		       int num_sources, struct in_addr *sources)
 1452: {
 1453:   int num_sources_tosend = 0;
 1454:   int i;
 1455: 
 1456:   /* 1. clear off SEND flag from all known sources (B) */
 1457:   source_clear_send_flag(group->group_source_list);
 1458: 
 1459:   /* 2. scan received sources (A) */
 1460:   for (i = 0; i < num_sources; ++i) {
 1461:     struct igmp_source *source;
 1462:     struct in_addr     *src_addr;
 1463:     
 1464:     src_addr = sources + i;
 1465:     
 1466:     /* lookup reported source (A) in known sources (B) */
 1467:     source = igmp_find_source_by_addr(group, *src_addr);
 1468:     if (source) {
 1469:       /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
 1470:       IGMP_SOURCE_DO_SEND(source->source_flags);
 1471:       ++num_sources_tosend;
 1472:     }
 1473:   } 
 1474:  
 1475:   /* 4. send sources marked with SEND flag: Q(G,A*B) */
 1476:   if (num_sources_tosend > 0) {
 1477:     source_query_send_by_flag(group, num_sources_tosend);
 1478:   }
 1479: }
 1480: 
 1481: void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
 1482: 			 struct in_addr group_addr,
 1483: 			 int num_sources, struct in_addr *sources)
 1484: {
 1485:   struct interface *ifp = igmp->interface;
 1486:   struct igmp_group *group;
 1487: 
 1488:   on_trace(__PRETTY_FUNCTION__,
 1489: 	   ifp, from, group_addr, num_sources, sources);
 1490: 
 1491:   /* non-existant group is created as INCLUDE {empty} */
 1492:   group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
 1493:   if (!group) {
 1494:     return;
 1495:   }
 1496: 
 1497:   if (group->group_filtermode_isexcl) {
 1498:     /* EXCLUDE mode */
 1499:     block_excl(group, num_sources, sources);
 1500:   }
 1501:   else {
 1502:     /* INCLUDE mode */
 1503:     block_incl(group, num_sources, sources);
 1504:   }
 1505: }
 1506: 
 1507: void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
 1508: {
 1509:   struct igmp_sock     *igmp;
 1510:   struct interface     *ifp;
 1511:   struct pim_interface *pim_ifp;
 1512:   char                 *ifname;
 1513:   int   lmqi_dsec; /* Last Member Query Interval */
 1514:   int   lmqc;      /* Last Member Query Count */
 1515:   int   lmqt_msec; /* Last Member Query Time */
 1516: 
 1517:   /*
 1518:     RFC 3376: 6.2.2. Definition of Group Timers
 1519:     
 1520:     The group timer is only used when a group is in EXCLUDE mode and
 1521:     it represents the time for the *filter-mode* of the group to
 1522:     expire and switch to INCLUDE mode.
 1523:   */
 1524:   if (!group->group_filtermode_isexcl) {
 1525:     return;
 1526:   }
 1527: 
 1528:   igmp    = group->group_igmp_sock;
 1529:   ifp     = igmp->interface;
 1530:   pim_ifp = ifp->info;
 1531:   ifname  = ifp->name;
 1532: 
 1533:   lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
 1534:   lmqc      = igmp->querier_robustness_variable;
 1535:   lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
 1536: 
 1537:   if (PIM_DEBUG_IGMP_TRACE) {
 1538:     char group_str[100];
 1539:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1540:     zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
 1541: 	       __PRETTY_FUNCTION__,
 1542: 	       group_str, ifname,
 1543: 	       lmqc, lmqi_dsec, lmqt_msec);
 1544:   }
 1545: 
 1546:   zassert(group->group_filtermode_isexcl);
 1547: 
 1548:   igmp_group_timer_on(group, lmqt_msec, ifname);
 1549: }
 1550: 
 1551: void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
 1552: {
 1553:   struct igmp_group    *group;
 1554:   struct igmp_sock     *igmp;
 1555:   struct interface     *ifp;
 1556:   struct pim_interface *pim_ifp;
 1557:   char                 *ifname;
 1558:   int   lmqi_dsec; /* Last Member Query Interval */
 1559:   int   lmqc;      /* Last Member Query Count */
 1560:   int   lmqt_msec; /* Last Member Query Time */
 1561: 
 1562:   group   = source->source_group;
 1563:   igmp    = group->group_igmp_sock;
 1564:   ifp     = igmp->interface;
 1565:   pim_ifp = ifp->info;
 1566:   ifname  = ifp->name;
 1567: 
 1568:   lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
 1569:   lmqc      = igmp->querier_robustness_variable;
 1570:   lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
 1571: 
 1572:   if (PIM_DEBUG_IGMP_TRACE) {
 1573:     char group_str[100];
 1574:     char source_str[100];
 1575:     pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
 1576:     pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
 1577:     zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
 1578: 	       __PRETTY_FUNCTION__,
 1579: 	       group_str, source_str, ifname,
 1580: 	       lmqc, lmqi_dsec, lmqt_msec);
 1581:   }
 1582: 
 1583:   igmp_source_timer_on(group, source, lmqt_msec);
 1584: }
 1585: 
 1586: /*
 1587:   Copy sources to message:
 1588:     
 1589:   struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
 1590:   if (num_sources > 0) {
 1591:   struct listnode    *node;
 1592:   struct igmp_source *src;
 1593:   int                 i = 0;
 1594: 
 1595:   for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
 1596:   sources[i++] = src->source_addr;
 1597:   }
 1598:   }
 1599: */
 1600: void pim_igmp_send_membership_query(struct igmp_group *group,
 1601: 				    int fd,
 1602: 				    const char *ifname,
 1603: 				    char *query_buf,
 1604: 				    int query_buf_size,
 1605: 				    int num_sources,
 1606: 				    struct in_addr dst_addr,
 1607: 				    struct in_addr group_addr,
 1608: 				    int query_max_response_time_dsec,
 1609: 				    uint8_t s_flag,
 1610: 				    uint8_t querier_robustness_variable,
 1611: 				    uint16_t querier_query_interval)
 1612: {
 1613:   ssize_t             msg_size;
 1614:   uint8_t             max_resp_code;
 1615:   uint8_t             qqic;
 1616:   ssize_t             sent;
 1617:   struct sockaddr_in  to;
 1618:   socklen_t           tolen;
 1619:   uint16_t            checksum;
 1620: 
 1621:   zassert(num_sources >= 0);
 1622: 
 1623:   msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
 1624:   if (msg_size > query_buf_size) {
 1625:     zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
 1626: 	     __FILE__, __PRETTY_FUNCTION__,
 1627: 	     msg_size, query_buf_size);
 1628:     return;
 1629:   }
 1630: 
 1631:   s_flag = PIM_FORCE_BOOLEAN(s_flag);
 1632:   zassert((s_flag == 0) || (s_flag == 1));
 1633: 
 1634:   max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
 1635:   qqic          = igmp_msg_encode16to8(querier_query_interval);
 1636: 
 1637:   /*
 1638:     RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
 1639: 
 1640:     If non-zero, the QRV field contains the [Robustness Variable]
 1641:     value used by the querier, i.e., the sender of the Query.  If the
 1642:     querier's [Robustness Variable] exceeds 7, the maximum value of
 1643:     the QRV field, the QRV is set to zero.
 1644:   */
 1645:   if (querier_robustness_variable > 7) {
 1646:     querier_robustness_variable = 0;
 1647:   }
 1648: 
 1649:   query_buf[0]                                         = PIM_IGMP_MEMBERSHIP_QUERY;
 1650:   query_buf[1]                                         = max_resp_code;
 1651:   *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET)   = 0; /* for computing checksum */
 1652:   memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
 1653: 
 1654:   query_buf[8]                                         = (s_flag << 3) | querier_robustness_variable;
 1655:   query_buf[9]                                         = qqic;
 1656:   *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
 1657: 
 1658:   checksum = in_cksum(query_buf, msg_size);
 1659:   *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
 1660: 
 1661:   if (PIM_DEBUG_IGMP_PACKETS) {
 1662:     char dst_str[100];
 1663:     char group_str[100];
 1664:     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
 1665:     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
 1666:     zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
 1667: 	       __PRETTY_FUNCTION__,
 1668: 	       dst_str, ifname, group_str, num_sources,
 1669: 	       msg_size, s_flag, querier_robustness_variable,
 1670: 	       querier_query_interval, qqic, checksum);
 1671:   }
 1672: 
 1673: #if 0
 1674:   memset(&to, 0, sizeof(to));
 1675: #endif
 1676:   to.sin_family = AF_INET;
 1677:   to.sin_addr = dst_addr;
 1678: #if 0
 1679:   to.sin_port = htons(0);
 1680: #endif
 1681:   tolen = sizeof(to);
 1682: 
 1683:   sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
 1684:                 (struct sockaddr *)&to, tolen);
 1685:   if (sent != (ssize_t) msg_size) {
 1686:     int e = errno;
 1687:     char dst_str[100];
 1688:     char group_str[100];
 1689:     pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
 1690:     pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
 1691:     if (sent < 0) {
 1692:       zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
 1693: 		__PRETTY_FUNCTION__,
 1694: 		dst_str, ifname, group_str, msg_size,
 1695: 		e, safe_strerror(e));
 1696:     }
 1697:     else {
 1698:       zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
 1699: 		__PRETTY_FUNCTION__,
 1700: 		dst_str, ifname, group_str,
 1701: 		msg_size, sent);
 1702:     }
 1703:     return;
 1704:   }
 1705: 
 1706:   /*
 1707:     s_flag sanity test: s_flag must be set for general queries
 1708: 
 1709:     RFC 3376: 6.6.1. Timer Updates
 1710: 
 1711:     When a router sends or receives a query with a clear Suppress
 1712:     Router-Side Processing flag, it must update its timers to reflect
 1713:     the correct timeout values for the group or sources being queried.
 1714: 
 1715:     General queries don't trigger timer update.
 1716:   */
 1717:   if (!s_flag) {
 1718:     /* general query? */
 1719:     if (PIM_INADDR_IS_ANY(group_addr)) {
 1720:       char dst_str[100];
 1721:       char group_str[100];
 1722:       pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
 1723:       pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
 1724:       zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
 1725: 		__PRETTY_FUNCTION__,
 1726: 		dst_str, ifname, group_str, num_sources);
 1727:     }
 1728:   }
 1729: 
 1730: }

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