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

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