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
rti_fill(struct in_multi * inm)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 *
rti_find(ifnet_t * ifp)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
rti_delete(ifnet_t * ifp)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
igmp_init(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
igmp_input(struct mbuf * m,int off,int proto)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
igmp_joingroup(struct in_multi * inm)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
igmp_leavegroup(struct in_multi * inm)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
igmp_fasttimo(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
igmp_slowtimo(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
igmp_sendpkt(struct in_multi * inm,int type)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
igmp_purgeif(ifnet_t * ifp)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
sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)666 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
667 {
668           return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS);
669 }
670 
671 static void
sysctl_net_inet_igmp_setup(struct sysctllog ** clog)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 }
692