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