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 /*      $NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv Exp $   */
    2 
    3 /*
    4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. Neither the name of the project nor the names of its contributors
   16  *    may be used to endorse or promote products derived from this software
   17  *    without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 /*
   33  * Internet Group Management Protocol (IGMP) routines.
   34  *
   35  * Written by Steve Deering, Stanford, May 1988.
   36  * Modified by Rosen Sharma, Stanford, Aug 1994.
   37  * Modified by Bill Fenner, Xerox PARC, Feb 1995.
   38  *
   39  * MULTICAST Revision: 1.3
   40  */
   41 
   42 #include <sys/cdefs.h>
   43 __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv Exp $");
   44 
   45 #ifdef _KERNEL_OPT
   46 #include "opt_mrouting.h"
   47 #include "opt_net_mpsafe.h"
   48 #endif
   49 
   50 #include <sys/param.h>
   51 #include <sys/mbuf.h>
   52 #include <sys/socket.h>
   53 #include <sys/socketvar.h>
   54 #include <sys/systm.h>
   55 #include <sys/cprng.h>
   56 #include <sys/sysctl.h>
   57 
   58 #include <net/if.h>
   59 #include <net/net_stats.h>
   60 
   61 #include <netinet/in.h>
   62 #include <netinet/in_var.h>
   63 #include <netinet/in_systm.h>
   64 #include <netinet/ip.h>
   65 #include <netinet/ip_var.h>
   66 #include <netinet/igmp.h>
   67 #include <netinet/igmp_var.h>
   68 
   69 /*
   70  * Per-interface router version information.
   71  */
   72 typedef struct router_info {
   73         LIST_ENTRY(router_info) rti_link;
   74         ifnet_t *       rti_ifp;
   75         int             rti_type;       /* type of router on this interface */
   76         int             rti_age;        /* time since last v1 query */
   77 } router_info_t;
   78 
   79 /*
   80  * The router-info list and the timer flag are protected by in_multilock.
   81  *
   82  * Lock order:
   83  *
   84  *      softnet_lock ->
   85  *              in_multilock
   86  */
   87 static struct pool      igmp_rti_pool           __cacheline_aligned;
   88 static LIST_HEAD(, router_info) rti_head        __cacheline_aligned;
   89 static int              igmp_timers_on          __cacheline_aligned;
   90 static percpu_t *       igmpstat_percpu         __read_mostly;
   91 
   92 #define IGMP_STATINC(x)         _NET_STATINC(igmpstat_percpu, x)
   93 
   94 static void             igmp_sendpkt(struct in_multi *, int);
   95 static int              rti_fill(struct in_multi *);
   96 static router_info_t *  rti_find(struct ifnet *);
   97 static void             rti_delete(struct ifnet *);
   98 static void             sysctl_net_inet_igmp_setup(struct sysctllog **);
   99 
  100 /*
  101  * rti_fill: associate router information with the given multicast group;
  102  * if there is no router information for the interface, then create it.
  103  */
  104 static int
  105 rti_fill(struct in_multi *inm)
  106 {
  107         router_info_t *rti;
  108 
  109         KASSERT(in_multi_lock_held());
  110 
  111         LIST_FOREACH(rti, &rti_head, rti_link) {
  112                 if (rti->rti_ifp == inm->inm_ifp) {
  113                         inm->inm_rti = rti;
  114                         return rti->rti_type == IGMP_v1_ROUTER ?
  115                             IGMP_v1_HOST_MEMBERSHIP_REPORT :
  116                             IGMP_v2_HOST_MEMBERSHIP_REPORT;
  117                 }
  118         }
  119         rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
  120         if (rti == NULL) {
  121                 return 0;
  122         }
  123         rti->rti_ifp = inm->inm_ifp;
  124         rti->rti_type = IGMP_v2_ROUTER;
  125         LIST_INSERT_HEAD(&rti_head, rti, rti_link);
  126         inm->inm_rti = rti;
  127         return IGMP_v2_HOST_MEMBERSHIP_REPORT;
  128 }
  129 
  130 /*
  131  * rti_find: lookup or create router information for the given interface.
  132  */
  133 static router_info_t *
  134 rti_find(ifnet_t *ifp)
  135 {
  136         router_info_t *rti;
  137 
  138         KASSERT(in_multi_lock_held());
  139 
  140         LIST_FOREACH(rti, &rti_head, rti_link) {
  141                 if (rti->rti_ifp == ifp)
  142                         return rti;
  143         }
  144         rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
  145         if (rti == NULL) {
  146                 return NULL;
  147         }
  148         rti->rti_ifp = ifp;
  149         rti->rti_type = IGMP_v2_ROUTER;
  150         LIST_INSERT_HEAD(&rti_head, rti, rti_link);
  151         return rti;
  152 }
  153 
  154 /*
  155  * rti_delete: remove and free the router information entry for the
  156  * given interface.
  157  */
  158 static void
  159 rti_delete(ifnet_t *ifp)
  160 {
  161         router_info_t *rti;
  162 
  163         KASSERT(in_multi_lock_held());
  164 
  165         LIST_FOREACH(rti, &rti_head, rti_link) {
  166                 if (rti->rti_ifp == ifp) {
  167                         LIST_REMOVE(rti, rti_link);
  168                         pool_put(&igmp_rti_pool, rti);
  169                         break;
  170                 }
  171         }
  172 }
  173 
  174 void
  175 igmp_init(void)
  176 {
  177         pool_init(&igmp_rti_pool, sizeof(router_info_t), 0, 0, 0,
  178             "igmppl", NULL, IPL_SOFTNET);
  179         igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS);
  180         sysctl_net_inet_igmp_setup(NULL);
  181         LIST_INIT(&rti_head);
  182 }
  183 
  184 void
  185 igmp_input(struct mbuf *m, int off, int proto)
  186 {
  187         ifnet_t *ifp;
  188         struct ip *ip = mtod(m, struct ip *);
  189         struct igmp *igmp;
  190         u_int minlen, timer;
  191         struct in_multi *inm;
  192         struct in_ifaddr *ia;
  193         int ip_len, iphlen;
  194         struct psref psref;
  195 
  196         iphlen = off;
  197 
  198         IGMP_STATINC(IGMP_STAT_RCV_TOTAL);
  199 
  200         /*
  201          * Validate lengths
  202          */
  203         minlen = iphlen + IGMP_MINLEN;
  204         ip_len = ntohs(ip->ip_len);
  205         if (ip_len < minlen) {
  206                 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
  207                 m_freem(m);
  208                 return;
  209         }
  210         if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
  211             || m->m_len < minlen) {
  212                 if ((m = m_pullup(m, minlen)) == NULL) {
  213                         IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
  214                         return;
  215                 }
  216                 ip = mtod(m, struct ip *);
  217         }
  218 
  219         /*
  220          * Validate checksum
  221          */
  222         m->m_data += iphlen;
  223         m->m_len -= iphlen;
  224         igmp = mtod(m, struct igmp *);
  225         /* No need to assert alignment here. */
  226         if (in_cksum(m, ip_len - iphlen)) {
  227                 IGMP_STATINC(IGMP_STAT_RCV_BADSUM);
  228                 m_freem(m);
  229                 return;
  230         }
  231         m->m_data -= iphlen;
  232         m->m_len += iphlen;
  233 
  234         ifp = m_get_rcvif_psref(m, &psref);
  235         if (__predict_false(ifp == NULL))
  236                 goto drop;
  237 
  238         switch (igmp->igmp_type) {
  239 
  240         case IGMP_HOST_MEMBERSHIP_QUERY:
  241                 IGMP_STATINC(IGMP_STAT_RCV_QUERIES);
  242 
  243                 if (ifp->if_flags & IFF_LOOPBACK)
  244                         break;
  245 
  246                 if (igmp->igmp_code == 0) {
  247                         struct in_multistep step;
  248                         router_info_t *rti;
  249 
  250                         if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
  251                                 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
  252                                 goto drop;
  253                         }
  254 
  255                         in_multi_lock(RW_WRITER);
  256                         rti = rti_find(ifp);
  257                         if (rti == NULL) {
  258                                 in_multi_unlock();
  259                                 break;
  260                         }
  261                         rti->rti_type = IGMP_v1_ROUTER;
  262                         rti->rti_age = 0;
  263 
  264                         /*
  265                          * Start the timers in all of our membership records
  266                          * for the interface on which the query arrived,
  267                          * except those that are already running and those
  268                          * that belong to a "local" group (224.0.0.X).
  269                          */
  270 
  271                         inm = in_first_multi(&step);
  272                         while (inm != NULL) {
  273                                 if (inm->inm_ifp == ifp &&
  274                                     inm->inm_timer == 0 &&
  275                                     !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
  276                                         inm->inm_state = IGMP_DELAYING_MEMBER;
  277                                         inm->inm_timer = IGMP_RANDOM_DELAY(
  278                                             IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
  279                                         igmp_timers_on = true;
  280                                 }
  281                                 inm = in_next_multi(&step);
  282                         }
  283                         in_multi_unlock();
  284                 } else {
  285                         struct in_multistep step;
  286 
  287                         if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
  288                                 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
  289                                 goto drop;
  290                         }
  291 
  292                         timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
  293                         if (timer == 0)
  294                                 timer = 1;
  295 
  296                         /*
  297                          * Start the timers in all of our membership records
  298                          * for the interface on which the query arrived,
  299                          * except those that are already running and those
  300                          * that belong to a "local" group (224.0.0.X).  For
  301                          * timers already running, check if they need to be
  302                          * reset.
  303                          */
  304                         in_multi_lock(RW_WRITER);
  305                         inm = in_first_multi(&step);
  306                         while (inm != NULL) {
  307                                 if (inm->inm_ifp == ifp &&
  308                                     !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  309                                     (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
  310                                      in_hosteq(ip->ip_dst, inm->inm_addr))) {
  311                                         switch (inm->inm_state) {
  312                                         case IGMP_DELAYING_MEMBER:
  313                                                 if (inm->inm_timer <= timer)
  314                                                         break;
  315                                                 /* FALLTHROUGH */
  316                                         case IGMP_IDLE_MEMBER:
  317                                         case IGMP_LAZY_MEMBER:
  318                                         case IGMP_AWAKENING_MEMBER:
  319                                                 inm->inm_state =
  320                                                     IGMP_DELAYING_MEMBER;
  321                                                 inm->inm_timer =
  322                                                     IGMP_RANDOM_DELAY(timer);
  323                                                 igmp_timers_on = true;
  324                                                 break;
  325                                         case IGMP_SLEEPING_MEMBER:
  326                                                 inm->inm_state =
  327                                                     IGMP_AWAKENING_MEMBER;
  328                                                 break;
  329                                         }
  330                                 }
  331                                 inm = in_next_multi(&step);
  332                         }
  333                         in_multi_unlock();
  334                 }
  335 
  336                 break;
  337 
  338         case IGMP_v1_HOST_MEMBERSHIP_REPORT:
  339                 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
  340 
  341                 if (ifp->if_flags & IFF_LOOPBACK)
  342                         break;
  343 
  344                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
  345                     !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
  346                         IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
  347                         goto drop;
  348                 }
  349 
  350                 /*
  351                  * KLUDGE: if the IP source address of the report has an
  352                  * unspecified (i.e., zero) subnet number, as is allowed for
  353                  * a booting host, replace it with the correct subnet number
  354                  * so that a process-level multicast routing daemon can
  355                  * determine which subnet it arrived from.  This is necessary
  356                  * to compensate for the lack of any way for a process to
  357                  * determine the arrival interface of an incoming packet.
  358                  */
  359                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
  360                         int s = pserialize_read_enter();
  361                         ia = in_get_ia_from_ifp(ifp); /* XXX */
  362                         if (ia)
  363                                 ip->ip_src.s_addr = ia->ia_subnet;
  364                         pserialize_read_exit(s);
  365                 }
  366 
  367                 /*
  368                  * If we belong to the group being reported, stop
  369                  * our timer for that group.
  370                  */
  371                 in_multi_lock(RW_WRITER);
  372                 inm = in_lookup_multi(igmp->igmp_group, ifp);
  373                 if (inm != NULL) {
  374                         inm->inm_timer = 0;
  375                         IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
  376 
  377                         switch (inm->inm_state) {
  378                         case IGMP_IDLE_MEMBER:
  379                         case IGMP_LAZY_MEMBER:
  380                         case IGMP_AWAKENING_MEMBER:
  381                         case IGMP_SLEEPING_MEMBER:
  382                                 inm->inm_state = IGMP_SLEEPING_MEMBER;
  383                                 break;
  384                         case IGMP_DELAYING_MEMBER:
  385                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
  386                                         inm->inm_state = IGMP_LAZY_MEMBER;
  387                                 else
  388                                         inm->inm_state = IGMP_SLEEPING_MEMBER;
  389                                 break;
  390                         }
  391                 }
  392                 in_multi_unlock();
  393                 break;
  394 
  395         case IGMP_v2_HOST_MEMBERSHIP_REPORT: {
  396                 int s = pserialize_read_enter();
  397 #ifdef MROUTING
  398                 /*
  399                  * Make sure we don't hear our own membership report.  Fast
  400                  * leave requires knowing that we are the only member of a
  401                  * group.
  402                  */
  403                 ia = in_get_ia_from_ifp(ifp);   /* XXX */
  404                 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) {
  405                         pserialize_read_exit(s);
  406                         break;
  407                 }
  408 #endif
  409 
  410                 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
  411 
  412                 if (ifp->if_flags & IFF_LOOPBACK) {
  413                         pserialize_read_exit(s);
  414                         break;
  415                 }
  416 
  417                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
  418                     !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
  419                         IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
  420                         pserialize_read_exit(s);
  421                         goto drop;
  422                 }
  423 
  424                 /*
  425                  * KLUDGE: if the IP source address of the report has an
  426                  * unspecified (i.e., zero) subnet number, as is allowed for
  427                  * a booting host, replace it with the correct subnet number
  428                  * so that a process-level multicast routing daemon can
  429                  * determine which subnet it arrived from.  This is necessary
  430                  * to compensate for the lack of any way for a process to
  431                  * determine the arrival interface of an incoming packet.
  432                  */
  433                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
  434 #ifndef MROUTING
  435                         ia = in_get_ia_from_ifp(ifp); /* XXX */
  436 #endif
  437                         if (ia)
  438                                 ip->ip_src.s_addr = ia->ia_subnet;
  439                 }
  440                 pserialize_read_exit(s);
  441 
  442                 /*
  443                  * If we belong to the group being reported, stop
  444                  * our timer for that group.
  445                  */
  446                 in_multi_lock(RW_WRITER);
  447                 inm = in_lookup_multi(igmp->igmp_group, ifp);
  448                 if (inm != NULL) {
  449                         inm->inm_timer = 0;
  450                         IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
  451 
  452                         switch (inm->inm_state) {
  453                         case IGMP_DELAYING_MEMBER:
  454                         case IGMP_IDLE_MEMBER:
  455                         case IGMP_AWAKENING_MEMBER:
  456                                 inm->inm_state = IGMP_LAZY_MEMBER;
  457                                 break;
  458                         case IGMP_LAZY_MEMBER:
  459                         case IGMP_SLEEPING_MEMBER:
  460                                 break;
  461                         }
  462                 }
  463                 in_multi_unlock();
  464                 break;
  465             }
  466         }
  467         m_put_rcvif_psref(ifp, &psref);
  468 
  469         /*
  470          * Pass all valid IGMP packets up to any process(es) listening
  471          * on a raw IGMP socket.
  472          */
  473         /*
  474          * Currently, igmp_input() is always called holding softnet_lock
  475          * by ipintr()(!NET_MPSAFE) or PR_INPUT_WRAP()(NET_MPSAFE).
  476          */
  477         KASSERT(mutex_owned(softnet_lock));
  478         rip_input(m, iphlen, proto);
  479         return;
  480 
  481 drop:
  482         m_put_rcvif_psref(ifp, &psref);
  483         m_freem(m);
  484         return;
  485 }
  486 
  487 int
  488 igmp_joingroup(struct in_multi *inm)
  489 {
  490         KASSERT(in_multi_lock_held());
  491         inm->inm_state = IGMP_IDLE_MEMBER;
  492 
  493         if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  494             (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
  495                 int report_type;
  496 
  497                 report_type = rti_fill(inm);
  498                 if (report_type == 0) {
  499                         return ENOMEM;
  500                 }
  501                 igmp_sendpkt(inm, report_type);
  502                 inm->inm_state = IGMP_DELAYING_MEMBER;
  503                 inm->inm_timer = IGMP_RANDOM_DELAY(
  504                     IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
  505                 igmp_timers_on = true;
  506         } else
  507                 inm->inm_timer = 0;
  508 
  509         return 0;
  510 }
  511 
  512 void
  513 igmp_leavegroup(struct in_multi *inm)
  514 {
  515         KASSERT(in_multi_lock_held());
  516 
  517         switch (inm->inm_state) {
  518         case IGMP_DELAYING_MEMBER:
  519         case IGMP_IDLE_MEMBER:
  520                 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
  521                     (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
  522                         if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
  523                                 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
  524                 break;
  525         case IGMP_LAZY_MEMBER:
  526         case IGMP_AWAKENING_MEMBER:
  527         case IGMP_SLEEPING_MEMBER:
  528                 break;
  529         }
  530 }
  531 
  532 void
  533 igmp_fasttimo(void)
  534 {
  535         struct in_multi *inm;
  536         struct in_multistep step;
  537 
  538         /*
  539          * Quick check to see if any work needs to be done, in order
  540          * to minimize the overhead of fasttimo processing.
  541          */
  542         if (!igmp_timers_on) {
  543                 return;
  544         }
  545 
  546         /* XXX: Needed for ip_output(). */
  547         SOFTNET_LOCK_UNLESS_NET_MPSAFE();
  548 
  549         in_multi_lock(RW_WRITER);
  550         igmp_timers_on = false;
  551         inm = in_first_multi(&step);
  552         while (inm != NULL) {
  553                 if (inm->inm_timer == 0) {
  554                         /* do nothing */
  555                 } else if (--inm->inm_timer == 0) {
  556                         if (inm->inm_state == IGMP_DELAYING_MEMBER) {
  557                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
  558                                         igmp_sendpkt(inm,
  559                                             IGMP_v1_HOST_MEMBERSHIP_REPORT);
  560                                 else
  561                                         igmp_sendpkt(inm,
  562                                             IGMP_v2_HOST_MEMBERSHIP_REPORT);
  563                                 inm->inm_state = IGMP_IDLE_MEMBER;
  564                         }
  565                 } else {
  566                         igmp_timers_on = true;
  567                 }
  568                 inm = in_next_multi(&step);
  569         }
  570         in_multi_unlock();
  571         SOFTNET_UNLOCK_UNLESS_NET_MPSAFE();
  572 }
  573 
  574 void
  575 igmp_slowtimo(void)
  576 {
  577         router_info_t *rti;
  578 
  579         in_multi_lock(RW_WRITER);
  580         LIST_FOREACH(rti, &rti_head, rti_link) {
  581                 if (rti->rti_type == IGMP_v1_ROUTER &&
  582                     ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
  583                         rti->rti_type = IGMP_v2_ROUTER;
  584                 }
  585         }
  586         in_multi_unlock();
  587 }
  588 
  589 /*
  590  * igmp_sendpkt: construct an IGMP packet, given the multicast structure
  591  * and the type, and send the datagram.
  592  */
  593 static void
  594 igmp_sendpkt(struct in_multi *inm, int type)
  595 {
  596         struct mbuf *m;
  597         struct igmp *igmp;
  598         struct ip *ip;
  599         struct ip_moptions imo;
  600 
  601         KASSERT(in_multi_lock_held());
  602 
  603         MGETHDR(m, M_DONTWAIT, MT_HEADER);
  604         if (m == NULL)
  605                 return;
  606         KASSERT(max_linkhdr + sizeof(struct ip) + IGMP_MINLEN <= MHLEN);
  607 
  608         m->m_data += max_linkhdr;
  609         m->m_len = sizeof(struct ip) + IGMP_MINLEN;
  610         m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
  611 
  612         ip = mtod(m, struct ip *);
  613         ip->ip_tos = 0;
  614         ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
  615         ip->ip_off = htons(0);
  616         ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
  617         ip->ip_p = IPPROTO_IGMP;
  618         ip->ip_src = zeroin_addr;
  619         ip->ip_dst = inm->inm_addr;
  620 
  621         m->m_data += sizeof(struct ip);
  622         m->m_len -= sizeof(struct ip);
  623         igmp = mtod(m, struct igmp *);
  624         igmp->igmp_type = type;
  625         igmp->igmp_code = 0;
  626         igmp->igmp_group = inm->inm_addr;
  627         igmp->igmp_cksum = 0;
  628         igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
  629         m->m_data -= sizeof(struct ip);
  630         m->m_len += sizeof(struct ip);
  631 
  632         imo.imo_multicast_if_index = if_get_index(inm->inm_ifp);
  633         imo.imo_multicast_ttl = 1;
  634 
  635         /*
  636          * Request loopback of the report if we are acting as a multicast
  637          * router, so that the process-level routing demon can hear it.
  638          */
  639 #ifdef MROUTING
  640         extern struct socket *ip_mrouter;
  641         imo.imo_multicast_loop = (ip_mrouter != NULL);
  642 #else
  643         imo.imo_multicast_loop = 0;
  644 #endif
  645 
  646         /*
  647          * Note: IP_IGMP_MCAST indicates that in_multilock is held.
  648          * The caller must still acquire softnet_lock for ip_output().
  649          */
  650 #ifndef NET_MPSAFE
  651         KASSERT(mutex_owned(softnet_lock));
  652 #endif
  653         ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL);
  654         IGMP_STATINC(IGMP_STAT_SND_REPORTS);
  655 }
  656 
  657 void
  658 igmp_purgeif(ifnet_t *ifp)
  659 {
  660         in_multi_lock(RW_WRITER);
  661         rti_delete(ifp);
  662         in_multi_unlock();
  663 }
  664 
  665 static int
  666 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
  667 {
  668         return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS);
  669 }
  670 
  671 static void
  672 sysctl_net_inet_igmp_setup(struct sysctllog **clog)
  673 {
  674         sysctl_createv(clog, 0, NULL, NULL,
  675                         CTLFLAG_PERMANENT,
  676                         CTLTYPE_NODE, "inet", NULL,
  677                         NULL, 0, NULL, 0,
  678                         CTL_NET, PF_INET, CTL_EOL);
  679         sysctl_createv(clog, 0, NULL, NULL,
  680                         CTLFLAG_PERMANENT,
  681                         CTLTYPE_NODE, "igmp",
  682                         SYSCTL_DESCR("Internet Group Management Protocol"),
  683                         NULL, 0, NULL, 0,
  684                         CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL);
  685         sysctl_createv(clog, 0, NULL, NULL,
  686                         CTLFLAG_PERMANENT,
  687                         CTLTYPE_STRUCT, "stats",
  688                         SYSCTL_DESCR("IGMP statistics"),
  689                         sysctl_net_inet_igmp_stats, 0, NULL, 0,
  690                         CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL);
  691 }

Cache object: a576fa027fe4b725a95c0907c718c5d6


[ 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.