The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/igmp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: igmp.c,v 1.82 2022/09/08 10:22:06 kn Exp $    */
    2 /*      $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $       */
    3 
    4 /*
    5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the project nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 
   33 /*
   34  * Copyright (c) 1988 Stephen Deering.
   35  * Copyright (c) 1992, 1993
   36  *      The Regents of the University of California.  All rights reserved.
   37  *
   38  * This code is derived from software contributed to Berkeley by
   39  * Stephen Deering of Stanford University.
   40  *
   41  * Redistribution and use in source and binary forms, with or without
   42  * modification, are permitted provided that the following conditions
   43  * are met:
   44  * 1. Redistributions of source code must retain the above copyright
   45  *    notice, this list of conditions and the following disclaimer.
   46  * 2. Redistributions in binary form must reproduce the above copyright
   47  *    notice, this list of conditions and the following disclaimer in the
   48  *    documentation and/or other materials provided with the distribution.
   49  * 3. Neither the name of the University nor the names of its contributors
   50  *    may be used to endorse or promote products derived from this software
   51  *    without specific prior written permission.
   52  *
   53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   63  * SUCH DAMAGE.
   64  *
   65  *      @(#)igmp.c      8.2 (Berkeley) 5/3/95
   66  */
   67 
   68 /*
   69  * Internet Group Management Protocol (IGMP) routines.
   70  *
   71  * Written by Steve Deering, Stanford, May 1988.
   72  * Modified by Rosen Sharma, Stanford, Aug 1994.
   73  * Modified by Bill Fenner, Xerox PARC, Feb 1995.
   74  *
   75  * MULTICAST Revision: 1.3
   76  */
   77 
   78 #include <sys/param.h>
   79 #include <sys/mbuf.h>
   80 #include <sys/systm.h>
   81 #include <sys/socket.h>
   82 #include <sys/protosw.h>
   83 #include <sys/sysctl.h>
   84 
   85 #include <net/if.h>
   86 #include <net/if_var.h>
   87 
   88 #include <netinet/in.h>
   89 #include <netinet/in_var.h>
   90 #include <netinet/ip.h>
   91 #include <netinet/ip_var.h>
   92 #include <netinet/igmp.h>
   93 #include <netinet/igmp_var.h>
   94 
   95 #include <sys/stdarg.h>
   96 
   97 #define IP_MULTICASTOPTS        0
   98 
   99 int     igmp_timers_are_running;        /* [N] shortcut for fast timer */
  100 static LIST_HEAD(, router_info) rti_head;
  101 static struct mbuf *router_alert;
  102 struct cpumem *igmpcounters;
  103 
  104 void igmp_checktimer(struct ifnet *);
  105 void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t);
  106 int rti_fill(struct in_multi *);
  107 struct router_info * rti_find(struct ifnet *);
  108 int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
  109 int igmp_sysctl_igmpstat(void *, size_t *, void *);
  110 
  111 void
  112 igmp_init(void)
  113 {
  114         struct ipoption *ra;
  115 
  116         igmp_timers_are_running = 0;
  117         LIST_INIT(&rti_head);
  118 
  119         igmpcounters = counters_alloc(igps_ncounters);
  120         router_alert = m_get(M_WAIT, MT_DATA);
  121 
  122         /*
  123          * Construct a Router Alert option (RAO) to use in report
  124          * messages as required by RFC2236.  This option has the
  125          * following format:
  126          *
  127          *      | 10010100 | 00000100 |  2 octet value  |
  128          *
  129          * where a value of "" indicates that routers shall examine
  130          * the packet.
  131          */
  132         ra = mtod(router_alert, struct ipoption *);
  133         ra->ipopt_dst.s_addr = INADDR_ANY;
  134         ra->ipopt_list[0] = IPOPT_RA;
  135         ra->ipopt_list[1] = 0x04;
  136         ra->ipopt_list[2] = 0x00;
  137         ra->ipopt_list[3] = 0x00;
  138         router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
  139 }
  140 
  141 int
  142 rti_fill(struct in_multi *inm)
  143 {
  144         struct router_info *rti;
  145 
  146         LIST_FOREACH(rti, &rti_head, rti_list) {
  147                 if (rti->rti_ifidx == inm->inm_ifidx) {
  148                         inm->inm_rti = rti;
  149                         if (rti->rti_type == IGMP_v1_ROUTER)
  150                                 return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
  151                         else
  152                                 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
  153                 }
  154         }
  155 
  156         rti = malloc(sizeof(*rti), M_MRTABLE, M_WAITOK);
  157         rti->rti_ifidx = inm->inm_ifidx;
  158         rti->rti_type = IGMP_v2_ROUTER;
  159         LIST_INSERT_HEAD(&rti_head, rti, rti_list);
  160         inm->inm_rti = rti;
  161         return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
  162 }
  163 
  164 struct router_info *
  165 rti_find(struct ifnet *ifp)
  166 {
  167         struct router_info *rti;
  168 
  169         KERNEL_ASSERT_LOCKED();
  170         LIST_FOREACH(rti, &rti_head, rti_list) {
  171                 if (rti->rti_ifidx == ifp->if_index)
  172                         return (rti);
  173         }
  174 
  175         rti = malloc(sizeof(*rti), M_MRTABLE, M_NOWAIT);
  176         if (rti == NULL)
  177                 return (NULL);
  178         rti->rti_ifidx = ifp->if_index;
  179         rti->rti_type = IGMP_v2_ROUTER;
  180         LIST_INSERT_HEAD(&rti_head, rti, rti_list);
  181         return (rti);
  182 }
  183 
  184 void
  185 rti_delete(struct ifnet *ifp)
  186 {
  187         struct router_info *rti, *trti;
  188 
  189         LIST_FOREACH_SAFE(rti, &rti_head, rti_list, trti) {
  190                 if (rti->rti_ifidx == ifp->if_index) {
  191                         LIST_REMOVE(rti, rti_list);
  192                         free(rti, M_MRTABLE, sizeof(*rti));
  193                         break;
  194                 }
  195         }
  196 }
  197 
  198 int
  199 igmp_input(struct mbuf **mp, int *offp, int proto, int af)
  200 {
  201         struct ifnet *ifp;
  202 
  203         igmpstat_inc(igps_rcv_total);
  204 
  205         ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
  206         if (ifp == NULL) {
  207                 m_freemp(mp);
  208                 return IPPROTO_DONE;
  209         }
  210 
  211         KERNEL_LOCK();
  212         proto = igmp_input_if(ifp, mp, offp, proto, af);
  213         KERNEL_UNLOCK();
  214         if_put(ifp);
  215         return proto;
  216 }
  217 
  218 int
  219 igmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
  220 {
  221         struct mbuf *m = *mp;
  222         int iphlen = *offp;
  223         struct ip *ip = mtod(m, struct ip *);
  224         struct igmp *igmp;
  225         int igmplen;
  226         int minlen;
  227         struct ifmaddr *ifma;
  228         struct in_multi *inm;
  229         struct router_info *rti;
  230         struct in_ifaddr *ia;
  231         int timer;
  232 
  233         igmplen = ntohs(ip->ip_len) - iphlen;
  234 
  235         /*
  236          * Validate lengths
  237          */
  238         if (igmplen < IGMP_MINLEN) {
  239                 igmpstat_inc(igps_rcv_tooshort);
  240                 m_freem(m);
  241                 return IPPROTO_DONE;
  242         }
  243         minlen = iphlen + IGMP_MINLEN;
  244         if ((m->m_flags & M_EXT || m->m_len < minlen) &&
  245             (m = *mp = m_pullup(m, minlen)) == NULL) {
  246                 igmpstat_inc(igps_rcv_tooshort);
  247                 return IPPROTO_DONE;
  248         }
  249 
  250         /*
  251          * Validate checksum
  252          */
  253         m->m_data += iphlen;
  254         m->m_len -= iphlen;
  255         igmp = mtod(m, struct igmp *);
  256         if (in_cksum(m, igmplen)) {
  257                 igmpstat_inc(igps_rcv_badsum);
  258                 m_freem(m);
  259                 return IPPROTO_DONE;
  260         }
  261         m->m_data -= iphlen;
  262         m->m_len += iphlen;
  263         ip = mtod(m, struct ip *);
  264 
  265         switch (igmp->igmp_type) {
  266 
  267         case IGMP_HOST_MEMBERSHIP_QUERY:
  268                 igmpstat_inc(igps_rcv_queries);
  269 
  270                 if (ifp->if_flags & IFF_LOOPBACK)
  271                         break;
  272 
  273                 if (igmp->igmp_code == 0) {
  274                         rti = rti_find(ifp);
  275                         if (rti == NULL) {
  276                                 m_freem(m);
  277                                 return IPPROTO_DONE;
  278                         }
  279                         rti->rti_type = IGMP_v1_ROUTER;
  280                         rti->rti_age = 0;
  281 
  282                         if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
  283                                 igmpstat_inc(igps_rcv_badqueries);
  284                                 m_freem(m);
  285                                 return IPPROTO_DONE;
  286                         }
  287 
  288                         /*
  289                          * Start the timers in all of our membership records
  290                          * for the interface on which the query arrived,
  291                          * except those that are already running and those
  292                          * that belong to a "local" group (224.0.0.X).
  293                          */
  294                         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
  295                                 if (ifma->ifma_addr->sa_family != AF_INET)
  296                                         continue;
  297                                 inm = ifmatoinm(ifma);
  298                                 if (inm->inm_timer == 0 &&
  299                                     !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
  300                                         inm->inm_state = IGMP_DELAYING_MEMBER;
  301                                         inm->inm_timer = IGMP_RANDOM_DELAY(
  302                                             IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
  303                                         igmp_timers_are_running = 1;
  304                                 }
  305                         }
  306                 } else {
  307                         if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
  308                                 igmpstat_inc(igps_rcv_badqueries);
  309                                 m_freem(m);
  310                                 return IPPROTO_DONE;
  311                         }
  312 
  313                         timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
  314                         if (timer == 0)
  315                                 timer = 1;
  316 
  317                         /*
  318                          * Start the timers in all of our membership records
  319                          * for the interface on which the query arrived,
  320                          * except those that are already running and those
  321                          * that belong to a "local" group (224.0.0.X).  For
  322                          * timers already running, check if they need to be
  323                          * reset.
  324                          */
  325                         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
  326                                 if (ifma->ifma_addr->sa_family != AF_INET)
  327                                         continue;
  328                                 inm = ifmatoinm(ifma);
  329                                 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  330                                     (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
  331                                      ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
  332                                         switch (inm->inm_state) {
  333                                         case IGMP_DELAYING_MEMBER:
  334                                                 if (inm->inm_timer <= timer)
  335                                                         break;
  336                                                 /* FALLTHROUGH */
  337                                         case IGMP_IDLE_MEMBER:
  338                                         case IGMP_LAZY_MEMBER:
  339                                         case IGMP_AWAKENING_MEMBER:
  340                                                 inm->inm_state =
  341                                                     IGMP_DELAYING_MEMBER;
  342                                                 inm->inm_timer =
  343                                                     IGMP_RANDOM_DELAY(timer);
  344                                                 igmp_timers_are_running = 1;
  345                                                 break;
  346                                         case IGMP_SLEEPING_MEMBER:
  347                                                 inm->inm_state =
  348                                                     IGMP_AWAKENING_MEMBER;
  349                                                 break;
  350                                         }
  351                                 }
  352                         }
  353                 }
  354 
  355                 break;
  356 
  357         case IGMP_v1_HOST_MEMBERSHIP_REPORT:
  358                 igmpstat_inc(igps_rcv_reports);
  359 
  360                 if (ifp->if_flags & IFF_LOOPBACK)
  361                         break;
  362 
  363                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
  364                     igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
  365                         igmpstat_inc(igps_rcv_badreports);
  366                         m_freem(m);
  367                         return IPPROTO_DONE;
  368                 }
  369 
  370                 /*
  371                  * KLUDGE: if the IP source address of the report has an
  372                  * unspecified (i.e., zero) subnet number, as is allowed for
  373                  * a booting host, replace it with the correct subnet number
  374                  * so that a process-level multicast routing daemon can
  375                  * determine which subnet it arrived from.  This is necessary
  376                  * to compensate for the lack of any way for a process to
  377                  * determine the arrival interface of an incoming packet.
  378                  */
  379                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
  380                         IFP_TO_IA(ifp, ia);
  381                         if (ia)
  382                                 ip->ip_src.s_addr = ia->ia_net;
  383                 }
  384 
  385                 /*
  386                  * If we belong to the group being reported, stop
  387                  * our timer for that group.
  388                  */
  389                 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
  390                 if (inm != NULL) {
  391                         inm->inm_timer = 0;
  392                         igmpstat_inc(igps_rcv_ourreports);
  393 
  394                         switch (inm->inm_state) {
  395                         case IGMP_IDLE_MEMBER:
  396                         case IGMP_LAZY_MEMBER:
  397                         case IGMP_AWAKENING_MEMBER:
  398                         case IGMP_SLEEPING_MEMBER:
  399                                 inm->inm_state = IGMP_SLEEPING_MEMBER;
  400                                 break;
  401                         case IGMP_DELAYING_MEMBER:
  402                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
  403                                         inm->inm_state = IGMP_LAZY_MEMBER;
  404                                 else
  405                                         inm->inm_state = IGMP_SLEEPING_MEMBER;
  406                                 break;
  407                         }
  408                 }
  409 
  410                 break;
  411 
  412         case IGMP_v2_HOST_MEMBERSHIP_REPORT:
  413 #ifdef MROUTING
  414                 /*
  415                  * Make sure we don't hear our own membership report.  Fast
  416                  * leave requires knowing that we are the only member of a
  417                  * group.
  418                  */
  419                 IFP_TO_IA(ifp, ia);
  420                 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
  421                         break;
  422 #endif
  423 
  424                 igmpstat_inc(igps_rcv_reports);
  425 
  426                 if (ifp->if_flags & IFF_LOOPBACK)
  427                         break;
  428 
  429                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
  430                     igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
  431                         igmpstat_inc(igps_rcv_badreports);
  432                         m_freem(m);
  433                         return IPPROTO_DONE;
  434                 }
  435 
  436                 /*
  437                  * KLUDGE: if the IP source address of the report has an
  438                  * unspecified (i.e., zero) subnet number, as is allowed for
  439                  * a booting host, replace it with the correct subnet number
  440                  * so that a process-level multicast routing daemon can
  441                  * determine which subnet it arrived from.  This is necessary
  442                  * to compensate for the lack of any way for a process to
  443                  * determine the arrival interface of an incoming packet.
  444                  */
  445                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
  446 #ifndef MROUTING
  447                         IFP_TO_IA(ifp, ia);
  448 #endif
  449                         if (ia)
  450                                 ip->ip_src.s_addr = ia->ia_net;
  451                 }
  452 
  453                 /*
  454                  * If we belong to the group being reported, stop
  455                  * our timer for that group.
  456                  */
  457                 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
  458                 if (inm != NULL) {
  459                         inm->inm_timer = 0;
  460                         igmpstat_inc(igps_rcv_ourreports);
  461 
  462                         switch (inm->inm_state) {
  463                         case IGMP_DELAYING_MEMBER:
  464                         case IGMP_IDLE_MEMBER:
  465                         case IGMP_AWAKENING_MEMBER:
  466                                 inm->inm_state = IGMP_LAZY_MEMBER;
  467                                 break;
  468                         case IGMP_LAZY_MEMBER:
  469                         case IGMP_SLEEPING_MEMBER:
  470                                 break;
  471                         }
  472                 }
  473 
  474                 break;
  475 
  476         }
  477 
  478         /*
  479          * Pass all valid IGMP packets up to any process(es) listening
  480          * on a raw IGMP socket.
  481          */
  482         return rip_input(mp, offp, proto, af);
  483 }
  484 
  485 void
  486 igmp_joingroup(struct in_multi *inm, struct ifnet *ifp)
  487 {
  488         int i;
  489 
  490         inm->inm_state = IGMP_IDLE_MEMBER;
  491 
  492         if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  493             (ifp->if_flags & IFF_LOOPBACK) == 0) {
  494                 i = rti_fill(inm);
  495                 igmp_sendpkt(ifp, inm, i, 0);
  496                 inm->inm_state = IGMP_DELAYING_MEMBER;
  497                 inm->inm_timer = IGMP_RANDOM_DELAY(
  498                     IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
  499                 igmp_timers_are_running = 1;
  500         } else
  501                 inm->inm_timer = 0;
  502 }
  503 
  504 void
  505 igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp)
  506 {
  507         switch (inm->inm_state) {
  508         case IGMP_DELAYING_MEMBER:
  509         case IGMP_IDLE_MEMBER:
  510                 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  511                     (ifp->if_flags & IFF_LOOPBACK) == 0)
  512                         if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
  513                                 igmp_sendpkt(ifp, inm,
  514                                     IGMP_HOST_LEAVE_MESSAGE,
  515                                     INADDR_ALLROUTERS_GROUP);
  516                 break;
  517         case IGMP_LAZY_MEMBER:
  518         case IGMP_AWAKENING_MEMBER:
  519         case IGMP_SLEEPING_MEMBER:
  520                 break;
  521         }
  522 }
  523 
  524 void
  525 igmp_fasttimo(void)
  526 {
  527         struct ifnet *ifp;
  528 
  529         /*
  530          * Quick check to see if any work needs to be done, in order
  531          * to minimize the overhead of fasttimo processing.
  532          * Variable igmp_timers_are_running is read atomically, but without
  533          * lock intentionally.  In case it is not set due to MP races, we may
  534          * miss to check the timers.  Then run the loop at next fast timeout.
  535          */
  536         if (!igmp_timers_are_running)
  537                 return;
  538 
  539         NET_LOCK();
  540 
  541         igmp_timers_are_running = 0;
  542         TAILQ_FOREACH(ifp, &ifnetlist, if_list)
  543                 igmp_checktimer(ifp);
  544 
  545         NET_UNLOCK();
  546 }
  547 
  548 void
  549 igmp_checktimer(struct ifnet *ifp)
  550 {
  551         struct in_multi *inm;
  552         struct ifmaddr *ifma;
  553 
  554         NET_ASSERT_LOCKED();
  555 
  556         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
  557                 if (ifma->ifma_addr->sa_family != AF_INET)
  558                         continue;
  559                 inm = ifmatoinm(ifma);
  560                 if (inm->inm_timer == 0) {
  561                         /* do nothing */
  562                 } else if (--inm->inm_timer == 0) {
  563                         if (inm->inm_state == IGMP_DELAYING_MEMBER) {
  564                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
  565                                         igmp_sendpkt(ifp, inm,
  566                                             IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
  567                                 else
  568                                         igmp_sendpkt(ifp, inm,
  569                                             IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
  570                                 inm->inm_state = IGMP_IDLE_MEMBER;
  571                         }
  572                 } else {
  573                         igmp_timers_are_running = 1;
  574                 }
  575         }
  576 }
  577 
  578 void
  579 igmp_slowtimo(void)
  580 {
  581         struct router_info *rti;
  582 
  583         NET_LOCK();
  584 
  585         LIST_FOREACH(rti, &rti_head, rti_list) {
  586                 if (rti->rti_type == IGMP_v1_ROUTER &&
  587                     ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
  588                         rti->rti_type = IGMP_v2_ROUTER;
  589                 }
  590         }
  591 
  592         NET_UNLOCK();
  593 }
  594 
  595 void
  596 igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type,
  597     in_addr_t addr)
  598 {
  599         struct mbuf *m;
  600         struct igmp *igmp;
  601         struct ip *ip;
  602         struct ip_moptions imo;
  603 
  604         MGETHDR(m, M_DONTWAIT, MT_HEADER);
  605         if (m == NULL)
  606                 return;
  607 
  608         /*
  609          * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
  610          * is smaller than mbuf size returned by MGETHDR.
  611          */
  612         m->m_data += max_linkhdr;
  613         m->m_len = sizeof(struct ip) + IGMP_MINLEN;
  614         m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
  615 
  616         ip = mtod(m, struct ip *);
  617         ip->ip_tos = 0;
  618         ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
  619         ip->ip_off = 0;
  620         ip->ip_p = IPPROTO_IGMP;
  621         ip->ip_src.s_addr = INADDR_ANY;
  622         if (addr) {
  623                 ip->ip_dst.s_addr = addr;
  624         } else {
  625                 ip->ip_dst = inm->inm_addr;
  626         }
  627 
  628         m->m_data += sizeof(struct ip);
  629         m->m_len -= sizeof(struct ip);
  630         igmp = mtod(m, struct igmp *);
  631         igmp->igmp_type = type;
  632         igmp->igmp_code = 0;
  633         igmp->igmp_group = inm->inm_addr;
  634         igmp->igmp_cksum = 0;
  635         igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
  636         m->m_data -= sizeof(struct ip);
  637         m->m_len += sizeof(struct ip);
  638 
  639         m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
  640         imo.imo_ifidx = inm->inm_ifidx;
  641         imo.imo_ttl = 1;
  642 
  643         /*
  644          * Request loopback of the report if we are acting as a multicast
  645          * router, so that the process-level routing daemon can hear it.
  646          */
  647 #ifdef MROUTING
  648         imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL);
  649 #else
  650         imo.imo_loop = 0;
  651 #endif /* MROUTING */
  652 
  653         ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
  654 
  655         igmpstat_inc(igps_snd_reports);
  656 }
  657 
  658 /*
  659  * Sysctl for igmp variables.
  660  */
  661 int
  662 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
  663     void *newp, size_t newlen)
  664 {
  665         /* All sysctl names at this level are terminal. */
  666         if (namelen != 1)
  667                 return (ENOTDIR);
  668 
  669         switch (name[0]) {
  670         case IGMPCTL_STATS:
  671                 if (newp != NULL)
  672                         return (EPERM);
  673                 return (igmp_sysctl_igmpstat(oldp, oldlenp, newp));
  674         default:
  675                 return (EOPNOTSUPP);
  676         }
  677         /* NOTREACHED */
  678 }
  679 
  680 int
  681 igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp)
  682 {
  683         uint64_t counters[igps_ncounters];
  684         struct igmpstat igmpstat;
  685         u_long *words = (u_long *)&igmpstat;
  686         int i;
  687 
  688         CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long)));
  689         memset(&igmpstat, 0, sizeof igmpstat);
  690         counters_read(igmpcounters, counters, nitems(counters));
  691 
  692         for (i = 0; i < nitems(counters); i++)
  693                 words[i] = (u_long)counters[i];
  694 
  695         return (sysctl_rdstruct(oldp, oldlenp, newp,
  696             &igmpstat, sizeof(igmpstat)));
  697 }

Cache object: f266744d79acbb6858fc20574d1d4e09


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.