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

1.1       misho       1: /*
                      2:   PIM for Quagga
                      3:   Copyright (C) 2008  Everton da Silva Marques
                      4: 
                      5:   This program is free software; you can redistribute it and/or modify
                      6:   it under the terms of the GNU General Public License as published by
                      7:   the Free Software Foundation; either version 2 of the License, or
                      8:   (at your option) any later version.
                      9: 
                     10:   This program is distributed in the hope that it will be useful, but
                     11:   WITHOUT ANY WARRANTY; without even the implied warranty of
                     12:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     13:   General Public License for more details.
                     14:   
                     15:   You should have received a copy of the GNU General Public License
                     16:   along with this program; see the file COPYING; if not, write to the
                     17:   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
                     18:   MA 02110-1301 USA
                     19:   
                     20:   $QuaggaId: $Format:%an, %ai, %h$ $
                     21: */
                     22: 
                     23: #include <zebra.h>
                     24: 
                     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>