LCOV - code coverage report
Current view: top level - netinet - igmp.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 289 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 14 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: igmp.c,v 1.72 2017/11/20 10:35:24 mpi 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 *igmpctl_vars[IGMPCTL_MAXID] = IGMPCTL_VARS;
     100             : 
     101             : int             igmp_timers_are_running;
     102             : static struct router_info *rti_head;
     103             : static struct mbuf *router_alert;
     104             : struct cpumem *igmpcounters;
     105             : 
     106             : void igmp_checktimer(struct ifnet *);
     107             : void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t);
     108             : int rti_fill(struct in_multi *);
     109             : struct router_info * rti_find(struct ifnet *);
     110             : int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
     111             : int igmp_sysctl_igmpstat(void *, size_t *, void *);
     112             : 
     113             : void
     114           0 : igmp_init(void)
     115             : {
     116             :         struct ipoption *ra;
     117             : 
     118           0 :         igmp_timers_are_running = 0;
     119           0 :         rti_head = 0;
     120             : 
     121           0 :         igmpcounters = counters_alloc(igps_ncounters);
     122           0 :         router_alert = m_get(M_DONTWAIT, MT_DATA);
     123           0 :         if (router_alert == NULL) {
     124           0 :                 printf("%s: no mbuf\n", __func__);
     125           0 :                 return;
     126             :         }
     127             : 
     128             :         /*
     129             :          * Construct a Router Alert option (RAO) to use in report
     130             :          * messages as required by RFC2236.  This option has the
     131             :          * following format:
     132             :          *
     133             :          *      | 10010100 | 00000100 |  2 octet value  |
     134             :          *
     135             :          * where a value of "0" indicates that routers shall examine
     136             :          * the packet.
     137             :          */
     138           0 :         ra = mtod(router_alert, struct ipoption *);
     139           0 :         ra->ipopt_dst.s_addr = INADDR_ANY;
     140           0 :         ra->ipopt_list[0] = IPOPT_RA;
     141           0 :         ra->ipopt_list[1] = 0x04;
     142           0 :         ra->ipopt_list[2] = 0x00;
     143           0 :         ra->ipopt_list[3] = 0x00;
     144           0 :         router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
     145           0 : }
     146             : 
     147             : /* Return -1 for error. */
     148             : int
     149           0 : rti_fill(struct in_multi *inm)
     150             : {
     151             :         struct router_info *rti;
     152             : 
     153           0 :         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
     154           0 :                 if (rti->rti_ifidx == inm->inm_ifidx) {
     155           0 :                         inm->inm_rti = rti;
     156           0 :                         if (rti->rti_type == IGMP_v1_ROUTER)
     157           0 :                                 return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
     158             :                         else
     159           0 :                                 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
     160             :                 }
     161             :         }
     162             : 
     163           0 :         rti = (struct router_info *)malloc(sizeof(struct router_info),
     164             :                                            M_MRTABLE, M_NOWAIT);
     165           0 :         if (rti == NULL)
     166           0 :                 return (-1);
     167           0 :         rti->rti_ifidx = inm->inm_ifidx;
     168           0 :         rti->rti_type = IGMP_v2_ROUTER;
     169           0 :         rti->rti_next = rti_head;
     170           0 :         rti_head = rti;
     171           0 :         inm->inm_rti = rti;
     172           0 :         return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
     173           0 : }
     174             : 
     175             : struct router_info *
     176           0 : rti_find(struct ifnet *ifp)
     177             : {
     178             :         struct router_info *rti;
     179             : 
     180           0 :         KERNEL_ASSERT_LOCKED();
     181           0 :         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
     182           0 :                 if (rti->rti_ifidx == ifp->if_index)
     183           0 :                         return (rti);
     184             :         }
     185             : 
     186           0 :         rti = (struct router_info *)malloc(sizeof(struct router_info),
     187             :                                            M_MRTABLE, M_NOWAIT);
     188           0 :         if (rti == NULL)
     189           0 :                 return (NULL);
     190           0 :         rti->rti_ifidx = ifp->if_index;
     191           0 :         rti->rti_type = IGMP_v2_ROUTER;
     192           0 :         rti->rti_next = rti_head;
     193           0 :         rti_head = rti;
     194           0 :         return (rti);
     195           0 : }
     196             : 
     197             : void
     198           0 : rti_delete(struct ifnet *ifp)
     199             : {
     200             :         struct router_info *rti, **prti = &rti_head;
     201             : 
     202           0 :         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
     203           0 :                 if (rti->rti_ifidx == ifp->if_index) {
     204           0 :                         *prti = rti->rti_next;
     205           0 :                         free(rti, M_MRTABLE, sizeof(*rti));
     206           0 :                         break;
     207             :                 }
     208             :                 prti = &rti->rti_next;
     209             :         }
     210           0 : }
     211             : 
     212             : int
     213           0 : igmp_input(struct mbuf **mp, int *offp, int proto, int af)
     214             : {
     215             :         struct ifnet *ifp;
     216             : 
     217           0 :         igmpstat_inc(igps_rcv_total);
     218             : 
     219           0 :         ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
     220           0 :         if (ifp == NULL) {
     221           0 :                 m_freemp(mp);
     222           0 :                 return IPPROTO_DONE;
     223             :         }
     224             : 
     225           0 :         KERNEL_LOCK();
     226           0 :         proto = igmp_input_if(ifp, mp, offp, proto, af);
     227           0 :         KERNEL_UNLOCK();
     228           0 :         if_put(ifp);
     229           0 :         return proto;
     230           0 : }
     231             : 
     232             : int
     233           0 : igmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
     234             : {
     235           0 :         struct mbuf *m = *mp;
     236           0 :         int iphlen = *offp;
     237           0 :         struct ip *ip = mtod(m, struct ip *);
     238             :         struct igmp *igmp;
     239             :         int igmplen;
     240             :         int minlen;
     241             :         struct ifmaddr *ifma;
     242             :         struct in_multi *inm;
     243             :         struct router_info *rti;
     244             :         struct in_ifaddr *ia;
     245             :         int timer;
     246             : 
     247           0 :         igmplen = ntohs(ip->ip_len) - iphlen;
     248             : 
     249             :         /*
     250             :          * Validate lengths
     251             :          */
     252           0 :         if (igmplen < IGMP_MINLEN) {
     253           0 :                 igmpstat_inc(igps_rcv_tooshort);
     254           0 :                 m_freem(m);
     255           0 :                 return IPPROTO_DONE;
     256             :         }
     257           0 :         minlen = iphlen + IGMP_MINLEN;
     258           0 :         if ((m->m_flags & M_EXT || m->m_len < minlen) &&
     259           0 :             (m = *mp = m_pullup(m, minlen)) == NULL) {
     260           0 :                 igmpstat_inc(igps_rcv_tooshort);
     261           0 :                 return IPPROTO_DONE;
     262             :         }
     263             : 
     264             :         /*
     265             :          * Validate checksum
     266             :          */
     267           0 :         m->m_data += iphlen;
     268           0 :         m->m_len -= iphlen;
     269           0 :         igmp = mtod(m, struct igmp *);
     270           0 :         if (in_cksum(m, igmplen)) {
     271           0 :                 igmpstat_inc(igps_rcv_badsum);
     272           0 :                 m_freem(m);
     273           0 :                 return IPPROTO_DONE;
     274             :         }
     275           0 :         m->m_data -= iphlen;
     276           0 :         m->m_len += iphlen;
     277           0 :         ip = mtod(m, struct ip *);
     278             : 
     279           0 :         switch (igmp->igmp_type) {
     280             : 
     281             :         case IGMP_HOST_MEMBERSHIP_QUERY:
     282           0 :                 igmpstat_inc(igps_rcv_queries);
     283             : 
     284           0 :                 if (ifp->if_flags & IFF_LOOPBACK)
     285             :                         break;
     286             : 
     287           0 :                 if (igmp->igmp_code == 0) {
     288           0 :                         rti = rti_find(ifp);
     289           0 :                         if (rti == NULL) {
     290           0 :                                 m_freem(m);
     291           0 :                                 return IPPROTO_DONE;
     292             :                         }
     293           0 :                         rti->rti_type = IGMP_v1_ROUTER;
     294           0 :                         rti->rti_age = 0;
     295             : 
     296           0 :                         if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
     297           0 :                                 igmpstat_inc(igps_rcv_badqueries);
     298           0 :                                 m_freem(m);
     299           0 :                                 return IPPROTO_DONE;
     300             :                         }
     301             : 
     302             :                         /*
     303             :                          * Start the timers in all of our membership records
     304             :                          * for the interface on which the query arrived,
     305             :                          * except those that are already running and those
     306             :                          * that belong to a "local" group (224.0.0.X).
     307             :                          */
     308           0 :                         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
     309           0 :                                 if (ifma->ifma_addr->sa_family != AF_INET)
     310             :                                         continue;
     311           0 :                                 inm = ifmatoinm(ifma);
     312           0 :                                 if (inm->inm_timer == 0 &&
     313           0 :                                     !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
     314           0 :                                         inm->inm_state = IGMP_DELAYING_MEMBER;
     315           0 :                                         inm->inm_timer = IGMP_RANDOM_DELAY(
     316             :                                             IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
     317           0 :                                         igmp_timers_are_running = 1;
     318           0 :                                 }
     319             :                         }
     320             :                 } else {
     321           0 :                         if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
     322           0 :                                 igmpstat_inc(igps_rcv_badqueries);
     323           0 :                                 m_freem(m);
     324           0 :                                 return IPPROTO_DONE;
     325             :                         }
     326             : 
     327           0 :                         timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
     328           0 :                         if (timer == 0)
     329             :                                 timer = 1;
     330             : 
     331             :                         /*
     332             :                          * Start the timers in all of our membership records
     333             :                          * for the interface on which the query arrived,
     334             :                          * except those that are already running and those
     335             :                          * that belong to a "local" group (224.0.0.X).  For
     336             :                          * timers already running, check if they need to be
     337             :                          * reset.
     338             :                          */
     339           0 :                         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
     340           0 :                                 if (ifma->ifma_addr->sa_family != AF_INET)
     341             :                                         continue;
     342           0 :                                 inm = ifmatoinm(ifma);
     343           0 :                                 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
     344           0 :                                     (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
     345           0 :                                      ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
     346           0 :                                         switch (inm->inm_state) {
     347             :                                         case IGMP_DELAYING_MEMBER:
     348           0 :                                                 if (inm->inm_timer <= timer)
     349             :                                                         break;
     350             :                                                 /* FALLTHROUGH */
     351             :                                         case IGMP_IDLE_MEMBER:
     352             :                                         case IGMP_LAZY_MEMBER:
     353             :                                         case IGMP_AWAKENING_MEMBER:
     354           0 :                                                 inm->inm_state =
     355             :                                                     IGMP_DELAYING_MEMBER;
     356           0 :                                                 inm->inm_timer =
     357           0 :                                                     IGMP_RANDOM_DELAY(timer);
     358           0 :                                                 igmp_timers_are_running = 1;
     359           0 :                                                 break;
     360             :                                         case IGMP_SLEEPING_MEMBER:
     361           0 :                                                 inm->inm_state =
     362             :                                                     IGMP_AWAKENING_MEMBER;
     363           0 :                                                 break;
     364             :                                         }
     365             :                                 }
     366             :                         }
     367             :                 }
     368             : 
     369             :                 break;
     370             : 
     371             :         case IGMP_v1_HOST_MEMBERSHIP_REPORT:
     372           0 :                 igmpstat_inc(igps_rcv_reports);
     373             : 
     374           0 :                 if (ifp->if_flags & IFF_LOOPBACK)
     375             :                         break;
     376             : 
     377           0 :                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
     378           0 :                     igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
     379           0 :                         igmpstat_inc(igps_rcv_badreports);
     380           0 :                         m_freem(m);
     381           0 :                         return IPPROTO_DONE;
     382             :                 }
     383             : 
     384             :                 /*
     385             :                  * KLUDGE: if the IP source address of the report has an
     386             :                  * unspecified (i.e., zero) subnet number, as is allowed for
     387             :                  * a booting host, replace it with the correct subnet number
     388             :                  * so that a process-level multicast routing daemon can
     389             :                  * determine which subnet it arrived from.  This is necessary
     390             :                  * to compensate for the lack of any way for a process to
     391             :                  * determine the arrival interface of an incoming packet.
     392             :                  */
     393           0 :                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
     394           0 :                         IFP_TO_IA(ifp, ia);
     395           0 :                         if (ia)
     396           0 :                                 ip->ip_src.s_addr = ia->ia_net;
     397             :                 }
     398             : 
     399             :                 /*
     400             :                  * If we belong to the group being reported, stop
     401             :                  * our timer for that group.
     402             :                  */
     403           0 :                 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
     404           0 :                 if (inm != NULL) {
     405           0 :                         inm->inm_timer = 0;
     406           0 :                         igmpstat_inc(igps_rcv_ourreports);
     407             : 
     408           0 :                         switch (inm->inm_state) {
     409             :                         case IGMP_IDLE_MEMBER:
     410             :                         case IGMP_LAZY_MEMBER:
     411             :                         case IGMP_AWAKENING_MEMBER:
     412             :                         case IGMP_SLEEPING_MEMBER:
     413           0 :                                 inm->inm_state = IGMP_SLEEPING_MEMBER;
     414           0 :                                 break;
     415             :                         case IGMP_DELAYING_MEMBER:
     416           0 :                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
     417           0 :                                         inm->inm_state = IGMP_LAZY_MEMBER;
     418             :                                 else
     419           0 :                                         inm->inm_state = IGMP_SLEEPING_MEMBER;
     420             :                                 break;
     421             :                         }
     422             :                 }
     423             : 
     424             :                 break;
     425             : 
     426             :         case IGMP_v2_HOST_MEMBERSHIP_REPORT:
     427             : #ifdef MROUTING
     428             :                 /*
     429             :                  * Make sure we don't hear our own membership report.  Fast
     430             :                  * leave requires knowing that we are the only member of a
     431             :                  * group.
     432             :                  */
     433           0 :                 IFP_TO_IA(ifp, ia);
     434           0 :                 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
     435             :                         break;
     436             : #endif
     437             : 
     438           0 :                 igmpstat_inc(igps_rcv_reports);
     439             : 
     440           0 :                 if (ifp->if_flags & IFF_LOOPBACK)
     441             :                         break;
     442             : 
     443           0 :                 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
     444           0 :                     igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
     445           0 :                         igmpstat_inc(igps_rcv_badreports);
     446           0 :                         m_freem(m);
     447           0 :                         return IPPROTO_DONE;
     448             :                 }
     449             : 
     450             :                 /*
     451             :                  * KLUDGE: if the IP source address of the report has an
     452             :                  * unspecified (i.e., zero) subnet number, as is allowed for
     453             :                  * a booting host, replace it with the correct subnet number
     454             :                  * so that a process-level multicast routing daemon can
     455             :                  * determine which subnet it arrived from.  This is necessary
     456             :                  * to compensate for the lack of any way for a process to
     457             :                  * determine the arrival interface of an incoming packet.
     458             :                  */
     459           0 :                 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
     460             : #ifndef MROUTING
     461             :                         IFP_TO_IA(ifp, ia);
     462             : #endif
     463             :                         if (ia)
     464           0 :                                 ip->ip_src.s_addr = ia->ia_net;
     465             :                 }
     466             : 
     467             :                 /*
     468             :                  * If we belong to the group being reported, stop
     469             :                  * our timer for that group.
     470             :                  */
     471           0 :                 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
     472           0 :                 if (inm != NULL) {
     473           0 :                         inm->inm_timer = 0;
     474           0 :                         igmpstat_inc(igps_rcv_ourreports);
     475             : 
     476           0 :                         switch (inm->inm_state) {
     477             :                         case IGMP_DELAYING_MEMBER:
     478             :                         case IGMP_IDLE_MEMBER:
     479             :                         case IGMP_AWAKENING_MEMBER:
     480           0 :                                 inm->inm_state = IGMP_LAZY_MEMBER;
     481           0 :                                 break;
     482             :                         case IGMP_LAZY_MEMBER:
     483             :                         case IGMP_SLEEPING_MEMBER:
     484             :                                 break;
     485             :                         }
     486             :                 }
     487             : 
     488             :                 break;
     489             : 
     490             :         }
     491             : 
     492             :         /*
     493             :          * Pass all valid IGMP packets up to any process(es) listening
     494             :          * on a raw IGMP socket.
     495             :          */
     496           0 :         return rip_input(mp, offp, proto, af);
     497           0 : }
     498             : 
     499             : void
     500           0 : igmp_joingroup(struct in_multi *inm)
     501             : {
     502             :         struct ifnet* ifp;
     503             :         int i;
     504             : 
     505           0 :         ifp = if_get(inm->inm_ifidx);
     506             : 
     507           0 :         inm->inm_state = IGMP_IDLE_MEMBER;
     508             : 
     509           0 :         if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
     510           0 :             ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) {
     511           0 :                 if ((i = rti_fill(inm)) == -1)
     512             :                         goto out;
     513             : 
     514           0 :                 igmp_sendpkt(ifp, inm, i, 0);
     515           0 :                 inm->inm_state = IGMP_DELAYING_MEMBER;
     516           0 :                 inm->inm_timer = IGMP_RANDOM_DELAY(
     517             :                     IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
     518           0 :                 igmp_timers_are_running = 1;
     519           0 :         } else
     520           0 :                 inm->inm_timer = 0;
     521             : 
     522             : out:
     523           0 :         if_put(ifp);
     524           0 : }
     525             : 
     526             : void
     527           0 : igmp_leavegroup(struct in_multi *inm)
     528             : {
     529             :         struct ifnet* ifp;
     530             : 
     531           0 :         ifp = if_get(inm->inm_ifidx);
     532             : 
     533           0 :         switch (inm->inm_state) {
     534             :         case IGMP_DELAYING_MEMBER:
     535             :         case IGMP_IDLE_MEMBER:
     536           0 :                 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
     537           0 :                     ifp && (ifp->if_flags & IFF_LOOPBACK) == 0)
     538           0 :                         if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
     539           0 :                                 igmp_sendpkt(ifp, inm,
     540             :                                     IGMP_HOST_LEAVE_MESSAGE,
     541             :                                     INADDR_ALLROUTERS_GROUP);
     542             :                 break;
     543             :         case IGMP_LAZY_MEMBER:
     544             :         case IGMP_AWAKENING_MEMBER:
     545             :         case IGMP_SLEEPING_MEMBER:
     546             :                 break;
     547             :         }
     548           0 :         if_put(ifp);
     549           0 : }
     550             : 
     551             : void
     552           0 : igmp_fasttimo(void)
     553             : {
     554             :         struct ifnet *ifp;
     555             : 
     556           0 :         NET_LOCK();
     557             : 
     558             :         /*
     559             :          * Quick check to see if any work needs to be done, in order
     560             :          * to minimize the overhead of fasttimo processing.
     561             :          */
     562           0 :         if (!igmp_timers_are_running)
     563             :                 goto out;
     564             : 
     565           0 :         igmp_timers_are_running = 0;
     566           0 :         TAILQ_FOREACH(ifp, &ifnet, if_list)
     567           0 :                 igmp_checktimer(ifp);
     568             : 
     569             : out:
     570           0 :         NET_UNLOCK();
     571           0 : }
     572             : 
     573             : 
     574             : void
     575           0 : igmp_checktimer(struct ifnet *ifp)
     576             : {
     577             :         struct in_multi *inm;
     578             :         struct ifmaddr *ifma;
     579             : 
     580           0 :         NET_ASSERT_LOCKED();
     581             : 
     582           0 :         TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
     583           0 :                 if (ifma->ifma_addr->sa_family != AF_INET)
     584             :                         continue;
     585           0 :                 inm = ifmatoinm(ifma);
     586           0 :                 if (inm->inm_timer == 0) {
     587             :                         /* do nothing */
     588           0 :                 } else if (--inm->inm_timer == 0) {
     589           0 :                         if (inm->inm_state == IGMP_DELAYING_MEMBER) {
     590           0 :                                 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
     591           0 :                                         igmp_sendpkt(ifp, inm,
     592             :                                             IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
     593             :                                 else
     594           0 :                                         igmp_sendpkt(ifp, inm,
     595             :                                             IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
     596           0 :                                 inm->inm_state = IGMP_IDLE_MEMBER;
     597           0 :                         }
     598             :                 } else {
     599           0 :                         igmp_timers_are_running = 1;
     600             :                 }
     601             :         }
     602           0 : }
     603             : 
     604             : void
     605           0 : igmp_slowtimo(void)
     606             : {
     607             :         struct router_info *rti;
     608             : 
     609           0 :         NET_LOCK();
     610             : 
     611           0 :         for (rti = rti_head; rti != 0; rti = rti->rti_next) {
     612           0 :                 if (rti->rti_type == IGMP_v1_ROUTER &&
     613           0 :                     ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
     614           0 :                         rti->rti_type = IGMP_v2_ROUTER;
     615           0 :                 }
     616             :         }
     617             : 
     618           0 :         NET_UNLOCK();
     619           0 : }
     620             : 
     621             : void
     622           0 : igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type,
     623             :     in_addr_t addr)
     624             : {
     625             :         struct mbuf *m;
     626             :         struct igmp *igmp;
     627             :         struct ip *ip;
     628           0 :         struct ip_moptions imo;
     629             : 
     630           0 :         MGETHDR(m, M_DONTWAIT, MT_HEADER);
     631           0 :         if (m == NULL)
     632           0 :                 return;
     633             : 
     634             :         /*
     635             :          * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
     636             :          * is smaller than mbuf size returned by MGETHDR.
     637             :          */
     638           0 :         m->m_data += max_linkhdr;
     639           0 :         m->m_len = sizeof(struct ip) + IGMP_MINLEN;
     640           0 :         m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
     641             : 
     642           0 :         ip = mtod(m, struct ip *);
     643           0 :         ip->ip_tos = 0;
     644           0 :         ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
     645           0 :         ip->ip_off = 0;
     646           0 :         ip->ip_p = IPPROTO_IGMP;
     647           0 :         ip->ip_src.s_addr = INADDR_ANY;
     648           0 :         if (addr) {
     649           0 :                 ip->ip_dst.s_addr = addr;
     650           0 :         } else {
     651           0 :                 ip->ip_dst = inm->inm_addr;
     652             :         }
     653             : 
     654           0 :         m->m_data += sizeof(struct ip);
     655           0 :         m->m_len -= sizeof(struct ip);
     656           0 :         igmp = mtod(m, struct igmp *);
     657           0 :         igmp->igmp_type = type;
     658           0 :         igmp->igmp_code = 0;
     659           0 :         igmp->igmp_group = inm->inm_addr;
     660           0 :         igmp->igmp_cksum = 0;
     661           0 :         igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
     662           0 :         m->m_data -= sizeof(struct ip);
     663           0 :         m->m_len += sizeof(struct ip);
     664             : 
     665           0 :         m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
     666           0 :         imo.imo_ifidx = inm->inm_ifidx;
     667           0 :         imo.imo_ttl = 1;
     668             : 
     669             :         /*
     670             :          * Request loopback of the report if we are acting as a multicast
     671             :          * router, so that the process-level routing daemon can hear it.
     672             :          */
     673             : #ifdef MROUTING
     674           0 :         imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL);
     675             : #else
     676             :         imo.imo_loop = 0;
     677             : #endif /* MROUTING */
     678             : 
     679           0 :         ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
     680             : 
     681           0 :         igmpstat_inc(igps_snd_reports);
     682           0 : }
     683             : 
     684             : /*
     685             :  * Sysctl for igmp variables.
     686             :  */
     687             : int
     688           0 : igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
     689             :     void *newp, size_t newlen)
     690             : {
     691             :         int error;
     692             : 
     693             :         /* All sysctl names at this level are terminal. */
     694           0 :         if (namelen != 1)
     695           0 :                 return (ENOTDIR);
     696             : 
     697           0 :         switch (name[0]) {
     698             :         case IGMPCTL_STATS:
     699           0 :                 if (newp != NULL)
     700           0 :                         return (EPERM);
     701           0 :                 return (igmp_sysctl_igmpstat(oldp, oldlenp, newp));
     702             :         default:
     703           0 :                 if (name[0] < IGMPCTL_MAXID) {
     704           0 :                         NET_LOCK();
     705           0 :                         error = sysctl_int_arr(igmpctl_vars, name, namelen,
     706             :                             oldp, oldlenp, newp, newlen);
     707           0 :                         NET_UNLOCK();
     708           0 :                         return (error);
     709             :                 }
     710           0 :                 return (ENOPROTOOPT);
     711             :         }
     712             :         /* NOTREACHED */
     713           0 : }
     714             : 
     715             : int
     716           0 : igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp)
     717             : {
     718           0 :         uint64_t counters[igps_ncounters];
     719           0 :         struct igmpstat igmpstat;
     720           0 :         u_long *words = (u_long *)&igmpstat;
     721             :         int i;
     722             : 
     723             :         CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long)));
     724           0 :         memset(&igmpstat, 0, sizeof igmpstat);
     725           0 :         counters_read(igmpcounters, counters, nitems(counters));
     726             : 
     727           0 :         for (i = 0; i < nitems(counters); i++)
     728           0 :                 words[i] = (u_long)counters[i];
     729             : 
     730           0 :         return (sysctl_rdstruct(oldp, oldlenp, newp,
     731             :             &igmpstat, sizeof(igmpstat)));
     732           0 : }

Generated by: LCOV version 1.13