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