xref: /netbsd-src/sys/netinet/igmp.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: igmp.c,v 1.55 2014/05/29 23:02:48 rmind 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.55 2014/05/29 23:02:48 rmind Exp $");
44 
45 #include "opt_mrouting.h"
46 
47 #include <sys/param.h>
48 #include <sys/mbuf.h>
49 #include <sys/socket.h>
50 #include <sys/socketvar.h>
51 #include <sys/protosw.h>
52 #include <sys/systm.h>
53 #include <sys/cprng.h>
54 #include <sys/sysctl.h>
55 
56 #include <net/if.h>
57 #include <net/route.h>
58 #include <net/net_stats.h>
59 
60 #include <netinet/in.h>
61 #include <netinet/in_var.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64 #include <netinet/ip_var.h>
65 #include <netinet/igmp.h>
66 #include <netinet/igmp_var.h>
67 
68 /*
69  * Per-interface router version information.
70  */
71 typedef struct router_info {
72 	LIST_ENTRY(router_info) rti_link;
73 	ifnet_t *	rti_ifp;
74 	int		rti_type;	/* type of router on this interface */
75 	int		rti_age;	/* time since last v1 query */
76 } router_info_t;
77 
78 /*
79  * The router-info list and the timer flag are protected by in_multilock.
80  *
81  * Lock order:
82  *
83  *	softnet_lock ->
84  *		in_multilock
85  */
86 static struct pool	igmp_rti_pool		__cacheline_aligned;
87 static LIST_HEAD(, router_info)	rti_head	__cacheline_aligned;
88 static int		igmp_timers_on		__cacheline_aligned;
89 static percpu_t *	igmpstat_percpu		__read_mostly;
90 
91 #define	IGMP_STATINC(x)		_NET_STATINC(igmpstat_percpu, x)
92 
93 static void		igmp_sendpkt(struct in_multi *, int);
94 static int		rti_fill(struct in_multi *);
95 static router_info_t *	rti_find(struct ifnet *);
96 static void		rti_delete(struct ifnet *);
97 static void		sysctl_net_inet_igmp_setup(struct sysctllog **);
98 
99 /*
100  * rti_fill: associate router information with the given multicast group;
101  * if there is no router information for the interface, then create it.
102  */
103 static int
104 rti_fill(struct in_multi *inm)
105 {
106 	router_info_t *rti;
107 
108 	KASSERT(in_multi_lock_held());
109 
110 	LIST_FOREACH(rti, &rti_head, rti_link) {
111 		if (rti->rti_ifp == inm->inm_ifp) {
112 			inm->inm_rti = rti;
113 			return rti->rti_type == IGMP_v1_ROUTER ?
114 			    IGMP_v1_HOST_MEMBERSHIP_REPORT :
115 			    IGMP_v2_HOST_MEMBERSHIP_REPORT;
116 		}
117 	}
118 	rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
119 	if (rti == NULL) {
120 		return 0;
121 	}
122 	rti->rti_ifp = inm->inm_ifp;
123 	rti->rti_type = IGMP_v2_ROUTER;
124 	LIST_INSERT_HEAD(&rti_head, rti, rti_link);
125 	inm->inm_rti = rti;
126 	return IGMP_v2_HOST_MEMBERSHIP_REPORT;
127 }
128 
129 /*
130  * rti_find: lookup or create router information for the given interface.
131  */
132 static router_info_t *
133 rti_find(ifnet_t *ifp)
134 {
135 	router_info_t *rti;
136 
137 	KASSERT(in_multi_lock_held());
138 
139 	LIST_FOREACH(rti, &rti_head, rti_link) {
140 		if (rti->rti_ifp == ifp)
141 			return rti;
142 	}
143 	rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
144 	if (rti == NULL) {
145 		return NULL;
146 	}
147 	rti->rti_ifp = ifp;
148 	rti->rti_type = IGMP_v2_ROUTER;
149 	LIST_INSERT_HEAD(&rti_head, rti, rti_link);
150 	return rti;
151 }
152 
153 /*
154  * rti_delete: remove and free the router information entry for the
155  * given interface.
156  */
157 static void
158 rti_delete(ifnet_t *ifp)
159 {
160 	router_info_t *rti;
161 
162 	KASSERT(in_multi_lock_held());
163 
164 	LIST_FOREACH(rti, &rti_head, rti_link) {
165 		if (rti->rti_ifp == ifp) {
166 			LIST_REMOVE(rti, rti_link);
167 			pool_put(&igmp_rti_pool, rti);
168 			break;
169 		}
170 	}
171 }
172 
173 void
174 igmp_init(void)
175 {
176 	pool_init(&igmp_rti_pool, sizeof(router_info_t), 0, 0, 0,
177 	    "igmppl", NULL, IPL_SOFTNET);
178 	igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS);
179 	sysctl_net_inet_igmp_setup(NULL);
180 	LIST_INIT(&rti_head);
181 }
182 
183 void
184 igmp_input(struct mbuf *m, ...)
185 {
186 	ifnet_t *ifp = m->m_pkthdr.rcvif;
187 	struct ip *ip = mtod(m, struct ip *);
188 	struct igmp *igmp;
189 	u_int minlen, timer;
190 	struct in_multi *inm;
191 	struct in_ifaddr *ia;
192 	int proto, ip_len, iphlen;
193 	va_list ap;
194 
195 	va_start(ap, m);
196 	iphlen = va_arg(ap, int);
197 	proto = va_arg(ap, int);
198 	va_end(ap);
199 
200 	IGMP_STATINC(IGMP_STAT_RCV_TOTAL);
201 
202 	/*
203 	 * Validate lengths
204 	 */
205 	minlen = iphlen + IGMP_MINLEN;
206 	ip_len = ntohs(ip->ip_len);
207 	if (ip_len < minlen) {
208 		IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
209 		m_freem(m);
210 		return;
211 	}
212 	if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
213 	    || m->m_len < minlen) {
214 		if ((m = m_pullup(m, minlen)) == NULL) {
215 			IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
216 			return;
217 		}
218 		ip = mtod(m, struct ip *);
219 	}
220 
221 	/*
222 	 * Validate checksum
223 	 */
224 	m->m_data += iphlen;
225 	m->m_len -= iphlen;
226 	igmp = mtod(m, struct igmp *);
227 	/* No need to assert alignment here. */
228 	if (in_cksum(m, ip_len - iphlen)) {
229 		IGMP_STATINC(IGMP_STAT_RCV_BADSUM);
230 		m_freem(m);
231 		return;
232 	}
233 	m->m_data -= iphlen;
234 	m->m_len += iphlen;
235 
236 	switch (igmp->igmp_type) {
237 
238 	case IGMP_HOST_MEMBERSHIP_QUERY:
239 		IGMP_STATINC(IGMP_STAT_RCV_QUERIES);
240 
241 		if (ifp->if_flags & IFF_LOOPBACK)
242 			break;
243 
244 		if (igmp->igmp_code == 0) {
245 			struct in_multistep step;
246 			router_info_t *rti;
247 
248 			if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
249 				IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
250 				m_freem(m);
251 				return;
252 			}
253 
254 			in_multi_lock(RW_WRITER);
255 			rti = rti_find(ifp);
256 			if (rti == NULL) {
257 				in_multi_unlock();
258 				break;
259 			}
260 			rti->rti_type = IGMP_v1_ROUTER;
261 			rti->rti_age = 0;
262 
263 			/*
264 			 * Start the timers in all of our membership records
265 			 * for the interface on which the query arrived,
266 			 * except those that are already running and those
267 			 * that belong to a "local" group (224.0.0.X).
268 			 */
269 
270 			inm = in_first_multi(&step);
271 			while (inm != NULL) {
272 				if (inm->inm_ifp == ifp &&
273 				    inm->inm_timer == 0 &&
274 				    !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
275 					inm->inm_state = IGMP_DELAYING_MEMBER;
276 					inm->inm_timer = IGMP_RANDOM_DELAY(
277 					    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
278 					igmp_timers_on = true;
279 				}
280 				inm = in_next_multi(&step);
281 			}
282 			in_multi_unlock();
283 		} else {
284 			struct in_multistep step;
285 
286 			if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
287 				IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
288 				m_freem(m);
289 				return;
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 			m_freem(m);
348 			return;
349 		}
350 
351 		/*
352 		 * KLUDGE: if the IP source address of the report has an
353 		 * unspecified (i.e., zero) subnet number, as is allowed for
354 		 * a booting host, replace it with the correct subnet number
355 		 * so that a process-level multicast routing daemon can
356 		 * determine which subnet it arrived from.  This is necessary
357 		 * to compensate for the lack of any way for a process to
358 		 * determine the arrival interface of an incoming packet.
359 		 */
360 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
361 			IFP_TO_IA(ifp, ia);		/* XXX */
362 			if (ia)
363 				ip->ip_src.s_addr = ia->ia_subnet;
364 		}
365 
366 		/*
367 		 * If we belong to the group being reported, stop
368 		 * our timer for that group.
369 		 */
370 		in_multi_lock(RW_WRITER);
371 		inm = in_lookup_multi(igmp->igmp_group, ifp);
372 		if (inm != NULL) {
373 			inm->inm_timer = 0;
374 			IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
375 
376 			switch (inm->inm_state) {
377 			case IGMP_IDLE_MEMBER:
378 			case IGMP_LAZY_MEMBER:
379 			case IGMP_AWAKENING_MEMBER:
380 			case IGMP_SLEEPING_MEMBER:
381 				inm->inm_state = IGMP_SLEEPING_MEMBER;
382 				break;
383 			case IGMP_DELAYING_MEMBER:
384 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
385 					inm->inm_state = IGMP_LAZY_MEMBER;
386 				else
387 					inm->inm_state = IGMP_SLEEPING_MEMBER;
388 				break;
389 			}
390 		}
391 		in_multi_unlock();
392 		break;
393 
394 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:
395 #ifdef MROUTING
396 		/*
397 		 * Make sure we don't hear our own membership report.  Fast
398 		 * leave requires knowing that we are the only member of a
399 		 * group.
400 		 */
401 		IFP_TO_IA(ifp, ia);			/* XXX */
402 		if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr))
403 			break;
404 #endif
405 
406 		IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
407 
408 		if (ifp->if_flags & IFF_LOOPBACK)
409 			break;
410 
411 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
412 		    !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
413 			IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
414 			m_freem(m);
415 			return;
416 		}
417 
418 		/*
419 		 * KLUDGE: if the IP source address of the report has an
420 		 * unspecified (i.e., zero) subnet number, as is allowed for
421 		 * a booting host, replace it with the correct subnet number
422 		 * so that a process-level multicast routing daemon can
423 		 * determine which subnet it arrived from.  This is necessary
424 		 * to compensate for the lack of any way for a process to
425 		 * determine the arrival interface of an incoming packet.
426 		 */
427 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
428 #ifndef MROUTING
429 			IFP_TO_IA(ifp, ia);		/* XXX */
430 #endif
431 			if (ia)
432 				ip->ip_src.s_addr = ia->ia_subnet;
433 		}
434 
435 		/*
436 		 * If we belong to the group being reported, stop
437 		 * our timer for that group.
438 		 */
439 		in_multi_lock(RW_WRITER);
440 		inm = in_lookup_multi(igmp->igmp_group, ifp);
441 		if (inm != NULL) {
442 			inm->inm_timer = 0;
443 			IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
444 
445 			switch (inm->inm_state) {
446 			case IGMP_DELAYING_MEMBER:
447 			case IGMP_IDLE_MEMBER:
448 			case IGMP_AWAKENING_MEMBER:
449 				inm->inm_state = IGMP_LAZY_MEMBER;
450 				break;
451 			case IGMP_LAZY_MEMBER:
452 			case IGMP_SLEEPING_MEMBER:
453 				break;
454 			}
455 		}
456 		in_multi_unlock();
457 		break;
458 
459 	}
460 
461 	/*
462 	 * Pass all valid IGMP packets up to any process(es) listening
463 	 * on a raw IGMP socket.
464 	 */
465 	rip_input(m, iphlen, proto);
466 	return;
467 }
468 
469 int
470 igmp_joingroup(struct in_multi *inm)
471 {
472 	KASSERT(in_multi_lock_held());
473 	inm->inm_state = IGMP_IDLE_MEMBER;
474 
475 	if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
476 	    (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
477 		int report_type;
478 
479 		report_type = rti_fill(inm);
480 		if (report_type == 0) {
481 			return ENOMEM;
482 		}
483 		igmp_sendpkt(inm, report_type);
484 		inm->inm_state = IGMP_DELAYING_MEMBER;
485 		inm->inm_timer = IGMP_RANDOM_DELAY(
486 		    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
487 		igmp_timers_on = true;
488 	} else
489 		inm->inm_timer = 0;
490 
491 	return 0;
492 }
493 
494 void
495 igmp_leavegroup(struct in_multi *inm)
496 {
497 	KASSERT(in_multi_lock_held());
498 
499 	switch (inm->inm_state) {
500 	case IGMP_DELAYING_MEMBER:
501 	case IGMP_IDLE_MEMBER:
502 		if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
503 		    (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
504 			if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
505 				igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
506 		break;
507 	case IGMP_LAZY_MEMBER:
508 	case IGMP_AWAKENING_MEMBER:
509 	case IGMP_SLEEPING_MEMBER:
510 		break;
511 	}
512 }
513 
514 void
515 igmp_fasttimo(void)
516 {
517 	struct in_multi *inm;
518 	struct in_multistep step;
519 
520 	/*
521 	 * Quick check to see if any work needs to be done, in order
522 	 * to minimize the overhead of fasttimo processing.
523 	 */
524 	if (!igmp_timers_on) {
525 		return;
526 	}
527 
528 	/* XXX: Needed for ip_output(). */
529 	mutex_enter(softnet_lock);
530 
531 	in_multi_lock(RW_WRITER);
532 	igmp_timers_on = false;
533 	inm = in_first_multi(&step);
534 	while (inm != NULL) {
535 		if (inm->inm_timer == 0) {
536 			/* do nothing */
537 		} else if (--inm->inm_timer == 0) {
538 			if (inm->inm_state == IGMP_DELAYING_MEMBER) {
539 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
540 					igmp_sendpkt(inm,
541 					    IGMP_v1_HOST_MEMBERSHIP_REPORT);
542 				else
543 					igmp_sendpkt(inm,
544 					    IGMP_v2_HOST_MEMBERSHIP_REPORT);
545 				inm->inm_state = IGMP_IDLE_MEMBER;
546 			}
547 		} else {
548 			igmp_timers_on = true;
549 		}
550 		inm = in_next_multi(&step);
551 	}
552 	in_multi_unlock();
553 	mutex_exit(softnet_lock);
554 }
555 
556 void
557 igmp_slowtimo(void)
558 {
559 	router_info_t *rti;
560 
561 	in_multi_lock(RW_WRITER);
562 	LIST_FOREACH(rti, &rti_head, rti_link) {
563 		if (rti->rti_type == IGMP_v1_ROUTER &&
564 		    ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
565 			rti->rti_type = IGMP_v2_ROUTER;
566 		}
567 	}
568 	in_multi_unlock();
569 }
570 
571 /*
572  * igmp_sendpkt: construct an IGMP packet, given the multicast structure
573  * and the type, and send the datagram.
574  */
575 static void
576 igmp_sendpkt(struct in_multi *inm, int type)
577 {
578 	struct mbuf *m;
579 	struct igmp *igmp;
580 	struct ip *ip;
581 	struct ip_moptions imo;
582 
583 	KASSERT(in_multi_lock_held());
584 
585 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
586 	if (m == NULL)
587 		return;
588 
589 	/*
590 	 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
591 	 * is smaller than mbuf size returned by MGETHDR.
592 	 */
593 	m->m_data += max_linkhdr;
594 	m->m_len = sizeof(struct ip) + IGMP_MINLEN;
595 	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
596 
597 	ip = mtod(m, struct ip *);
598 	ip->ip_tos = 0;
599 	ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
600 	ip->ip_off = htons(0);
601 	ip->ip_p = IPPROTO_IGMP;
602 	ip->ip_src = zeroin_addr;
603 	ip->ip_dst = inm->inm_addr;
604 
605 	m->m_data += sizeof(struct ip);
606 	m->m_len -= sizeof(struct ip);
607 	igmp = mtod(m, struct igmp *);
608 	igmp->igmp_type = type;
609 	igmp->igmp_code = 0;
610 	igmp->igmp_group = inm->inm_addr;
611 	igmp->igmp_cksum = 0;
612 	igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
613 	m->m_data -= sizeof(struct ip);
614 	m->m_len += sizeof(struct ip);
615 
616 	imo.imo_multicast_ifp = inm->inm_ifp;
617 	imo.imo_multicast_ttl = 1;
618 #ifdef RSVP_ISI
619 	imo.imo_multicast_vif = -1;
620 #endif
621 	/*
622 	 * Request loopback of the report if we are acting as a multicast
623 	 * router, so that the process-level routing demon can hear it.
624 	 */
625 #ifdef MROUTING
626 	extern struct socket *ip_mrouter;
627 	imo.imo_multicast_loop = (ip_mrouter != NULL);
628 #else
629 	imo.imo_multicast_loop = 0;
630 #endif
631 
632 	/*
633 	 * Note: IP_IGMP_MCAST indicates that in_multilock is held.
634 	 * The caller must still acquire softnet_lock for ip_output().
635 	 */
636 	KASSERT(mutex_owned(softnet_lock));
637 	ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL);
638 	IGMP_STATINC(IGMP_STAT_SND_REPORTS);
639 }
640 
641 void
642 igmp_purgeif(ifnet_t *ifp)
643 {
644 	in_multi_lock(RW_WRITER);
645 	rti_delete(ifp);
646 	in_multi_unlock();
647 }
648 
649 static int
650 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
651 {
652 	return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS);
653 }
654 
655 static void
656 sysctl_net_inet_igmp_setup(struct sysctllog **clog)
657 {
658 	sysctl_createv(clog, 0, NULL, NULL,
659 			CTLFLAG_PERMANENT,
660 			CTLTYPE_NODE, "inet", NULL,
661 			NULL, 0, NULL, 0,
662 			CTL_NET, PF_INET, CTL_EOL);
663 	sysctl_createv(clog, 0, NULL, NULL,
664 			CTLFLAG_PERMANENT,
665 			CTLTYPE_NODE, "igmp",
666 			SYSCTL_DESCR("Internet Group Management Protocol"),
667 			NULL, 0, NULL, 0,
668 			CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL);
669 	sysctl_createv(clog, 0, NULL, NULL,
670 			CTLFLAG_PERMANENT,
671 			CTLTYPE_STRUCT, "stats",
672 			SYSCTL_DESCR("IGMP statistics"),
673 			sysctl_net_inet_igmp_stats, 0, NULL, 0,
674 			CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL);
675 }
676