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
99int	igmp_timers_are_running;	/* [N] shortcut for fast timer */
100static LIST_HEAD(, router_info) rti_head;
101static struct mbuf *router_alert;
102struct cpumem *igmpcounters;
103
104void igmp_checktimer(struct ifnet *);
105void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t);
106int rti_fill(struct in_multi *);
107struct router_info * rti_find(struct ifnet *);
108int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
109int igmp_sysctl_igmpstat(void *, size_t *, void *);
110
111void
112igmp_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
141int
142rti_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
164struct router_info *
165rti_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
184void
185rti_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
198int
199igmp_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
218int
219igmp_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
485void
486igmp_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
504void
505igmp_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
524void
525igmp_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
548void
549igmp_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
578void
579igmp_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
595void
596igmp_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 */
661int
662igmp_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
680int
681igmp_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