back to topotato report
topotato coverage report
Current view: top level - pimd - pim_igmp.c (source / functions) Hit Total Coverage
Test: test_pim_bfd.py::PIMBFDTest Lines: 158 655 24.1 %
Date: 2023-02-24 18:39:40 Functions: 16 46 34.8 %

          Line data    Source code
       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 along
      16             :  * with this program; see the file COPYING; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      18             :  */
      19             : 
      20             : #include <zebra.h>
      21             : 
      22             : #include "memory.h"
      23             : #include "prefix.h"
      24             : #include "if.h"
      25             : #include "hash.h"
      26             : #include "jhash.h"
      27             : #include "lib_errors.h"
      28             : 
      29             : #include "pimd.h"
      30             : #include "pim_instance.h"
      31             : #include "pim_igmp.h"
      32             : #include "pim_igmpv2.h"
      33             : #include "pim_igmpv3.h"
      34             : #include "pim_igmp_mtrace.h"
      35             : #include "pim_iface.h"
      36             : #include "pim_sock.h"
      37             : #include "pim_mroute.h"
      38             : #include "pim_str.h"
      39             : #include "pim_util.h"
      40             : #include "pim_time.h"
      41             : #include "pim_ssm.h"
      42             : #include "pim_tib.h"
      43             : 
      44             : static void group_timer_off(struct gm_group *group);
      45             : static void pim_igmp_general_query(struct thread *t);
      46             : 
      47           0 : void igmp_anysource_forward_start(struct pim_instance *pim,
      48             :                                   struct gm_group *group)
      49             : {
      50           0 :         struct gm_source *source;
      51           0 :         struct in_addr src_addr = {.s_addr = 0};
      52             :         /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
      53           0 :         assert(group->group_filtermode_isexcl);
      54           0 :         assert(listcount(group->group_source_list) < 1);
      55             : 
      56           0 :         source = igmp_get_source_by_addr(group, src_addr, NULL);
      57           0 :         if (!source) {
      58           0 :                 zlog_warn("%s: Failure to create * source", __func__);
      59           0 :                 return;
      60             :         }
      61             : 
      62           0 :         igmp_source_forward_start(pim, source);
      63             : }
      64             : 
      65           0 : void igmp_anysource_forward_stop(struct gm_group *group)
      66             : {
      67           0 :         struct gm_source *source;
      68           0 :         struct in_addr star = {.s_addr = 0};
      69             : 
      70           0 :         source = igmp_find_source_by_addr(group, star);
      71           0 :         if (source)
      72           0 :                 igmp_source_forward_stop(source);
      73           0 : }
      74             : 
      75           0 : static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
      76             :                                                struct gm_source *source,
      77             :                                                int is_grp_ssm)
      78             : {
      79           0 :         pim_sgaddr sg;
      80           0 :         struct gm_group *group = source->source_group;
      81             : 
      82           0 :         memset(&sg, 0, sizeof(sg));
      83           0 :         sg.src = source->source_addr;
      84           0 :         sg.grp = group->group_addr;
      85             : 
      86             :         /** if there is no PIM state **/
      87           0 :         if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
      88           0 :                 if (pim_addr_is_any(source->source_addr)) {
      89           0 :                         if (is_grp_ssm) {
      90           0 :                                 if (PIM_DEBUG_PIM_EVENTS)
      91           0 :                                         zlog_debug(
      92             :                                                 "local membership del for %pSG as G is now SSM",
      93             :                                                 &sg);
      94           0 :                                 igmp_source_forward_stop(source);
      95             :                         }
      96             :                 } else {
      97           0 :                         if (!is_grp_ssm) {
      98           0 :                                 if (PIM_DEBUG_PIM_EVENTS)
      99           0 :                                         zlog_debug(
     100             :                                                 "local membership del for %pSG as G is now ASM",
     101             :                                                 &sg);
     102           0 :                                 igmp_source_forward_stop(source);
     103             :                         }
     104             :                 }
     105             :         } else {
     106           0 :                 if (!pim_addr_is_any(source->source_addr) && (is_grp_ssm)) {
     107           0 :                         if (PIM_DEBUG_PIM_EVENTS)
     108           0 :                                 zlog_debug(
     109             :                                         "local membership add for %pSG as G is now SSM",
     110             :                                         &sg);
     111           0 :                         igmp_source_forward_start(pim, source);
     112             :                 }
     113             :         }
     114           0 : }
     115             : 
     116           0 : void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
     117             : {
     118           0 :         struct interface *ifp;
     119             : 
     120           0 :         FOR_ALL_INTERFACES (pim->vrf, ifp) {
     121           0 :                 struct pim_interface *pim_ifp = ifp->info;
     122           0 :                 struct listnode *grpnode, *grp_nextnode;
     123           0 :                 struct gm_group *grp;
     124           0 :                 struct pim_ifchannel *ch, *ch_temp;
     125             : 
     126           0 :                 if (!pim_ifp)
     127           0 :                         continue;
     128             : 
     129             :                 /* scan igmp groups */
     130           0 :                 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grpnode,
     131             :                                        grp_nextnode, grp)) {
     132           0 :                         struct listnode *srcnode;
     133           0 :                         struct gm_source *src;
     134           0 :                         int is_grp_ssm;
     135             : 
     136             :                         /*
     137             :                          * RFC 4604
     138             :                          * section 2.2.1
     139             :                          * EXCLUDE mode does not apply to SSM addresses,
     140             :                          * and an SSM-aware router will ignore
     141             :                          * MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE
     142             :                          * requests in the SSM range.
     143             :                          */
     144           0 :                         is_grp_ssm = pim_is_grp_ssm(pim, grp->group_addr);
     145           0 :                         if (is_grp_ssm && grp->group_filtermode_isexcl) {
     146           0 :                                 igmp_group_delete(grp);
     147             :                         } else {
     148             :                                 /* scan group sources */
     149           0 :                                 for (ALL_LIST_ELEMENTS_RO(
     150             :                                              grp->group_source_list, srcnode,
     151             :                                              src)) {
     152           0 :                                         igmp_source_forward_reevaluate_one(
     153             :                                                 pim, src, is_grp_ssm);
     154             :                                 } /* scan group sources */
     155             :                         }
     156             :                 } /* scan igmp groups */
     157             : 
     158           0 :                 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
     159             :                                  ch_temp) {
     160           0 :                         if (pim_is_grp_ssm(pim, ch->sg.grp)) {
     161           0 :                                 if (pim_addr_is_any(ch->sg.src))
     162           0 :                                         pim_ifchannel_delete(ch);
     163             :                         }
     164             :                 }
     165             :         } /* scan interfaces */
     166           0 : }
     167             : 
     168           0 : void igmp_source_forward_start(struct pim_instance *pim,
     169             :                                struct gm_source *source)
     170             : {
     171           0 :         struct gm_group *group;
     172           0 :         pim_sgaddr sg;
     173             : 
     174           0 :         memset(&sg, 0, sizeof(sg));
     175           0 :         sg.src = source->source_addr;
     176           0 :         sg.grp = source->source_group->group_addr;
     177             : 
     178           0 :         if (PIM_DEBUG_GM_TRACE) {
     179           0 :                 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
     180             :                            source->source_group->interface->name,
     181             :                            IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
     182             :         }
     183             : 
     184             :         /*
     185             :          * PIM state should not be allowed for ASM group with valid source
     186             :          * address.
     187             :          */
     188           0 :         if ((!pim_is_grp_ssm(pim, source->source_group->group_addr)) &&
     189           0 :             !pim_addr_is_any(source->source_addr)) {
     190           0 :                 zlog_warn(
     191             :                         "%s: (S,G)=%pSG ASM range having source address, not allowed to create PIM state",
     192             :                         __func__, &sg);
     193           0 :                 return;
     194             :         }
     195             : 
     196             :         /* Prevent IGMP interface from installing multicast route multiple
     197             :            times */
     198           0 :         if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
     199             :                 return;
     200             :         }
     201             : 
     202           0 :         group = source->source_group;
     203             : 
     204           0 :         if (tib_sg_gm_join(pim, sg, group->interface,
     205             :                            &source->source_channel_oil))
     206           0 :                 IGMP_SOURCE_DO_FORWARDING(source->source_flags);
     207             : }
     208             : 
     209             : /*
     210             :   igmp_source_forward_stop: stop forwarding, but keep the source
     211             :   igmp_source_delete:       stop forwarding, and delete the source
     212             :  */
     213           0 : void igmp_source_forward_stop(struct gm_source *source)
     214             : {
     215           0 :         struct pim_interface *pim_oif;
     216           0 :         struct gm_group *group;
     217           0 :         pim_sgaddr sg;
     218             : 
     219           0 :         memset(&sg, 0, sizeof(sg));
     220           0 :         sg.src = source->source_addr;
     221           0 :         sg.grp = source->source_group->group_addr;
     222             : 
     223           0 :         if (PIM_DEBUG_GM_TRACE) {
     224           0 :                 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
     225             :                            source->source_group->interface->name,
     226             :                            IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
     227             :         }
     228             : 
     229             :         /* Prevent IGMP interface from removing multicast route multiple
     230             :            times */
     231           0 :         if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
     232           0 :                 return;
     233             :         }
     234             : 
     235           0 :         group = source->source_group;
     236           0 :         pim_oif = group->interface->info;
     237             : 
     238           0 :         tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
     239             :                         &source->source_channel_oil);
     240           0 :         IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
     241             : }
     242             : 
     243             : /* This socket is used for TXing IGMP packets only, IGMP RX happens
     244             :  * in pim_mroute_msg()
     245             :  */
     246          37 : static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp)
     247             : {
     248          37 :         int fd;
     249          37 :         int join = 0;
     250          37 :         struct in_addr group;
     251          37 :         struct pim_interface *pim_ifp = ifp->info;
     252             : 
     253          37 :         fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
     254             : 
     255          37 :         if (fd < 0)
     256             :                 return -1;
     257             : 
     258          37 :         if (inet_aton(PIM_ALL_ROUTERS, &group)) {
     259          37 :                 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
     260          37 :                         ++join;
     261             :         } else {
     262           0 :                 zlog_warn(
     263             :                         "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
     264             :                         __FILE__, __func__, fd, &ifaddr, PIM_ALL_ROUTERS, errno,
     265             :                         safe_strerror(errno));
     266             :         }
     267             : 
     268             :         /*
     269             :           IGMP routers periodically send IGMP general queries to
     270             :           AllSystems=224.0.0.1
     271             :           IGMP routers must receive general queries for querier election.
     272             :         */
     273          37 :         if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
     274          37 :                 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
     275          37 :                         ++join;
     276             :         } else {
     277           0 :                 zlog_warn(
     278             :                         "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
     279             :                         __FILE__, __func__, fd, &ifaddr,
     280             :                         PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
     281             :         }
     282             : 
     283          37 :         if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
     284          37 :                 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
     285             :                                      pim_ifp)) {
     286          37 :                         ++join;
     287             :                 }
     288             :         } else {
     289           0 :                 zlog_warn(
     290             :                         "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
     291             :                         __FILE__, __func__, fd, &ifaddr,
     292             :                         PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
     293             :         }
     294             : 
     295          37 :         if (!join) {
     296           0 :                 flog_err_sys(
     297             :                         EC_LIB_SOCKET,
     298             :                         "IGMP socket fd=%d could not join any group on interface address %pI4",
     299             :                         fd, &ifaddr);
     300           0 :                 close(fd);
     301           0 :                 fd = -1;
     302             :         }
     303             : 
     304             :         return fd;
     305             : }
     306             : 
     307             : #undef IGMP_SOCK_DUMP
     308             : 
     309             : #ifdef IGMP_SOCK_DUMP
     310             : static void igmp_sock_dump(array_t *igmp_sock_array)
     311             : {
     312             :         int size = array_size(igmp_sock_array);
     313             :         for (int i = 0; i < size; ++i) {
     314             : 
     315             :                 struct gm_sock *igmp = array_get(igmp_sock_array, i);
     316             : 
     317             :                 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__,
     318             :                            __func__, i, size, &igmp->ifaddr,
     319             :                            igmp->fd);
     320             :         }
     321             : }
     322             : #endif
     323             : 
     324          89 : struct gm_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
     325             :                                             struct in_addr ifaddr)
     326             : {
     327          89 :         struct listnode *sock_node;
     328          89 :         struct gm_sock *igmp;
     329             : 
     330             : #ifdef IGMP_SOCK_DUMP
     331             :         igmp_sock_dump(igmp_sock_list);
     332             : #endif
     333             : 
     334         185 :         for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
     335          69 :                 if (ifaddr.s_addr == igmp->ifaddr.s_addr)
     336          62 :                         return igmp;
     337             : 
     338             :         return NULL;
     339             : }
     340             : 
     341           0 : static void pim_igmp_other_querier_expire(struct thread *t)
     342             : {
     343           0 :         struct gm_sock *igmp;
     344             : 
     345           0 :         igmp = THREAD_ARG(t);
     346             : 
     347           0 :         assert(!igmp->t_igmp_query_timer);
     348             : 
     349           0 :         if (PIM_DEBUG_GM_TRACE) {
     350           0 :                 char ifaddr_str[INET_ADDRSTRLEN];
     351           0 :                 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     352             :                                sizeof(ifaddr_str));
     353           0 :                 zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
     354             :         }
     355             :         /* Mark the interface address as querier address */
     356           0 :         igmp->querier_addr = igmp->ifaddr;
     357             : 
     358             :         /*
     359             :           We are the current querier, then
     360             :           re-start sending general queries.
     361             :           RFC 2236 - sec 7 Other Querier
     362             :           present timer expired (Send General
     363             :           Query, Set Gen. Query. timer)
     364             :         */
     365           0 :         pim_igmp_general_query(t);
     366           0 : }
     367             : 
     368           0 : void pim_igmp_other_querier_timer_on(struct gm_sock *igmp)
     369             : {
     370           0 :         long other_querier_present_interval_msec;
     371           0 :         struct pim_interface *pim_ifp;
     372             : 
     373           0 :         assert(igmp);
     374           0 :         assert(igmp->interface);
     375           0 :         assert(igmp->interface->info);
     376             : 
     377           0 :         pim_ifp = igmp->interface->info;
     378             : 
     379           0 :         if (igmp->t_other_querier_timer) {
     380             :                 /*
     381             :                   There is other querier present already,
     382             :                   then reset the other-querier-present timer.
     383             :                 */
     384             : 
     385           0 :                 if (PIM_DEBUG_GM_TRACE) {
     386           0 :                         char ifaddr_str[INET_ADDRSTRLEN];
     387           0 :                         pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     388             :                                        sizeof(ifaddr_str));
     389           0 :                         zlog_debug(
     390             :                                 "Querier %s resetting TIMER event for Other-Querier-Present",
     391             :                                 ifaddr_str);
     392             :                 }
     393           0 :                 THREAD_OFF(igmp->t_other_querier_timer);
     394             :         } else {
     395             :                 /*
     396             :                   We are the current querier, then stop sending general queries:
     397             :                   igmp->t_igmp_query_timer = NULL;
     398             :                 */
     399           0 :                 pim_igmp_general_query_off(igmp);
     400             :         }
     401             : 
     402             :         /*
     403             :           Since this socket is starting the other-querier-present timer,
     404             :           there should not be periodic query timer for this socket.
     405             :          */
     406           0 :         assert(!igmp->t_igmp_query_timer);
     407             : 
     408             :         /*
     409             :           RFC 3376: 8.5. Other Querier Present Interval
     410             : 
     411             :           The Other Querier Present Interval is the length of time that must
     412             :           pass before a multicast router decides that there is no longer
     413             :           another multicast router which should be the querier.  This value
     414             :           MUST be ((the Robustness Variable) times (the Query Interval)) plus
     415             :           (one half of one Query Response Interval).
     416             : 
     417             :           other_querier_present_interval_msec = \
     418             :             igmp->querier_robustness_variable * \
     419             :             1000 * igmp->querier_query_interval + \
     420             :             100 * (pim_ifp->query_max_response_time_dsec >> 1);
     421             :         */
     422           0 :         other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC(
     423             :                 igmp->querier_robustness_variable, igmp->querier_query_interval,
     424             :                 pim_ifp->gm_query_max_response_time_dsec);
     425             : 
     426           0 :         if (PIM_DEBUG_GM_TRACE) {
     427           0 :                 char ifaddr_str[INET_ADDRSTRLEN];
     428           0 :                 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     429             :                                sizeof(ifaddr_str));
     430           0 :                 zlog_debug(
     431             :                         "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
     432             :                         ifaddr_str, other_querier_present_interval_msec / 1000,
     433             :                         other_querier_present_interval_msec % 1000);
     434             :         }
     435             : 
     436           0 :         thread_add_timer_msec(router->master, pim_igmp_other_querier_expire,
     437             :                               igmp, other_querier_present_interval_msec,
     438             :                               &igmp->t_other_querier_timer);
     439           0 : }
     440             : 
     441          37 : void pim_igmp_other_querier_timer_off(struct gm_sock *igmp)
     442             : {
     443          37 :         assert(igmp);
     444             : 
     445          37 :         if (PIM_DEBUG_GM_TRACE) {
     446           0 :                 if (igmp->t_other_querier_timer) {
     447           0 :                         char ifaddr_str[INET_ADDRSTRLEN];
     448           0 :                         pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     449             :                                        sizeof(ifaddr_str));
     450           0 :                         zlog_debug(
     451             :                                 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
     452             :                                 ifaddr_str, igmp->fd, igmp->interface->name);
     453             :                 }
     454             :         }
     455          37 :         THREAD_OFF(igmp->t_other_querier_timer);
     456          37 : }
     457             : 
     458           0 : int igmp_validate_checksum(char *igmp_msg, int igmp_msg_len)
     459             : {
     460           0 :         uint16_t recv_checksum;
     461           0 :         uint16_t checksum;
     462             : 
     463           0 :         IGMP_GET_INT16((unsigned char *)(igmp_msg + IGMP_CHECKSUM_OFFSET),
     464             :                        recv_checksum);
     465             : 
     466             :         /* Clear the checksum field */
     467           0 :         memset(igmp_msg + IGMP_CHECKSUM_OFFSET, 0, 2);
     468             : 
     469           0 :         checksum = in_cksum(igmp_msg, igmp_msg_len);
     470           0 :         if (ntohs(checksum) != recv_checksum) {
     471           0 :                 zlog_warn("Invalid checksum received %x, calculated %x",
     472             :                           recv_checksum, ntohs(checksum));
     473           0 :                 return -1;
     474             :         }
     475             : 
     476             :         return 0;
     477             : }
     478             : 
     479           0 : static int igmp_recv_query(struct gm_sock *igmp, int query_version,
     480             :                            int max_resp_code, struct in_addr from,
     481             :                            const char *from_str, char *igmp_msg,
     482             :                            int igmp_msg_len)
     483             : {
     484           0 :         struct interface *ifp;
     485           0 :         struct pim_interface *pim_ifp;
     486           0 :         struct in_addr group_addr;
     487             : 
     488           0 :         if (igmp->mtrace_only)
     489             :                 return 0;
     490             : 
     491           0 :         memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
     492             : 
     493           0 :         ifp = igmp->interface;
     494           0 :         pim_ifp = ifp->info;
     495             : 
     496           0 :         if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
     497           0 :                 zlog_warn(
     498             :                         "Recv IGMP query v%d from %s on %s with invalid checksum",
     499             :                         query_version, from_str, ifp->name);
     500           0 :                 return -1;
     501             :         }
     502             : 
     503           0 :         if (!pim_if_connected_to_source(ifp, from)) {
     504           0 :                 if (PIM_DEBUG_GM_PACKETS)
     505           0 :                         zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
     506             :                                    ifp->name, from_str);
     507           0 :                 return 0;
     508             :         }
     509             : 
     510           0 :         if (if_address_is_local(&from, AF_INET, ifp->vrf->vrf_id)) {
     511           0 :                 if (PIM_DEBUG_GM_PACKETS)
     512           0 :                         zlog_debug("Recv IGMP query on interface: %s from ourself %s",
     513             :                                    ifp->name, from_str);
     514           0 :                 return 0;
     515             :         }
     516             : 
     517             :         /* Collecting IGMP Rx stats */
     518           0 :         switch (query_version) {
     519           0 :         case 1:
     520           0 :                 igmp->igmp_stats.query_v1++;
     521           0 :                 break;
     522           0 :         case 2:
     523           0 :                 igmp->igmp_stats.query_v2++;
     524           0 :                 break;
     525           0 :         case 3:
     526           0 :                 igmp->igmp_stats.query_v3++;
     527           0 :                 break;
     528           0 :         default:
     529           0 :                 igmp->igmp_stats.unsupported++;
     530             :         }
     531             : 
     532             :         /*
     533             :          * RFC 3376 defines some guidelines on operating in backwards
     534             :          * compatibility with older versions of IGMP but there are some gaps in
     535             :          * the logic:
     536             :          *
     537             :          * - once we drop from say version 3 to version 2 we will never go back
     538             :          *   to version 3 even if the node that TXed an IGMP v2 query upgrades
     539             :          *   to v3
     540             :          *
     541             :          * - The node with the lowest IP is the querier so we will only know to
     542             :          *   drop from v3 to v2 if the node that is the querier is also the one
     543             :          *   that is running igmp v2.  If a non-querier only supports igmp v2
     544             :          *   we will have no way of knowing.
     545             :          *
     546             :          * For now we will simplify things and inform the user that they need to
     547             :          * configure all PIM routers to use the same version of IGMP.
     548             :          */
     549           0 :         if (query_version != pim_ifp->igmp_version) {
     550           0 :                 zlog_warn(
     551             :                         "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version",
     552             :                         query_version, from_str, ifp->name,
     553             :                         pim_ifp->igmp_version);
     554           0 :                 return 0;
     555             :         }
     556             : 
     557           0 :         if (PIM_DEBUG_GM_PACKETS) {
     558           0 :                 char group_str[INET_ADDRSTRLEN];
     559           0 :                 pim_inet4_dump("<group?>", group_addr, group_str,
     560             :                                sizeof(group_str));
     561           0 :                 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
     562             :                            query_version, from_str, ifp->name, group_str);
     563             :         }
     564             : 
     565             :         /*
     566             :           RFC 3376: 6.6.2. Querier Election
     567             : 
     568             :           When a router receives a query with a lower IP address, it sets
     569             :           the Other-Querier-Present timer to Other Querier Present Interval
     570             :           and ceases to send queries on the network if it was the previously
     571             :           elected querier.
     572             :          */
     573           0 :         if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
     574             : 
     575             :                 /* As per RFC 2236 section 3:
     576             :                  * When a Querier receives a Leave Group message for a group
     577             :                  * that has group members on the reception interface, it sends
     578             :                  * [Last Member Query Count] Group-Specific Queries every [Last
     579             :                  * Member Query Interval] to the group being left.  These
     580             :                  * Group-Specific Queries have their Max Response time set to
     581             :                  * [Last Member Query Interval].  If no Reports are received
     582             :                  * after the response time of the last query expires, the
     583             :                  * routers assume that the group has no local members, as above.
     584             :                  * Any Querier to non-Querier transition is ignored during this
     585             :                  * time; the same router keeps sending the Group-Specific
     586             :                  * Queries.
     587             :                  */
     588           0 :                 const struct gm_group *group;
     589           0 :                 const struct listnode *grpnode;
     590             : 
     591           0 :                 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
     592             :                                           group)) {
     593           0 :                         if (!group->t_group_query_retransmit_timer)
     594           0 :                                 continue;
     595             : 
     596           0 :                         if (PIM_DEBUG_GM_TRACE)
     597           0 :                                 zlog_debug(
     598             :                                         "%s: lower address query packet from %s is ignored when last member query interval timer is running",
     599             :                                         ifp->name, from_str);
     600           0 :                         return 0;
     601             :                 }
     602             : 
     603           0 :                 if (PIM_DEBUG_GM_TRACE) {
     604           0 :                         char ifaddr_str[INET_ADDRSTRLEN];
     605           0 :                         pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     606             :                                        sizeof(ifaddr_str));
     607           0 :                         zlog_debug(
     608             :                                 "%s: local address %s (%u) lost querier election to %s (%u)",
     609             :                                 ifp->name, ifaddr_str,
     610             :                                 ntohl(igmp->ifaddr.s_addr), from_str,
     611             :                                 ntohl(from.s_addr));
     612             :                 }
     613             :                 /* Reset the other querier timer only if query is received from
     614             :                  * the previously elected querier or a better new querier
     615             :                  * This will make sure that non-querier elects the new querier
     616             :                  * whose ip address is higher than the old querier
     617             :                  * in case the old querier goes down via other querier present
     618             :                  * timer expiry
     619             :                  */
     620           0 :                 if (ntohl(from.s_addr) <= ntohl(igmp->querier_addr.s_addr)) {
     621           0 :                         igmp->querier_addr.s_addr = from.s_addr;
     622           0 :                         pim_igmp_other_querier_timer_on(igmp);
     623             :                 }
     624             :         }
     625             : 
     626             :         /* IGMP version 3 is the only one where we process the RXed query */
     627           0 :         if (query_version == 3) {
     628           0 :                 igmp_v3_recv_query(igmp, from_str, igmp_msg);
     629             :         }
     630             : 
     631             :         return 0;
     632             : }
     633             : 
     634           0 : static void on_trace(const char *label, struct interface *ifp,
     635             :                      struct in_addr from)
     636             : {
     637           0 :         if (PIM_DEBUG_GM_TRACE) {
     638           0 :                 char from_str[INET_ADDRSTRLEN];
     639           0 :                 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
     640           0 :                 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
     641             :         }
     642           0 : }
     643             : 
     644           0 : static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
     645             :                                const char *from_str, char *igmp_msg,
     646             :                                int igmp_msg_len)
     647             : {
     648           0 :         struct interface *ifp = igmp->interface;
     649           0 :         struct gm_group *group;
     650           0 :         struct in_addr group_addr;
     651             : 
     652           0 :         on_trace(__func__, igmp->interface, from);
     653             : 
     654           0 :         if (igmp->mtrace_only)
     655             :                 return 0;
     656             : 
     657           0 :         if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
     658           0 :                 zlog_warn(
     659             :                         "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
     660             :                         from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
     661           0 :                 return -1;
     662             :         }
     663             : 
     664           0 :         if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
     665           0 :                 zlog_warn(
     666             :                         "Recv IGMP report v1 from %s on %s with invalid checksum",
     667             :                         from_str, ifp->name);
     668           0 :                 return -1;
     669             :         }
     670             : 
     671             :         /* Collecting IGMP Rx stats */
     672           0 :         igmp->igmp_stats.report_v1++;
     673             : 
     674           0 :         if (PIM_DEBUG_GM_TRACE) {
     675           0 :                 zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
     676             :         }
     677             : 
     678           0 :         memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
     679             : 
     680           0 :         if (pim_is_group_filtered(ifp->info, &group_addr))
     681             :                 return -1;
     682             : 
     683             :         /* non-existent group is created as INCLUDE {empty} */
     684           0 :         group = igmp_add_group_by_addr(igmp, group_addr);
     685           0 :         if (!group) {
     686             :                 return -1;
     687             :         }
     688             : 
     689           0 :         group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
     690             : 
     691           0 :         return 0;
     692             : }
     693             : 
     694          31 : bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
     695             : {
     696          31 :         char *igmp_msg;
     697          31 :         int igmp_msg_len;
     698          31 :         int msg_type;
     699          31 :         size_t ip_hlen; /* ip header length in bytes */
     700             : 
     701          31 :         if (len < sizeof(*ip_hdr)) {
     702           0 :                 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
     703             :                           sizeof(*ip_hdr));
     704           0 :                 return false;
     705             :         }
     706             : 
     707          31 :         ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
     708          31 :         *hlen = ip_hlen;
     709             : 
     710          31 :         if (ip_hlen > len) {
     711           0 :                 zlog_warn(
     712             :                         "IGMP packet header claims size %zu, but we only have %zu bytes",
     713             :                         ip_hlen, len);
     714           0 :                 return false;
     715             :         }
     716             : 
     717          31 :         igmp_msg = (char *)ip_hdr + ip_hlen;
     718          31 :         igmp_msg_len = len - ip_hlen;
     719          31 :         msg_type = *igmp_msg;
     720             : 
     721          31 :         if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
     722           0 :                 zlog_warn("IGMP message size=%d shorter than minimum=%d",
     723             :                           igmp_msg_len, PIM_IGMP_MIN_LEN);
     724           0 :                 return false;
     725             :         }
     726             : 
     727          31 :         if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
     728          31 :             && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
     729          31 :                 if (ip_hdr->ip_ttl != 1) {
     730           0 :                         zlog_warn(
     731             :                                 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
     732             :                                 ip_hdr->ip_ttl);
     733           0 :                         return false;
     734             :                 }
     735             :         }
     736             : 
     737             :         return true;
     738             : }
     739             : 
     740          31 : int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
     741             : {
     742          31 :         struct ip *ip_hdr = (struct ip *)buf;
     743          31 :         size_t ip_hlen; /* ip header length in bytes */
     744          31 :         char *igmp_msg;
     745          31 :         int igmp_msg_len;
     746          31 :         int msg_type;
     747          31 :         char from_str[INET_ADDRSTRLEN];
     748          31 :         char to_str[INET_ADDRSTRLEN];
     749             : 
     750          31 :         if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
     751             :                 return -1;
     752             : 
     753          31 :         igmp_msg = buf + ip_hlen;
     754          31 :         igmp_msg_len = len - ip_hlen;
     755          31 :         msg_type = *igmp_msg;
     756             : 
     757          31 :         pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
     758          31 :         pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
     759             : 
     760          31 :         if (PIM_DEBUG_GM_PACKETS) {
     761           0 :                 zlog_debug(
     762             :                         "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
     763             :                         from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
     764             :                         msg_type, igmp_msg_len);
     765             :         }
     766             : 
     767          31 :         switch (msg_type) {
     768           0 :         case PIM_IGMP_MEMBERSHIP_QUERY: {
     769           0 :                 int max_resp_code = igmp_msg[1];
     770           0 :                 int query_version;
     771             : 
     772             :                 /*
     773             :                   RFC 3376: 7.1. Query Version Distinctions
     774             :                   IGMPv1 Query: length = 8 octets AND Max Resp Code field is
     775             :                   zero
     776             :                   IGMPv2 Query: length = 8 octets AND Max Resp Code field is
     777             :                   non-zero
     778             :                   IGMPv3 Query: length >= 12 octets
     779             :                 */
     780             : 
     781           0 :                 if (igmp_msg_len == 8) {
     782           0 :                         query_version = max_resp_code ? 2 : 1;
     783           0 :                 } else if (igmp_msg_len >= 12) {
     784             :                         query_version = 3;
     785             :                 } else {
     786           0 :                         zlog_warn("Unknown IGMP query version");
     787           0 :                         return -1;
     788             :                 }
     789             : 
     790           0 :                 return igmp_recv_query(igmp, query_version, max_resp_code,
     791             :                                        ip_hdr->ip_src, from_str, igmp_msg,
     792             :                                        igmp_msg_len);
     793             :         }
     794             : 
     795          31 :         case PIM_IGMP_V3_MEMBERSHIP_REPORT:
     796          31 :                 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
     797             :                                            igmp_msg, igmp_msg_len);
     798             : 
     799           0 :         case PIM_IGMP_V2_MEMBERSHIP_REPORT:
     800           0 :                 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
     801             :                                            igmp_msg, igmp_msg_len);
     802             : 
     803           0 :         case PIM_IGMP_V1_MEMBERSHIP_REPORT:
     804           0 :                 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
     805             :                                            igmp_msg, igmp_msg_len);
     806             : 
     807           0 :         case PIM_IGMP_V2_LEAVE_GROUP:
     808           0 :                 return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
     809             :                                           igmp_msg_len);
     810             : 
     811           0 :         case PIM_IGMP_MTRACE_RESPONSE:
     812           0 :                 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
     813             :                                                  from_str, igmp_msg,
     814             :                                                  igmp_msg_len);
     815           0 :         case PIM_IGMP_MTRACE_QUERY_REQUEST:
     816           0 :                 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
     817             :                                                 from_str, igmp_msg,
     818             :                                                 igmp_msg_len);
     819             :         }
     820             : 
     821           0 :         zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
     822             : 
     823             :         /* Collecting IGMP Rx stats */
     824           0 :         igmp->igmp_stats.unsupported++;
     825             : 
     826           0 :         return -1;
     827             : }
     828             : 
     829           0 : void pim_igmp_general_query_on(struct gm_sock *igmp)
     830             : {
     831           0 :         struct pim_interface *pim_ifp;
     832           0 :         int startup_mode;
     833           0 :         int query_interval;
     834             : 
     835             :         /*
     836             :           Since this socket is starting as querier,
     837             :           there should not exist a timer for other-querier-present.
     838             :          */
     839           0 :         assert(!igmp->t_other_querier_timer);
     840           0 :         pim_ifp = igmp->interface->info;
     841           0 :         assert(pim_ifp);
     842             : 
     843             :         /*
     844             :           RFC 3376: 8.6. Startup Query Interval
     845             : 
     846             :           The Startup Query Interval is the interval between General Queries
     847             :           sent by a Querier on startup.  Default: 1/4 the Query Interval.
     848             :           The first one should be sent out immediately instead of 125/4
     849             :           seconds from now.
     850             :         */
     851           0 :         startup_mode = igmp->startup_query_count > 0;
     852           0 :         if (startup_mode) {
     853             :                 /*
     854             :                  * If this is the first time we are sending a query on a
     855             :                  * newly configured igmp interface send it out in 1 second
     856             :                  * just to give the entire world a tiny bit of time to settle
     857             :                  * else the query interval is:
     858             :                  * query_interval = pim_ifp->gm_default_query_interval >> 2;
     859             :                  */
     860           0 :                 if (igmp->startup_query_count ==
     861           0 :                     igmp->querier_robustness_variable)
     862             :                         query_interval = 1;
     863             :                 else
     864           0 :                         query_interval = PIM_IGMP_SQI(
     865             :                                 pim_ifp->gm_default_query_interval);
     866             : 
     867           0 :                 --igmp->startup_query_count;
     868             :         } else {
     869           0 :                 query_interval = igmp->querier_query_interval;
     870             :         }
     871             : 
     872           0 :         if (PIM_DEBUG_GM_TRACE) {
     873           0 :                 char ifaddr_str[INET_ADDRSTRLEN];
     874           0 :                 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     875             :                                sizeof(ifaddr_str));
     876           0 :                 zlog_debug(
     877             :                         "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
     878             :                         ifaddr_str, query_interval,
     879             :                         startup_mode ? "startup" : "non-startup", igmp->fd);
     880             :         }
     881           0 :         thread_add_timer(router->master, pim_igmp_general_query, igmp,
     882             :                          query_interval, &igmp->t_igmp_query_timer);
     883           0 : }
     884             : 
     885          37 : void pim_igmp_general_query_off(struct gm_sock *igmp)
     886             : {
     887          37 :         assert(igmp);
     888             : 
     889          37 :         if (PIM_DEBUG_GM_TRACE) {
     890           0 :                 if (igmp->t_igmp_query_timer) {
     891           0 :                         char ifaddr_str[INET_ADDRSTRLEN];
     892           0 :                         pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
     893             :                                        sizeof(ifaddr_str));
     894           0 :                         zlog_debug(
     895             :                                 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
     896             :                                 ifaddr_str, igmp->fd, igmp->interface->name);
     897             :                 }
     898             :         }
     899          37 :         THREAD_OFF(igmp->t_igmp_query_timer);
     900          37 : }
     901             : 
     902             : /* Issue IGMP general query */
     903           0 : static void pim_igmp_general_query(struct thread *t)
     904           0 : {
     905           0 :         struct gm_sock *igmp;
     906           0 :         struct in_addr dst_addr;
     907           0 :         struct in_addr group_addr;
     908           0 :         struct pim_interface *pim_ifp;
     909           0 :         int query_buf_size;
     910             : 
     911           0 :         igmp = THREAD_ARG(t);
     912             : 
     913           0 :         assert(igmp->interface);
     914           0 :         assert(igmp->interface->info);
     915             : 
     916           0 :         pim_ifp = igmp->interface->info;
     917             : 
     918           0 :         if (pim_ifp->igmp_version == 3) {
     919             :                 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
     920             :         } else {
     921           0 :                 query_buf_size = IGMP_V12_MSG_SIZE;
     922             :         }
     923             : 
     924           0 :         char query_buf[query_buf_size];
     925             : 
     926             :         /*
     927             :           RFC3376: 4.1.12. IP Destination Addresses for Queries
     928             : 
     929             :           In IGMPv3, General Queries are sent with an IP destination address
     930             :           of 224.0.0.1, the all-systems multicast address.  Group-Specific
     931             :           and Group-and-Source-Specific Queries are sent with an IP
     932             :           destination address equal to the multicast address of interest.
     933             :         */
     934             : 
     935           0 :         dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
     936           0 :         group_addr.s_addr = PIM_NET_INADDR_ANY;
     937             : 
     938           0 :         if (PIM_DEBUG_GM_TRACE) {
     939           0 :                 char querier_str[INET_ADDRSTRLEN];
     940           0 :                 char dst_str[INET_ADDRSTRLEN];
     941           0 :                 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
     942             :                                sizeof(querier_str));
     943           0 :                 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
     944           0 :                 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
     945             :                            querier_str, dst_str, igmp->interface->name);
     946             :         }
     947             : 
     948           0 :         igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
     949             :                         sizeof(query_buf), 0 /* num_sources */, dst_addr,
     950             :                         group_addr, pim_ifp->gm_query_max_response_time_dsec,
     951             :                         1 /* s_flag: always set for general queries */, igmp);
     952             : 
     953           0 :         pim_igmp_general_query_on(igmp);
     954           0 : }
     955             : 
     956          37 : static void sock_close(struct gm_sock *igmp)
     957             : {
     958          37 :         pim_igmp_other_querier_timer_off(igmp);
     959          37 :         pim_igmp_general_query_off(igmp);
     960             : 
     961          37 :         if (PIM_DEBUG_GM_TRACE_DETAIL) {
     962           0 :                 if (igmp->t_igmp_read) {
     963           0 :                         zlog_debug(
     964             :                                 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
     965             :                                 &igmp->ifaddr, igmp->fd,
     966             :                                 igmp->interface->name);
     967             :                 }
     968             :         }
     969          37 :         THREAD_OFF(igmp->t_igmp_read);
     970             : 
     971          37 :         if (close(igmp->fd)) {
     972           0 :                 flog_err(
     973             :                         EC_LIB_SOCKET,
     974             :                         "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
     975             :                         &igmp->ifaddr, igmp->fd,
     976             :                         igmp->interface->name, errno, safe_strerror(errno));
     977             :         }
     978             : 
     979          37 :         if (PIM_DEBUG_GM_TRACE_DETAIL) {
     980           0 :                 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
     981             :                            &igmp->ifaddr, igmp->fd,
     982             :                            igmp->interface->name);
     983             :         }
     984          37 : }
     985             : 
     986           0 : void igmp_startup_mode_on(struct gm_sock *igmp)
     987             : {
     988           0 :         struct pim_interface *pim_ifp;
     989             : 
     990           0 :         pim_ifp = igmp->interface->info;
     991             : 
     992             :         /*
     993             :           RFC 3376: 8.7. Startup Query Count
     994             : 
     995             :           The Startup Query Count is the number of Queries sent out on
     996             :           startup, separated by the Startup Query Interval.  Default: the
     997             :           Robustness Variable.
     998             :         */
     999           0 :         igmp->startup_query_count = igmp->querier_robustness_variable;
    1000             : 
    1001             :         /*
    1002             :           Since we're (re)starting, reset QQI to default Query Interval
    1003             :         */
    1004           0 :         igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
    1005           0 : }
    1006             : 
    1007           0 : static void igmp_group_free(struct gm_group *group)
    1008             : {
    1009           0 :         list_delete(&group->group_source_list);
    1010             : 
    1011           0 :         XFREE(MTYPE_PIM_IGMP_GROUP, group);
    1012           0 : }
    1013             : 
    1014           0 : static void igmp_group_count_incr(struct pim_interface *pim_ifp)
    1015             : {
    1016           0 :         uint32_t group_count = listcount(pim_ifp->gm_group_list);
    1017             : 
    1018           0 :         ++pim_ifp->pim->gm_group_count;
    1019           0 :         if (pim_ifp->pim->gm_group_count == pim_ifp->pim->gm_watermark_limit) {
    1020           0 :                 zlog_warn(
    1021             :                         "IGMP group count reached watermark limit: %u(vrf: %s)",
    1022             :                         pim_ifp->pim->gm_group_count,
    1023             :                         VRF_LOGNAME(pim_ifp->pim->vrf));
    1024             :         }
    1025             : 
    1026           0 :         if (pim_ifp->igmp_peak_group_count < group_count)
    1027           0 :                 pim_ifp->igmp_peak_group_count = group_count;
    1028           0 : }
    1029             : 
    1030           0 : static void igmp_group_count_decr(struct pim_interface *pim_ifp)
    1031             : {
    1032           0 :         if (pim_ifp->pim->gm_group_count == 0) {
    1033           0 :                 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
    1034             :                           VRF_LOGNAME(pim_ifp->pim->vrf));
    1035           0 :                 return;
    1036             :         }
    1037             : 
    1038           0 :         --pim_ifp->pim->gm_group_count;
    1039             : }
    1040             : 
    1041           0 : void igmp_group_delete(struct gm_group *group)
    1042             : {
    1043           0 :         struct listnode *src_node;
    1044           0 :         struct listnode *src_nextnode;
    1045           0 :         struct gm_source *src;
    1046           0 :         struct pim_interface *pim_ifp = group->interface->info;
    1047             : 
    1048           0 :         if (PIM_DEBUG_GM_TRACE) {
    1049           0 :                 char group_str[INET_ADDRSTRLEN];
    1050           0 :                 pim_inet4_dump("<group?>", group->group_addr, group_str,
    1051             :                                sizeof(group_str));
    1052           0 :                 zlog_debug("Deleting IGMP group %s from interface %s",
    1053             :                            group_str, group->interface->name);
    1054             :         }
    1055             : 
    1056           0 :         for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
    1057             :                                src)) {
    1058           0 :                 igmp_source_delete(src);
    1059             :         }
    1060             : 
    1061           0 :         THREAD_OFF(group->t_group_query_retransmit_timer);
    1062             : 
    1063           0 :         group_timer_off(group);
    1064           0 :         igmp_group_count_decr(pim_ifp);
    1065           0 :         listnode_delete(pim_ifp->gm_group_list, group);
    1066           0 :         hash_release(pim_ifp->gm_group_hash, group);
    1067             : 
    1068           0 :         igmp_group_free(group);
    1069           0 : }
    1070             : 
    1071           0 : void igmp_group_delete_empty_include(struct gm_group *group)
    1072             : {
    1073           0 :         assert(!group->group_filtermode_isexcl);
    1074           0 :         assert(!listcount(group->group_source_list));
    1075             : 
    1076           0 :         igmp_group_delete(group);
    1077           0 : }
    1078             : 
    1079          37 : void igmp_sock_free(struct gm_sock *igmp)
    1080             : {
    1081          37 :         assert(!igmp->t_igmp_read);
    1082          37 :         assert(!igmp->t_igmp_query_timer);
    1083          37 :         assert(!igmp->t_other_querier_timer);
    1084             : 
    1085          37 :         XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
    1086          37 : }
    1087             : 
    1088          37 : void igmp_sock_delete(struct gm_sock *igmp)
    1089             : {
    1090          37 :         struct pim_interface *pim_ifp;
    1091             : 
    1092          37 :         sock_close(igmp);
    1093             : 
    1094          37 :         pim_ifp = igmp->interface->info;
    1095             : 
    1096          37 :         listnode_delete(pim_ifp->gm_socket_list, igmp);
    1097             : 
    1098          37 :         igmp_sock_free(igmp);
    1099             : 
    1100          37 :         if (!listcount(pim_ifp->gm_socket_list))
    1101          37 :                 pim_igmp_if_reset(pim_ifp);
    1102          37 : }
    1103             : 
    1104          10 : void igmp_sock_delete_all(struct interface *ifp)
    1105             : {
    1106          10 :         struct pim_interface *pim_ifp;
    1107          10 :         struct listnode *igmp_node, *igmp_nextnode;
    1108          10 :         struct gm_sock *igmp;
    1109             : 
    1110          10 :         pim_ifp = ifp->info;
    1111             : 
    1112          26 :         for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
    1113             :                                igmp_nextnode, igmp)) {
    1114           6 :                 igmp_sock_delete(igmp);
    1115             :         }
    1116          10 : }
    1117             : 
    1118           0 : static unsigned int igmp_group_hash_key(const void *arg)
    1119             : {
    1120           0 :         const struct gm_group *group = arg;
    1121             : 
    1122           0 :         return jhash_1word(group->group_addr.s_addr, 0);
    1123             : }
    1124             : 
    1125           0 : static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
    1126             : {
    1127           0 :         const struct gm_group *g1 = (const struct gm_group *)arg1;
    1128           0 :         const struct gm_group *g2 = (const struct gm_group *)arg2;
    1129             : 
    1130           0 :         if (g1->group_addr.s_addr == g2->group_addr.s_addr)
    1131           0 :                 return true;
    1132             : 
    1133             :         return false;
    1134             : }
    1135             : 
    1136          10 : void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
    1137             : {
    1138          10 :         char hash_name[64];
    1139             : 
    1140          10 :         pim_ifp->gm_socket_list = list_new();
    1141          10 :         pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
    1142             : 
    1143          10 :         pim_ifp->gm_group_list = list_new();
    1144          10 :         pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
    1145             : 
    1146          10 :         snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
    1147          10 :         pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
    1148             :                                              igmp_group_hash_equal, hash_name);
    1149          10 : }
    1150             : 
    1151          47 : void pim_igmp_if_reset(struct pim_interface *pim_ifp)
    1152             : {
    1153          47 :         struct listnode *grp_node, *grp_nextnode;
    1154          47 :         struct gm_group *grp;
    1155             : 
    1156          94 :         for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
    1157             :                                grp)) {
    1158           0 :                 igmp_group_delete(grp);
    1159             :         }
    1160          47 : }
    1161             : 
    1162          10 : void pim_igmp_if_fini(struct pim_interface *pim_ifp)
    1163             : {
    1164          10 :         pim_igmp_if_reset(pim_ifp);
    1165             : 
    1166          10 :         assert(pim_ifp->gm_group_list);
    1167          10 :         assert(!listcount(pim_ifp->gm_group_list));
    1168             : 
    1169          10 :         list_delete(&pim_ifp->gm_group_list);
    1170          10 :         hash_free(pim_ifp->gm_group_hash);
    1171             : 
    1172          10 :         list_delete(&pim_ifp->gm_socket_list);
    1173          10 : }
    1174             : 
    1175          37 : static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
    1176             :                                      struct interface *ifp, int mtrace_only)
    1177             : {
    1178          37 :         struct pim_interface *pim_ifp;
    1179          37 :         struct gm_sock *igmp;
    1180             : 
    1181          37 :         pim_ifp = ifp->info;
    1182             : 
    1183          37 :         if (PIM_DEBUG_GM_TRACE) {
    1184           0 :                 zlog_debug(
    1185             :                         "Creating IGMP socket fd=%d for address %pI4 on interface %s",
    1186             :                         fd, &ifaddr, ifp->name);
    1187             :         }
    1188             : 
    1189          37 :         igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
    1190             : 
    1191          37 :         igmp->fd = fd;
    1192          37 :         igmp->interface = ifp;
    1193          37 :         igmp->ifaddr = ifaddr;
    1194          37 :         igmp->querier_addr = ifaddr;
    1195          37 :         igmp->t_igmp_read = NULL;
    1196          37 :         igmp->t_igmp_query_timer = NULL;
    1197          37 :         igmp->t_other_querier_timer = NULL; /* no other querier present */
    1198          37 :         igmp->querier_robustness_variable =
    1199          37 :                 pim_ifp->gm_default_robustness_variable;
    1200          37 :         igmp->sock_creation = pim_time_monotonic_sec();
    1201             : 
    1202          37 :         igmp_stats_init(&igmp->igmp_stats);
    1203             : 
    1204          37 :         if (mtrace_only) {
    1205          37 :                 igmp->mtrace_only = mtrace_only;
    1206          37 :                 return igmp;
    1207             :         }
    1208             : 
    1209           0 :         igmp->mtrace_only = false;
    1210             : 
    1211             :         /*
    1212             :           igmp_startup_mode_on() will reset QQI:
    1213             : 
    1214             :           igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
    1215             :         */
    1216           0 :         igmp_startup_mode_on(igmp);
    1217           0 :         pim_igmp_general_query_on(igmp);
    1218             : 
    1219           0 :         return igmp;
    1220             : }
    1221             : 
    1222             : static void igmp_read_on(struct gm_sock *igmp);
    1223             : 
    1224           0 : static void pim_igmp_read(struct thread *t)
    1225             : {
    1226           0 :         uint8_t buf[10000];
    1227           0 :         struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
    1228           0 :         struct sockaddr_storage from;
    1229           0 :         struct sockaddr_storage to;
    1230           0 :         socklen_t fromlen = sizeof(from);
    1231           0 :         socklen_t tolen = sizeof(to);
    1232           0 :         ifindex_t ifindex = -1;
    1233           0 :         int len;
    1234             : 
    1235           0 :         while (1) {
    1236           0 :                 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
    1237             :                                             &fromlen, &to, &tolen, &ifindex);
    1238           0 :                 if (len < 0) {
    1239           0 :                         if (errno == EINTR)
    1240           0 :                                 continue;
    1241           0 :                         if (errno == EWOULDBLOCK || errno == EAGAIN)
    1242             :                                 break;
    1243             : 
    1244             :                         goto done;
    1245             :                 }
    1246             :         }
    1247             : 
    1248           0 : done:
    1249           0 :         igmp_read_on(igmp);
    1250           0 : }
    1251             : 
    1252          37 : static void igmp_read_on(struct gm_sock *igmp)
    1253             : {
    1254             : 
    1255          37 :         if (PIM_DEBUG_GM_TRACE_DETAIL) {
    1256           0 :                 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
    1257             :                            igmp->fd);
    1258             :         }
    1259          37 :         thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
    1260             :                         &igmp->t_igmp_read);
    1261          37 : }
    1262             : 
    1263          37 : struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
    1264             :                                   struct in_addr ifaddr, struct interface *ifp,
    1265             :                                   bool mtrace_only)
    1266             : {
    1267          37 :         struct gm_sock *igmp;
    1268          37 :         struct sockaddr_in sin;
    1269          37 :         int fd;
    1270             : 
    1271          37 :         fd = igmp_sock_open(ifaddr, ifp);
    1272          37 :         if (fd < 0) {
    1273           0 :                 zlog_warn("Could not open IGMP socket for %pI4 on %s",
    1274             :                           &ifaddr, ifp->name);
    1275           0 :                 return NULL;
    1276             :         }
    1277             : 
    1278          37 :         sin.sin_family = AF_INET;
    1279          37 :         sin.sin_addr = ifaddr;
    1280          37 :         sin.sin_port = 0;
    1281          37 :         if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
    1282           0 :                 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
    1283             :                           &ifaddr, ifp->name, strerror(errno), errno);
    1284           0 :                 close(fd);
    1285             : 
    1286           0 :                 return NULL;
    1287             :         }
    1288             : 
    1289          37 :         igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
    1290             : 
    1291          37 :         igmp_read_on(igmp);
    1292             : 
    1293          37 :         listnode_add(igmp_sock_list, igmp);
    1294             : 
    1295             : #ifdef IGMP_SOCK_DUMP
    1296             :         igmp_sock_dump(igmp_sock_array);
    1297             : #endif
    1298             : 
    1299          37 :         return igmp;
    1300             : }
    1301             : 
    1302             : /*
    1303             :   RFC 3376: 6.5. Switching Router Filter-Modes
    1304             : 
    1305             :   When a router's filter-mode for a group is EXCLUDE and the group
    1306             :   timer expires, the router filter-mode for the group transitions to
    1307             :   INCLUDE.
    1308             : 
    1309             :   A router uses source records with running source timers as its state
    1310             :   for the switch to a filter-mode of INCLUDE.  If there are any source
    1311             :   records with source timers greater than zero (i.e., requested to be
    1312             :   forwarded), a router switches to filter-mode of INCLUDE using those
    1313             :   source records.  Source records whose timers are zero (from the
    1314             :   previous EXCLUDE mode) are deleted.
    1315             :  */
    1316           0 : static void igmp_group_timer(struct thread *t)
    1317             : {
    1318           0 :         struct gm_group *group;
    1319             : 
    1320           0 :         group = THREAD_ARG(t);
    1321             : 
    1322           0 :         if (PIM_DEBUG_GM_TRACE) {
    1323           0 :                 char group_str[INET_ADDRSTRLEN];
    1324           0 :                 pim_inet4_dump("<group?>", group->group_addr, group_str,
    1325             :                                sizeof(group_str));
    1326           0 :                 zlog_debug("%s: Timer for group %s on interface %s", __func__,
    1327             :                            group_str, group->interface->name);
    1328             :         }
    1329             : 
    1330           0 :         assert(group->group_filtermode_isexcl);
    1331             : 
    1332           0 :         group->group_filtermode_isexcl = 0;
    1333             : 
    1334             :         /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
    1335           0 :         igmp_anysource_forward_stop(group);
    1336             : 
    1337           0 :         igmp_source_delete_expired(group->group_source_list);
    1338             : 
    1339           0 :         assert(!group->group_filtermode_isexcl);
    1340             : 
    1341             :         /*
    1342             :           RFC 3376: 6.2.2. Definition of Group Timers
    1343             : 
    1344             :           If there are no more source records for the group, delete group
    1345             :           record.
    1346             :         */
    1347           0 :         if (listcount(group->group_source_list) < 1) {
    1348           0 :                 igmp_group_delete_empty_include(group);
    1349             :         }
    1350           0 : }
    1351             : 
    1352           0 : static void group_timer_off(struct gm_group *group)
    1353             : {
    1354           0 :         if (!group->t_group_timer)
    1355             :                 return;
    1356             : 
    1357           0 :         if (PIM_DEBUG_GM_TRACE) {
    1358           0 :                 char group_str[INET_ADDRSTRLEN];
    1359           0 :                 pim_inet4_dump("<group?>", group->group_addr, group_str,
    1360             :                                sizeof(group_str));
    1361           0 :                 zlog_debug("Cancelling TIMER event for group %s on %s",
    1362             :                            group_str, group->interface->name);
    1363             :         }
    1364           0 :         THREAD_OFF(group->t_group_timer);
    1365             : }
    1366             : 
    1367           0 : void igmp_group_timer_on(struct gm_group *group, long interval_msec,
    1368             :                          const char *ifname)
    1369             : {
    1370           0 :         group_timer_off(group);
    1371             : 
    1372           0 :         if (PIM_DEBUG_GM_EVENTS) {
    1373           0 :                 char group_str[INET_ADDRSTRLEN];
    1374           0 :                 pim_inet4_dump("<group?>", group->group_addr, group_str,
    1375             :                                sizeof(group_str));
    1376           0 :                 zlog_debug(
    1377             :                         "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
    1378             :                         interval_msec / 1000, interval_msec % 1000, group_str,
    1379             :                         ifname);
    1380             :         }
    1381             : 
    1382             :         /*
    1383             :           RFC 3376: 6.2.2. Definition of Group Timers
    1384             : 
    1385             :           The group timer is only used when a group is in EXCLUDE mode and
    1386             :           it represents the time for the *filter-mode* of the group to
    1387             :           expire and switch to INCLUDE mode.
    1388             :         */
    1389           0 :         assert(group->group_filtermode_isexcl);
    1390             : 
    1391           0 :         thread_add_timer_msec(router->master, igmp_group_timer, group,
    1392             :                               interval_msec, &group->t_group_timer);
    1393           0 : }
    1394             : 
    1395           0 : struct gm_group *find_group_by_addr(struct gm_sock *igmp,
    1396             :                                     struct in_addr group_addr)
    1397             : {
    1398           0 :         struct gm_group lookup;
    1399           0 :         struct pim_interface *pim_ifp = igmp->interface->info;
    1400             : 
    1401           0 :         lookup.group_addr.s_addr = group_addr.s_addr;
    1402             : 
    1403           0 :         return hash_lookup(pim_ifp->gm_group_hash, &lookup);
    1404             : }
    1405             : 
    1406           0 : struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
    1407             :                                         struct in_addr group_addr)
    1408             : {
    1409           0 :         struct gm_group *group;
    1410           0 :         struct pim_interface *pim_ifp = igmp->interface->info;
    1411             : 
    1412           0 :         group = find_group_by_addr(igmp, group_addr);
    1413           0 :         if (group) {
    1414             :                 return group;
    1415             :         }
    1416             : 
    1417           0 :         if (!pim_is_group_224_4(group_addr)) {
    1418           0 :                 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
    1419             :                           __func__);
    1420           0 :                 return NULL;
    1421             :         }
    1422             : 
    1423           0 :         if (pim_is_group_224_0_0_0_24(group_addr)) {
    1424           0 :                 if (PIM_DEBUG_GM_TRACE)
    1425           0 :                         zlog_debug(
    1426             :                                 "%s: Group specified %pI4 is part of 224.0.0.0/24",
    1427             :                                 __func__, &group_addr);
    1428           0 :                 return NULL;
    1429             :         }
    1430             :         /*
    1431             :           Non-existant group is created as INCLUDE {empty}:
    1432             : 
    1433             :           RFC 3376 - 5.1. Action on Change of Interface State
    1434             : 
    1435             :           If no interface state existed for that multicast address before
    1436             :           the change (i.e., the change consisted of creating a new
    1437             :           per-interface record), or if no state exists after the change
    1438             :           (i.e., the change consisted of deleting a per-interface record),
    1439             :           then the "non-existent" state is considered to have a filter mode
    1440             :           of INCLUDE and an empty source list.
    1441             :         */
    1442             : 
    1443           0 :         group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
    1444             : 
    1445           0 :         group->group_source_list = list_new();
    1446           0 :         group->group_source_list->del = (void (*)(void *))igmp_source_free;
    1447             : 
    1448           0 :         group->t_group_timer = NULL;
    1449           0 :         group->t_group_query_retransmit_timer = NULL;
    1450           0 :         group->group_specific_query_retransmit_count = 0;
    1451           0 :         group->group_addr = group_addr;
    1452           0 :         group->interface = igmp->interface;
    1453           0 :         group->last_igmp_v1_report_dsec = -1;
    1454           0 :         group->last_igmp_v2_report_dsec = -1;
    1455           0 :         group->group_creation = pim_time_monotonic_sec();
    1456           0 :         group->igmp_version = IGMP_DEFAULT_VERSION;
    1457             : 
    1458             :         /* initialize new group as INCLUDE {empty} */
    1459           0 :         group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
    1460             : 
    1461           0 :         listnode_add(pim_ifp->gm_group_list, group);
    1462           0 :         group = hash_get(pim_ifp->gm_group_hash, group, hash_alloc_intern);
    1463             : 
    1464           0 :         if (PIM_DEBUG_GM_TRACE) {
    1465           0 :                 char group_str[INET_ADDRSTRLEN];
    1466           0 :                 pim_inet4_dump("<group?>", group->group_addr, group_str,
    1467             :                                sizeof(group_str));
    1468           0 :                 zlog_debug(
    1469             :                         "Creating new IGMP group %s on socket %d interface %s",
    1470             :                         group_str, igmp->fd, igmp->interface->name);
    1471             :         }
    1472             : 
    1473           0 :         igmp_group_count_incr(pim_ifp);
    1474             : 
    1475             :         /*
    1476             :           RFC 3376: 6.2.2. Definition of Group Timers
    1477             : 
    1478             :           The group timer is only used when a group is in EXCLUDE mode and
    1479             :           it represents the time for the *filter-mode* of the group to
    1480             :           expire and switch to INCLUDE mode.
    1481             :         */
    1482           0 :         assert(!group->group_filtermode_isexcl); /* INCLUDE mode */
    1483           0 :         assert(!group->t_group_timer);                /* group timer == 0 */
    1484             : 
    1485             :         /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
    1486           0 :         igmp_anysource_forward_stop(group);
    1487             : 
    1488           0 :         return group;
    1489             : }
    1490             : 
    1491           0 : void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
    1492             :                      int query_buf_size, int num_sources,
    1493             :                      struct in_addr dst_addr, struct in_addr group_addr,
    1494             :                      int query_max_response_time_dsec, uint8_t s_flag,
    1495             :                      struct gm_sock *igmp)
    1496             : {
    1497           0 :         if (pim_addr_is_any(group_addr) &&
    1498           0 :             ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
    1499           0 :                 igmp->igmp_stats.general_queries_sent++;
    1500           0 :         else if (group)
    1501           0 :                 igmp->igmp_stats.group_queries_sent++;
    1502             : 
    1503           0 :         if (igmp_version == 3) {
    1504           0 :                 igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
    1505             :                                    query_buf, query_buf_size, num_sources,
    1506             :                                    dst_addr, group_addr,
    1507             :                                    query_max_response_time_dsec, s_flag,
    1508           0 :                                    igmp->querier_robustness_variable,
    1509           0 :                                    igmp->querier_query_interval);
    1510           0 :         } else if (igmp_version == 2) {
    1511           0 :                 igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
    1512             :                                    query_buf, dst_addr, group_addr,
    1513             :                                    query_max_response_time_dsec);
    1514             :         }
    1515           0 : }
    1516             : 
    1517           0 : void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
    1518             : {
    1519           0 :         struct pim_interface *pim_ifp = ifp->info;
    1520           0 :         struct listnode *sock_node = NULL;
    1521           0 :         struct gm_sock *igmp = NULL;
    1522           0 :         struct in_addr dst_addr;
    1523           0 :         struct in_addr group_addr;
    1524           0 :         int query_buf_size;
    1525             : 
    1526           0 :         if (!igmp_ver)
    1527             :                 igmp_ver = 2;
    1528             : 
    1529           0 :         if (igmp_ver == 3)
    1530             :                 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
    1531             :         else
    1532             :                 query_buf_size = IGMP_V12_MSG_SIZE;
    1533             : 
    1534           0 :         dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
    1535           0 :         group_addr.s_addr = PIM_NET_INADDR_ANY;
    1536             : 
    1537           0 :         if (PIM_DEBUG_GM_TRACE)
    1538           0 :                 zlog_debug("Issuing general query on request on %s", ifp->name);
    1539             : 
    1540           0 :         for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
    1541             : 
    1542           0 :                 char query_buf[query_buf_size];
    1543             : 
    1544           0 :                 igmp_send_query(
    1545             :                         igmp_ver, 0 /* igmp_group */, query_buf,
    1546             :                         sizeof(query_buf), 0 /* num_sources */, dst_addr,
    1547             :                         group_addr, pim_ifp->gm_query_max_response_time_dsec,
    1548             :                         1 /* s_flag: always set for general queries */, igmp);
    1549             :         }
    1550           0 : }

Generated by: LCOV version v1.16-topotato