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