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