Annotation of embedaddon/quagga/pimd/pim_igmpv3.c, revision 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>