xref: /netbsd-src/sys/netinet/igmp.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: igmp.c,v 1.49 2008/05/04 07:22:14 thorpej Exp $	*/
2 
3 /*
4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * Internet Group Management Protocol (IGMP) routines.
34  *
35  * Written by Steve Deering, Stanford, May 1988.
36  * Modified by Rosen Sharma, Stanford, Aug 1994.
37  * Modified by Bill Fenner, Xerox PARC, Feb 1995.
38  *
39  * MULTICAST Revision: 1.3
40  */
41 
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.49 2008/05/04 07:22:14 thorpej Exp $");
44 
45 #include "opt_mrouting.h"
46 
47 #include <sys/param.h>
48 #include <sys/mbuf.h>
49 #include <sys/socket.h>
50 #include <sys/socketvar.h>
51 #include <sys/protosw.h>
52 #include <sys/systm.h>
53 #include <sys/sysctl.h>
54 
55 #include <net/if.h>
56 #include <net/route.h>
57 #include <net/net_stats.h>
58 
59 #include <netinet/in.h>
60 #include <netinet/in_var.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63 #include <netinet/ip_var.h>
64 #include <netinet/igmp.h>
65 #include <netinet/igmp_var.h>
66 
67 #include <machine/stdarg.h>
68 
69 #define IP_MULTICASTOPTS	0
70 
71 POOL_INIT(igmp_rti_pool, sizeof(struct router_info), 0, 0, 0, "igmppl", NULL,
72     IPL_SOFTNET);
73 
74 static percpu_t *igmpstat_percpu;
75 
76 #define	IGMP_STATINC(x)		_NET_STATINC(igmpstat_percpu, x)
77 
78 int igmp_timers_are_running;
79 static LIST_HEAD(, router_info) rti_head = LIST_HEAD_INITIALIZER(rti_head);
80 
81 void igmp_sendpkt(struct in_multi *, int);
82 static int rti_fill(struct in_multi *);
83 static struct router_info *rti_find(struct ifnet *);
84 static void rti_delete(struct ifnet *);
85 
86 static int
87 rti_fill(struct in_multi *inm)
88 {
89 	struct router_info *rti;
90 
91 	/* this function is called at splsoftnet() */
92 	LIST_FOREACH(rti, &rti_head, rti_link) {
93 		if (rti->rti_ifp == inm->inm_ifp) {
94 			inm->inm_rti = rti;
95 			if (rti->rti_type == IGMP_v1_ROUTER)
96 				return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
97 			else
98 				return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
99 		}
100 	}
101 
102 	rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
103 	if (rti == NULL)
104 		return 0;
105 	rti->rti_ifp = inm->inm_ifp;
106 	rti->rti_type = IGMP_v2_ROUTER;
107 	LIST_INSERT_HEAD(&rti_head, rti, rti_link);
108 	inm->inm_rti = rti;
109 	return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
110 }
111 
112 static struct router_info *
113 rti_find(struct ifnet *ifp)
114 {
115 	struct router_info *rti;
116 	int s = splsoftnet();
117 
118 	LIST_FOREACH(rti, &rti_head, rti_link) {
119 		if (rti->rti_ifp == ifp)
120 			return (rti);
121 	}
122 
123 	rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
124 	if (rti == NULL) {
125 		splx(s);
126 		return NULL;
127 	}
128 	rti->rti_ifp = ifp;
129 	rti->rti_type = IGMP_v2_ROUTER;
130 	LIST_INSERT_HEAD(&rti_head, rti, rti_link);
131 	splx(s);
132 	return (rti);
133 }
134 
135 static void
136 rti_delete(struct ifnet *ifp)	/* MUST be called at splsoftnet */
137 {
138 	struct router_info *rti;
139 
140 	LIST_FOREACH(rti, &rti_head, rti_link) {
141 		if (rti->rti_ifp == ifp) {
142 			LIST_REMOVE(rti, rti_link);
143 			pool_put(&igmp_rti_pool, rti);
144 			return;
145 		}
146 	}
147 }
148 
149 void
150 igmp_init(void)
151 {
152 
153 	igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS);
154 }
155 
156 void
157 igmp_input(struct mbuf *m, ...)
158 {
159 	int proto;
160 	int iphlen;
161 	struct ifnet *ifp = m->m_pkthdr.rcvif;
162 	struct ip *ip = mtod(m, struct ip *);
163 	struct igmp *igmp;
164 	u_int minlen;
165 	struct in_multi *inm;
166 	struct in_multistep step;
167 	struct router_info *rti;
168 	struct in_ifaddr *ia;
169 	u_int timer;
170 	va_list ap;
171 	u_int16_t ip_len;
172 
173 	va_start(ap, m);
174 	iphlen = va_arg(ap, int);
175 	proto = va_arg(ap, int);
176 	va_end(ap);
177 
178 	IGMP_STATINC(IGMP_STAT_RCV_TOTAL);
179 
180 	/*
181 	 * Validate lengths
182 	 */
183 	minlen = iphlen + IGMP_MINLEN;
184 	ip_len = ntohs(ip->ip_len);
185 	if (ip_len < minlen) {
186 		IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
187 		m_freem(m);
188 		return;
189 	}
190 	if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
191 	    || m->m_len < minlen) {
192 		if ((m = m_pullup(m, minlen)) == 0) {
193 			IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
194 			return;
195 		}
196 		ip = mtod(m, struct ip *);
197 	}
198 
199 	/*
200 	 * Validate checksum
201 	 */
202 	m->m_data += iphlen;
203 	m->m_len -= iphlen;
204 	igmp = mtod(m, struct igmp *);
205 	/* No need to assert alignment here. */
206 	if (in_cksum(m, ip_len - iphlen)) {
207 		IGMP_STATINC(IGMP_STAT_RCV_BADSUM);
208 		m_freem(m);
209 		return;
210 	}
211 	m->m_data -= iphlen;
212 	m->m_len += iphlen;
213 
214 	switch (igmp->igmp_type) {
215 
216 	case IGMP_HOST_MEMBERSHIP_QUERY:
217 		IGMP_STATINC(IGMP_STAT_RCV_QUERIES);
218 
219 		if (ifp->if_flags & IFF_LOOPBACK)
220 			break;
221 
222 		if (igmp->igmp_code == 0) {
223 			rti = rti_find(ifp);
224 			if (rti == NULL)
225 				break;
226 			rti->rti_type = IGMP_v1_ROUTER;
227 			rti->rti_age = 0;
228 
229 			if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
230 				IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
231 				m_freem(m);
232 				return;
233 			}
234 
235 			/*
236 			 * Start the timers in all of our membership records
237 			 * for the interface on which the query arrived,
238 			 * except those that are already running and those
239 			 * that belong to a "local" group (224.0.0.X).
240 			 */
241 			IN_FIRST_MULTI(step, inm);
242 			while (inm != NULL) {
243 				if (inm->inm_ifp == ifp &&
244 				    inm->inm_timer == 0 &&
245 				    !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
246 					inm->inm_state = IGMP_DELAYING_MEMBER;
247 					inm->inm_timer = IGMP_RANDOM_DELAY(
248 					    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
249 					igmp_timers_are_running = 1;
250 				}
251 				IN_NEXT_MULTI(step, inm);
252 			}
253 		} else {
254 			if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
255 				IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
256 				m_freem(m);
257 				return;
258 			}
259 
260 			timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
261 			if (timer == 0)
262 				timer =1;
263 
264 			/*
265 			 * Start the timers in all of our membership records
266 			 * for the interface on which the query arrived,
267 			 * except those that are already running and those
268 			 * that belong to a "local" group (224.0.0.X).  For
269 			 * timers already running, check if they need to be
270 			 * reset.
271 			 */
272 			IN_FIRST_MULTI(step, inm);
273 			while (inm != NULL) {
274 				if (inm->inm_ifp == ifp &&
275 				    !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
276 				    (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
277 				     in_hosteq(ip->ip_dst, inm->inm_addr))) {
278 					switch (inm->inm_state) {
279 					case IGMP_DELAYING_MEMBER:
280 						if (inm->inm_timer <= timer)
281 							break;
282 						/* FALLTHROUGH */
283 					case IGMP_IDLE_MEMBER:
284 					case IGMP_LAZY_MEMBER:
285 					case IGMP_AWAKENING_MEMBER:
286 						inm->inm_state =
287 						    IGMP_DELAYING_MEMBER;
288 						inm->inm_timer =
289 						    IGMP_RANDOM_DELAY(timer);
290 						igmp_timers_are_running = 1;
291 						break;
292 					case IGMP_SLEEPING_MEMBER:
293 						inm->inm_state =
294 						    IGMP_AWAKENING_MEMBER;
295 						break;
296 					}
297 				}
298 				IN_NEXT_MULTI(step, inm);
299 			}
300 		}
301 
302 		break;
303 
304 	case IGMP_v1_HOST_MEMBERSHIP_REPORT:
305 		IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
306 
307 		if (ifp->if_flags & IFF_LOOPBACK)
308 			break;
309 
310 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
311 		    !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
312 			IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
313 			m_freem(m);
314 			return;
315 		}
316 
317 		/*
318 		 * KLUDGE: if the IP source address of the report has an
319 		 * unspecified (i.e., zero) subnet number, as is allowed for
320 		 * a booting host, replace it with the correct subnet number
321 		 * so that a process-level multicast routing daemon can
322 		 * determine which subnet it arrived from.  This is necessary
323 		 * to compensate for the lack of any way for a process to
324 		 * determine the arrival interface of an incoming packet.
325 		 */
326 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
327 			IFP_TO_IA(ifp, ia);		/* XXX */
328 			if (ia)
329 				ip->ip_src.s_addr = ia->ia_subnet;
330 		}
331 
332 		/*
333 		 * If we belong to the group being reported, stop
334 		 * our timer for that group.
335 		 */
336 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
337 		if (inm != NULL) {
338 			inm->inm_timer = 0;
339 			IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
340 
341 			switch (inm->inm_state) {
342 			case IGMP_IDLE_MEMBER:
343 			case IGMP_LAZY_MEMBER:
344 			case IGMP_AWAKENING_MEMBER:
345 			case IGMP_SLEEPING_MEMBER:
346 				inm->inm_state = IGMP_SLEEPING_MEMBER;
347 				break;
348 			case IGMP_DELAYING_MEMBER:
349 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
350 					inm->inm_state = IGMP_LAZY_MEMBER;
351 				else
352 					inm->inm_state = IGMP_SLEEPING_MEMBER;
353 				break;
354 			}
355 		}
356 
357 		break;
358 
359 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:
360 #ifdef MROUTING
361 		/*
362 		 * Make sure we don't hear our own membership report.  Fast
363 		 * leave requires knowing that we are the only member of a
364 		 * group.
365 		 */
366 		IFP_TO_IA(ifp, ia);			/* XXX */
367 		if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr))
368 			break;
369 #endif
370 
371 		IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
372 
373 		if (ifp->if_flags & IFF_LOOPBACK)
374 			break;
375 
376 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
377 		    !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
378 			IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
379 			m_freem(m);
380 			return;
381 		}
382 
383 		/*
384 		 * KLUDGE: if the IP source address of the report has an
385 		 * unspecified (i.e., zero) subnet number, as is allowed for
386 		 * a booting host, replace it with the correct subnet number
387 		 * so that a process-level multicast routing daemon can
388 		 * determine which subnet it arrived from.  This is necessary
389 		 * to compensate for the lack of any way for a process to
390 		 * determine the arrival interface of an incoming packet.
391 		 */
392 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
393 #ifndef MROUTING
394 			IFP_TO_IA(ifp, ia);		/* XXX */
395 #endif
396 			if (ia)
397 				ip->ip_src.s_addr = ia->ia_subnet;
398 		}
399 
400 		/*
401 		 * If we belong to the group being reported, stop
402 		 * our timer for that group.
403 		 */
404 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
405 		if (inm != NULL) {
406 			inm->inm_timer = 0;
407 			IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
408 
409 			switch (inm->inm_state) {
410 			case IGMP_DELAYING_MEMBER:
411 			case IGMP_IDLE_MEMBER:
412 			case IGMP_AWAKENING_MEMBER:
413 				inm->inm_state = IGMP_LAZY_MEMBER;
414 				break;
415 			case IGMP_LAZY_MEMBER:
416 			case IGMP_SLEEPING_MEMBER:
417 				break;
418 			}
419 		}
420 
421 		break;
422 
423 	}
424 
425 	/*
426 	 * Pass all valid IGMP packets up to any process(es) listening
427 	 * on a raw IGMP socket.
428 	 */
429 	rip_input(m, iphlen, proto);
430 	return;
431 }
432 
433 int
434 igmp_joingroup(struct in_multi *inm)
435 {
436 	int report_type;
437 	int s = splsoftnet();
438 
439 	inm->inm_state = IGMP_IDLE_MEMBER;
440 
441 	if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
442 	    (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
443 		report_type = rti_fill(inm);
444 		if (report_type == 0) {
445 			splx(s);
446 			return ENOMEM;
447 		}
448 		igmp_sendpkt(inm, report_type);
449 		inm->inm_state = IGMP_DELAYING_MEMBER;
450 		inm->inm_timer = IGMP_RANDOM_DELAY(
451 		    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
452 		igmp_timers_are_running = 1;
453 	} else
454 		inm->inm_timer = 0;
455 	splx(s);
456 	return 0;
457 }
458 
459 void
460 igmp_leavegroup(struct in_multi *inm)
461 {
462 
463 	switch (inm->inm_state) {
464 	case IGMP_DELAYING_MEMBER:
465 	case IGMP_IDLE_MEMBER:
466 		if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
467 		    (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
468 			if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
469 				igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
470 		break;
471 	case IGMP_LAZY_MEMBER:
472 	case IGMP_AWAKENING_MEMBER:
473 	case IGMP_SLEEPING_MEMBER:
474 		break;
475 	}
476 }
477 
478 void
479 igmp_fasttimo(void)
480 {
481 	struct in_multi *inm;
482 	struct in_multistep step;
483 
484 	/*
485 	 * Quick check to see if any work needs to be done, in order
486 	 * to minimize the overhead of fasttimo processing.
487 	 */
488 	if (!igmp_timers_are_running)
489 		return;
490 
491 	mutex_enter(softnet_lock);
492 	KERNEL_LOCK(1, NULL);
493 
494 	igmp_timers_are_running = 0;
495 	IN_FIRST_MULTI(step, inm);
496 	while (inm != NULL) {
497 		if (inm->inm_timer == 0) {
498 			/* do nothing */
499 		} else if (--inm->inm_timer == 0) {
500 			if (inm->inm_state == IGMP_DELAYING_MEMBER) {
501 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
502 					igmp_sendpkt(inm,
503 					    IGMP_v1_HOST_MEMBERSHIP_REPORT);
504 				else
505 					igmp_sendpkt(inm,
506 					    IGMP_v2_HOST_MEMBERSHIP_REPORT);
507 				inm->inm_state = IGMP_IDLE_MEMBER;
508 			}
509 		} else {
510 			igmp_timers_are_running = 1;
511 		}
512 		IN_NEXT_MULTI(step, inm);
513 	}
514 
515 	KERNEL_UNLOCK_ONE(NULL);
516 	mutex_exit(softnet_lock);
517 }
518 
519 void
520 igmp_slowtimo(void)
521 {
522 	struct router_info *rti;
523 
524 	mutex_enter(softnet_lock);
525 	KERNEL_LOCK(1, NULL);
526 	LIST_FOREACH(rti, &rti_head, rti_link) {
527 		if (rti->rti_type == IGMP_v1_ROUTER &&
528 		    ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
529 			rti->rti_type = IGMP_v2_ROUTER;
530 		}
531 	}
532 	KERNEL_UNLOCK_ONE(NULL);
533 	mutex_exit(softnet_lock);
534 }
535 
536 void
537 igmp_sendpkt(struct in_multi *inm, int type)
538 {
539 	struct mbuf *m;
540 	struct igmp *igmp;
541 	struct ip *ip;
542 	struct ip_moptions imo;
543 #ifdef MROUTING
544 	extern struct socket *ip_mrouter;
545 #endif /* MROUTING */
546 
547 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
548 	if (m == NULL)
549 		return;
550 	/*
551 	 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
552 	 * is smaller than mbuf size returned by MGETHDR.
553 	 */
554 	m->m_data += max_linkhdr;
555 	m->m_len = sizeof(struct ip) + IGMP_MINLEN;
556 	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
557 
558 	ip = mtod(m, struct ip *);
559 	ip->ip_tos = 0;
560 	ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
561 	ip->ip_off = htons(0);
562 	ip->ip_p = IPPROTO_IGMP;
563 	ip->ip_src = zeroin_addr;
564 	ip->ip_dst = inm->inm_addr;
565 
566 	m->m_data += sizeof(struct ip);
567 	m->m_len -= sizeof(struct ip);
568 	igmp = mtod(m, struct igmp *);
569 	igmp->igmp_type = type;
570 	igmp->igmp_code = 0;
571 	igmp->igmp_group = inm->inm_addr;
572 	igmp->igmp_cksum = 0;
573 	igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
574 	m->m_data -= sizeof(struct ip);
575 	m->m_len += sizeof(struct ip);
576 
577 	imo.imo_multicast_ifp = inm->inm_ifp;
578 	imo.imo_multicast_ttl = 1;
579 #ifdef RSVP_ISI
580 	imo.imo_multicast_vif = -1;
581 #endif
582 	/*
583 	 * Request loopback of the report if we are acting as a multicast
584 	 * router, so that the process-level routing demon can hear it.
585 	 */
586 #ifdef MROUTING
587 	imo.imo_multicast_loop = (ip_mrouter != NULL);
588 #else
589 	imo.imo_multicast_loop = 0;
590 #endif /* MROUTING */
591 
592 	ip_output(m, NULL, NULL, IP_MULTICASTOPTS, &imo, NULL);
593 
594 	IGMP_STATINC(IGMP_STAT_SND_REPORTS);
595 }
596 
597 void
598 igmp_purgeif(struct ifnet *ifp)	/* MUST be called at splsoftnet() */
599 {
600 	rti_delete(ifp);	/* manipulates pools */
601 }
602 
603 static int
604 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
605 {
606 
607 	return (NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS));
608 }
609 
610 SYSCTL_SETUP(sysctl_net_inet_igmp_setup, "sysctl net.inet.igmp subtree setup")
611 {
612 
613 	sysctl_createv(clog, 0, NULL, NULL,
614 			CTLFLAG_PERMANENT,
615 			CTLTYPE_NODE, "net", NULL,
616 			NULL, 0, NULL, 0,
617 			CTL_NET, CTL_EOL);
618 	sysctl_createv(clog, 0, NULL, NULL,
619 			CTLFLAG_PERMANENT,
620 			CTLTYPE_NODE, "inet", NULL,
621 			NULL, 0, NULL, 0,
622 			CTL_NET, PF_INET, CTL_EOL);
623 	sysctl_createv(clog, 0, NULL, NULL,
624 			CTLFLAG_PERMANENT,
625 			CTLTYPE_NODE, "igmp",
626 			SYSCTL_DESCR("Internet Group Management Protocol"),
627 			NULL, 0, NULL, 0,
628 			CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL);
629 
630 	sysctl_createv(clog, 0, NULL, NULL,
631 			CTLFLAG_PERMANENT,
632 			CTLTYPE_STRUCT, "stats",
633 			SYSCTL_DESCR("IGMP statistics"),
634 			sysctl_net_inet_igmp_stats, 0, NULL, 0,
635 			CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL);
636 }
637