Annotation of embedaddon/quagga/pimd/pim_igmpv3.c, revision 1.1.1.1

1.1       misho       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>