xref: /openbsd-src/sys/netinet/igmp.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: igmp.c,v 1.26 2007/12/14 18:33:40 deraadt 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/socket.h>
81 #include <sys/protosw.h>
82 #include <sys/sysctl.h>
83 
84 #include <net/if.h>
85 #include <net/route.h>
86 
87 #include <netinet/in.h>
88 #include <netinet/in_var.h>
89 #include <netinet/in_systm.h>
90 #include <netinet/ip.h>
91 #include <netinet/ip_var.h>
92 #include <netinet/igmp.h>
93 #include <netinet/igmp_var.h>
94 #include <dev/rndvar.h>
95 
96 #include <sys/stdarg.h>
97 
98 #define IP_MULTICASTOPTS	0
99 
100 int *igmpctl_vars[IGMPCTL_MAXID] = IGMPCTL_VARS;
101 
102 int		igmp_timers_are_running;
103 static struct router_info *rti_head;
104 struct igmpstat igmpstat;
105 
106 void igmp_sendpkt(struct in_multi *, int, in_addr_t);
107 int rti_fill(struct in_multi *);
108 struct router_info * rti_find(struct ifnet *);
109 
110 void
111 igmp_init()
112 {
113 
114 	/*
115 	 * To avoid byte-swapping the same value over and over again.
116 	 */
117 	igmp_timers_are_running = 0;
118 	rti_head = 0;
119 }
120 
121 /* Return -1 for error. */
122 int
123 rti_fill(inm)
124 	struct in_multi *inm;
125 {
126 	struct router_info *rti;
127 
128 	for (rti = rti_head; rti != 0; rti = rti->rti_next) {
129 		if (rti->rti_ifp == inm->inm_ia->ia_ifp) {
130 			inm->inm_rti = rti;
131 			if (rti->rti_type == IGMP_v1_ROUTER)
132 				return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
133 			else
134 				return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
135 		}
136 	}
137 
138 	rti = (struct router_info *)malloc(sizeof(struct router_info),
139 					   M_MRTABLE, M_NOWAIT);
140 	if (rti == NULL)
141 		return (-1);
142 	rti->rti_ifp = inm->inm_ia->ia_ifp;
143 	rti->rti_type = IGMP_v2_ROUTER;
144 	rti->rti_next = rti_head;
145 	rti_head = rti;
146 	inm->inm_rti = rti;
147 	return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
148 }
149 
150 struct router_info *
151 rti_find(ifp)
152 	struct ifnet *ifp;
153 {
154 	struct router_info *rti;
155 
156 	for (rti = rti_head; rti != 0; rti = rti->rti_next) {
157 		if (rti->rti_ifp == ifp)
158 			return (rti);
159 	}
160 
161 	rti = (struct router_info *)malloc(sizeof(struct router_info),
162 					   M_MRTABLE, M_NOWAIT);
163 	if (rti == NULL)
164 		return (NULL);
165 	rti->rti_ifp = ifp;
166 	rti->rti_type = IGMP_v2_ROUTER;
167 	rti->rti_next = rti_head;
168 	rti_head = rti;
169 	return (rti);
170 }
171 
172 void
173 rti_delete(ifp)
174 	struct ifnet *ifp;
175 {
176 	struct router_info *rti, **prti = &rti_head;
177 
178 	for (rti = rti_head; rti != 0; rti = rti->rti_next) {
179 		if (rti->rti_ifp == ifp) {
180 			*prti = rti->rti_next;
181 			free(rti, M_MRTABLE);
182 			break;
183 		}
184 		prti = &rti->rti_next;
185 	}
186 }
187 
188 void
189 igmp_input(struct mbuf *m, ...)
190 {
191 	int iphlen;
192 	struct ifnet *ifp = m->m_pkthdr.rcvif;
193 	struct ip *ip = mtod(m, struct ip *);
194 	struct igmp *igmp;
195 	int igmplen;
196 	int minlen;
197 	struct in_multi *inm;
198 	struct in_multistep step;
199 	struct router_info *rti;
200 	struct in_ifaddr *ia;
201 	int timer;
202 	va_list ap;
203 
204 	va_start(ap, m);
205 	iphlen = va_arg(ap, int);
206 	va_end(ap);
207 
208 	++igmpstat.igps_rcv_total;
209 
210 	igmplen = ntohs(ip->ip_len) - iphlen;
211 
212 	/*
213 	 * Validate lengths
214 	 */
215 	if (igmplen < IGMP_MINLEN) {
216 		++igmpstat.igps_rcv_tooshort;
217 		m_freem(m);
218 		return;
219 	}
220 	minlen = iphlen + IGMP_MINLEN;
221 	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
222 	    (m = m_pullup(m, minlen)) == NULL) {
223 		++igmpstat.igps_rcv_tooshort;
224 		return;
225 	}
226 
227 	/*
228 	 * Validate checksum
229 	 */
230 	m->m_data += iphlen;
231 	m->m_len -= iphlen;
232 	igmp = mtod(m, struct igmp *);
233 	if (in_cksum(m, igmplen)) {
234 		++igmpstat.igps_rcv_badsum;
235 		m_freem(m);
236 		return;
237 	}
238 	m->m_data -= iphlen;
239 	m->m_len += iphlen;
240 	ip = mtod(m, struct ip *);
241 
242 	switch (igmp->igmp_type) {
243 
244 	case IGMP_HOST_MEMBERSHIP_QUERY:
245 		++igmpstat.igps_rcv_queries;
246 
247 		if (ifp->if_flags & IFF_LOOPBACK)
248 			break;
249 
250 		if (igmp->igmp_code == 0) {
251 			rti = rti_find(ifp);
252 			if (rti == NULL) {
253 				m_freem(m);
254 				return;
255 			}
256 			rti->rti_type = IGMP_v1_ROUTER;
257 			rti->rti_age = 0;
258 
259 			if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
260 				++igmpstat.igps_rcv_badqueries;
261 				m_freem(m);
262 				return;
263 			}
264 
265 			/*
266 			 * Start the timers in all of our membership records
267 			 * for the interface on which the query arrived,
268 			 * except those that are already running and those
269 			 * that belong to a "local" group (224.0.0.X).
270 			 */
271 			IN_FIRST_MULTI(step, inm);
272 			while (inm != NULL) {
273 				if (inm->inm_ia->ia_ifp == ifp &&
274 				    inm->inm_timer == 0 &&
275 				    !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
276 					inm->inm_state = IGMP_DELAYING_MEMBER;
277 					inm->inm_timer = IGMP_RANDOM_DELAY(
278 					    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
279 					igmp_timers_are_running = 1;
280 				}
281 				IN_NEXT_MULTI(step, inm);
282 			}
283 		} else {
284 			if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
285 				++igmpstat.igps_rcv_badqueries;
286 				m_freem(m);
287 				return;
288 			}
289 
290 			timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
291 			if (timer == 0)
292 				timer = 1;
293 
294 			/*
295 			 * Start the timers in all of our membership records
296 			 * for the interface on which the query arrived,
297 			 * except those that are already running and those
298 			 * that belong to a "local" group (224.0.0.X).  For
299 			 * timers already running, check if they need to be
300 			 * reset.
301 			 */
302 			IN_FIRST_MULTI(step, inm);
303 			while (inm != NULL) {
304 				if (inm->inm_ia->ia_ifp == ifp &&
305 				    !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
306 				    (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
307 				     ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
308 					switch (inm->inm_state) {
309 					case IGMP_DELAYING_MEMBER:
310 						if (inm->inm_timer <= timer)
311 							break;
312 						/* FALLTHROUGH */
313 					case IGMP_IDLE_MEMBER:
314 					case IGMP_LAZY_MEMBER:
315 					case IGMP_AWAKENING_MEMBER:
316 						inm->inm_state =
317 						    IGMP_DELAYING_MEMBER;
318 						inm->inm_timer =
319 						    IGMP_RANDOM_DELAY(timer);
320 						igmp_timers_are_running = 1;
321 						break;
322 					case IGMP_SLEEPING_MEMBER:
323 						inm->inm_state =
324 						    IGMP_AWAKENING_MEMBER;
325 						break;
326 					}
327 				}
328 				IN_NEXT_MULTI(step, inm);
329 			}
330 		}
331 
332 		break;
333 
334 	case IGMP_v1_HOST_MEMBERSHIP_REPORT:
335 		++igmpstat.igps_rcv_reports;
336 
337 		if (ifp->if_flags & IFF_LOOPBACK)
338 			break;
339 
340 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
341 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
342 			++igmpstat.igps_rcv_badreports;
343 			m_freem(m);
344 			return;
345 		}
346 
347 		/*
348 		 * KLUDGE: if the IP source address of the report has an
349 		 * unspecified (i.e., zero) subnet number, as is allowed for
350 		 * a booting host, replace it with the correct subnet number
351 		 * so that a process-level multicast routing daemon can
352 		 * determine which subnet it arrived from.  This is necessary
353 		 * to compensate for the lack of any way for a process to
354 		 * determine the arrival interface of an incoming packet.
355 		 */
356 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
357 			IFP_TO_IA(ifp, ia);
358 			if (ia)
359 				ip->ip_src.s_addr = ia->ia_subnet;
360 		}
361 
362 		/*
363 		 * If we belong to the group being reported, stop
364 		 * our timer for that group.
365 		 */
366 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
367 		if (inm != NULL) {
368 			inm->inm_timer = 0;
369 			++igmpstat.igps_rcv_ourreports;
370 
371 			switch (inm->inm_state) {
372 			case IGMP_IDLE_MEMBER:
373 			case IGMP_LAZY_MEMBER:
374 			case IGMP_AWAKENING_MEMBER:
375 			case IGMP_SLEEPING_MEMBER:
376 				inm->inm_state = IGMP_SLEEPING_MEMBER;
377 				break;
378 			case IGMP_DELAYING_MEMBER:
379 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
380 					inm->inm_state = IGMP_LAZY_MEMBER;
381 				else
382 					inm->inm_state = IGMP_SLEEPING_MEMBER;
383 				break;
384 			}
385 		}
386 
387 		break;
388 
389 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:
390 #ifdef MROUTING
391 		/*
392 		 * Make sure we don't hear our own membership report.  Fast
393 		 * leave requires knowing that we are the only member of a
394 		 * group.
395 		 */
396 		IFP_TO_IA(ifp, ia);
397 		if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
398 			break;
399 #endif
400 
401 		++igmpstat.igps_rcv_reports;
402 
403 		if (ifp->if_flags & IFF_LOOPBACK)
404 			break;
405 
406 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
407 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
408 			++igmpstat.igps_rcv_badreports;
409 			m_freem(m);
410 			return;
411 		}
412 
413 		/*
414 		 * KLUDGE: if the IP source address of the report has an
415 		 * unspecified (i.e., zero) subnet number, as is allowed for
416 		 * a booting host, replace it with the correct subnet number
417 		 * so that a process-level multicast routing daemon can
418 		 * determine which subnet it arrived from.  This is necessary
419 		 * to compensate for the lack of any way for a process to
420 		 * determine the arrival interface of an incoming packet.
421 		 */
422 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
423 #ifndef MROUTING
424 			IFP_TO_IA(ifp, ia);
425 #endif
426 			if (ia)
427 				ip->ip_src.s_addr = ia->ia_subnet;
428 		}
429 
430 		/*
431 		 * If we belong to the group being reported, stop
432 		 * our timer for that group.
433 		 */
434 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
435 		if (inm != NULL) {
436 			inm->inm_timer = 0;
437 			++igmpstat.igps_rcv_ourreports;
438 
439 			switch (inm->inm_state) {
440 			case IGMP_DELAYING_MEMBER:
441 			case IGMP_IDLE_MEMBER:
442 			case IGMP_AWAKENING_MEMBER:
443 				inm->inm_state = IGMP_LAZY_MEMBER;
444 				break;
445 			case IGMP_LAZY_MEMBER:
446 			case IGMP_SLEEPING_MEMBER:
447 				break;
448 			}
449 		}
450 
451 		break;
452 
453 	}
454 
455 	/*
456 	 * Pass all valid IGMP packets up to any process(es) listening
457 	 * on a raw IGMP socket.
458 	 */
459 	rip_input(m);
460 }
461 
462 void
463 igmp_joingroup(inm)
464 	struct in_multi *inm;
465 {
466 	int i, s = splsoftnet();
467 
468 	inm->inm_state = IGMP_IDLE_MEMBER;
469 
470 	if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
471 	    (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0) {
472 		if ((i = rti_fill(inm)) == -1) {
473 			splx(s);
474 			return;
475 		}
476 		igmp_sendpkt(inm, i, 0);
477 		inm->inm_state = IGMP_DELAYING_MEMBER;
478 		inm->inm_timer = IGMP_RANDOM_DELAY(
479 		    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
480 		igmp_timers_are_running = 1;
481 	} else
482 		inm->inm_timer = 0;
483 	splx(s);
484 }
485 
486 void
487 igmp_leavegroup(inm)
488 	struct in_multi *inm;
489 {
490 
491 	switch (inm->inm_state) {
492 	case IGMP_DELAYING_MEMBER:
493 	case IGMP_IDLE_MEMBER:
494 		if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
495 		    (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0)
496 			if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
497 				igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE,
498 				    INADDR_ALLROUTERS_GROUP);
499 		break;
500 	case IGMP_LAZY_MEMBER:
501 	case IGMP_AWAKENING_MEMBER:
502 	case IGMP_SLEEPING_MEMBER:
503 		break;
504 	}
505 }
506 
507 void
508 igmp_fasttimo()
509 {
510 	struct in_multi *inm;
511 	struct in_multistep step;
512 	int s;
513 
514 	/*
515 	 * Quick check to see if any work needs to be done, in order
516 	 * to minimize the overhead of fasttimo processing.
517 	 */
518 	if (!igmp_timers_are_running)
519 		return;
520 
521 	s = splsoftnet();
522 	igmp_timers_are_running = 0;
523 	IN_FIRST_MULTI(step, inm);
524 	while (inm != NULL) {
525 		if (inm->inm_timer == 0) {
526 			/* do nothing */
527 		} else if (--inm->inm_timer == 0) {
528 			if (inm->inm_state == IGMP_DELAYING_MEMBER) {
529 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
530 					igmp_sendpkt(inm,
531 					    IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
532 				else
533 					igmp_sendpkt(inm,
534 					    IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
535 				inm->inm_state = IGMP_IDLE_MEMBER;
536 			}
537 		} else {
538 			igmp_timers_are_running = 1;
539 		}
540 		IN_NEXT_MULTI(step, inm);
541 	}
542 	splx(s);
543 }
544 
545 void
546 igmp_slowtimo()
547 {
548 	struct router_info *rti;
549 	int s;
550 
551 	s = splsoftnet();
552 	for (rti = rti_head; rti != 0; rti = rti->rti_next) {
553 		if (rti->rti_type == IGMP_v1_ROUTER &&
554 		    ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
555 			rti->rti_type = IGMP_v2_ROUTER;
556 		}
557 	}
558 	splx(s);
559 }
560 
561 void
562 igmp_sendpkt(inm, type, addr)
563 	struct in_multi *inm;
564 	int type;
565 	in_addr_t addr;
566 {
567 	struct mbuf *m;
568 	struct igmp *igmp;
569 	struct ip *ip;
570 	struct ip_moptions imo;
571 #ifdef MROUTING
572 	extern struct socket *ip_mrouter;
573 #endif /* MROUTING */
574 
575 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
576 	if (m == NULL)
577 		return;
578 	/*
579 	 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
580 	 * is smaller than mbuf size returned by MGETHDR.
581 	 */
582 	m->m_data += max_linkhdr;
583 	m->m_len = sizeof(struct ip) + IGMP_MINLEN;
584 	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
585 
586 	ip = mtod(m, struct ip *);
587 	ip->ip_tos = 0;
588 	ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
589 	ip->ip_off = 0;
590 	ip->ip_p = IPPROTO_IGMP;
591 	ip->ip_src.s_addr = INADDR_ANY;
592 	if (addr) {
593 		ip->ip_dst.s_addr = addr;
594 	} else {
595 		ip->ip_dst = inm->inm_addr;
596 	}
597 
598 	m->m_data += sizeof(struct ip);
599 	m->m_len -= sizeof(struct ip);
600 	igmp = mtod(m, struct igmp *);
601 	igmp->igmp_type = type;
602 	igmp->igmp_code = 0;
603 	igmp->igmp_group = inm->inm_addr;
604 	igmp->igmp_cksum = 0;
605 	igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
606 	m->m_data -= sizeof(struct ip);
607 	m->m_len += sizeof(struct ip);
608 
609 	imo.imo_multicast_ifp = inm->inm_ia->ia_ifp;
610 	imo.imo_multicast_ttl = 1;
611 #ifdef RSVP_ISI
612 	imo.imo_multicast_vif = -1;
613 #endif
614 	/*
615 	 * Request loopback of the report if we are acting as a multicast
616 	 * router, so that the process-level routing daemon can hear it.
617 	 */
618 #ifdef MROUTING
619 	imo.imo_multicast_loop = (ip_mrouter != NULL);
620 #else
621 	imo.imo_multicast_loop = 0;
622 #endif /* MROUTING */
623 
624 	ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
625 	    &imo, (void *)NULL);
626 
627 	++igmpstat.igps_snd_reports;
628 }
629 
630 /*
631  * Sysctl for igmp variables.
632  */
633 int
634 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
635     void *newp, size_t newlen)
636 {
637 	/* All sysctl names at this level are terminal. */
638 	if (namelen != 1)
639 		return (ENOTDIR);
640 
641 	switch (name[0]) {
642 	case IGMPCTL_STATS:
643 		if (newp != NULL)
644 			return (EPERM);
645 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
646 		    &igmpstat, sizeof(igmpstat)));
647 	default:
648 		if (name[0] < IGMPCTL_MAXID)
649 			return (sysctl_int_arr(igmpctl_vars, name, namelen,
650 			    oldp, oldlenp, newp, newlen));
651 		return (ENOPROTOOPT);
652 	}
653 	/* NOTREACHED */
654 }
655