igmp.c revision 1.67 1 /* $NetBSD: igmp.c,v 1.67 2018/04/10 08:22:35 maxv 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.67 2018/04/10 08:22:35 maxv Exp $");
44
45 #ifdef _KERNEL_OPT
46 #include "opt_mrouting.h"
47 #include "opt_net_mpsafe.h"
48 #endif
49
50 #include <sys/param.h>
51 #include <sys/mbuf.h>
52 #include <sys/socket.h>
53 #include <sys/socketvar.h>
54 #include <sys/systm.h>
55 #include <sys/cprng.h>
56 #include <sys/sysctl.h>
57
58 #include <net/if.h>
59 #include <net/net_stats.h>
60
61 #include <netinet/in.h>
62 #include <netinet/in_var.h>
63 #include <netinet/in_systm.h>
64 #include <netinet/ip.h>
65 #include <netinet/ip_var.h>
66 #include <netinet/igmp.h>
67 #include <netinet/igmp_var.h>
68
69 /*
70 * Per-interface router version information.
71 */
72 typedef struct router_info {
73 LIST_ENTRY(router_info) rti_link;
74 ifnet_t * rti_ifp;
75 int rti_type; /* type of router on this interface */
76 int rti_age; /* time since last v1 query */
77 } router_info_t;
78
79 /*
80 * The router-info list and the timer flag are protected by in_multilock.
81 *
82 * Lock order:
83 *
84 * softnet_lock ->
85 * in_multilock
86 */
87 static struct pool igmp_rti_pool __cacheline_aligned;
88 static LIST_HEAD(, router_info) rti_head __cacheline_aligned;
89 static int igmp_timers_on __cacheline_aligned;
90 static percpu_t * igmpstat_percpu __read_mostly;
91
92 #define IGMP_STATINC(x) _NET_STATINC(igmpstat_percpu, x)
93
94 static void igmp_sendpkt(struct in_multi *, int);
95 static int rti_fill(struct in_multi *);
96 static router_info_t * rti_find(struct ifnet *);
97 static void rti_delete(struct ifnet *);
98 static void sysctl_net_inet_igmp_setup(struct sysctllog **);
99
100 /*
101 * rti_fill: associate router information with the given multicast group;
102 * if there is no router information for the interface, then create it.
103 */
104 static int
105 rti_fill(struct in_multi *inm)
106 {
107 router_info_t *rti;
108
109 KASSERT(in_multi_lock_held());
110
111 LIST_FOREACH(rti, &rti_head, rti_link) {
112 if (rti->rti_ifp == inm->inm_ifp) {
113 inm->inm_rti = rti;
114 return rti->rti_type == IGMP_v1_ROUTER ?
115 IGMP_v1_HOST_MEMBERSHIP_REPORT :
116 IGMP_v2_HOST_MEMBERSHIP_REPORT;
117 }
118 }
119 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
120 if (rti == NULL) {
121 return 0;
122 }
123 rti->rti_ifp = inm->inm_ifp;
124 rti->rti_type = IGMP_v2_ROUTER;
125 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
126 inm->inm_rti = rti;
127 return IGMP_v2_HOST_MEMBERSHIP_REPORT;
128 }
129
130 /*
131 * rti_find: lookup or create router information for the given interface.
132 */
133 static router_info_t *
134 rti_find(ifnet_t *ifp)
135 {
136 router_info_t *rti;
137
138 KASSERT(in_multi_lock_held());
139
140 LIST_FOREACH(rti, &rti_head, rti_link) {
141 if (rti->rti_ifp == ifp)
142 return rti;
143 }
144 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
145 if (rti == NULL) {
146 return NULL;
147 }
148 rti->rti_ifp = ifp;
149 rti->rti_type = IGMP_v2_ROUTER;
150 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
151 return rti;
152 }
153
154 /*
155 * rti_delete: remove and free the router information entry for the
156 * given interface.
157 */
158 static void
159 rti_delete(ifnet_t *ifp)
160 {
161 router_info_t *rti;
162
163 KASSERT(in_multi_lock_held());
164
165 LIST_FOREACH(rti, &rti_head, rti_link) {
166 if (rti->rti_ifp == ifp) {
167 LIST_REMOVE(rti, rti_link);
168 pool_put(&igmp_rti_pool, rti);
169 break;
170 }
171 }
172 }
173
174 void
175 igmp_init(void)
176 {
177 pool_init(&igmp_rti_pool, sizeof(router_info_t), 0, 0, 0,
178 "igmppl", NULL, IPL_SOFTNET);
179 igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS);
180 sysctl_net_inet_igmp_setup(NULL);
181 LIST_INIT(&rti_head);
182 }
183
184 void
185 igmp_input(struct mbuf *m, ...)
186 {
187 ifnet_t *ifp;
188 struct ip *ip = mtod(m, struct ip *);
189 struct igmp *igmp;
190 u_int minlen, timer;
191 struct in_multi *inm;
192 struct in_ifaddr *ia;
193 int proto, ip_len, iphlen;
194 va_list ap;
195 struct psref psref;
196
197 va_start(ap, m);
198 iphlen = va_arg(ap, int);
199 proto = va_arg(ap, int);
200 va_end(ap);
201
202 IGMP_STATINC(IGMP_STAT_RCV_TOTAL);
203
204 /*
205 * Validate lengths
206 */
207 minlen = iphlen + IGMP_MINLEN;
208 ip_len = ntohs(ip->ip_len);
209 if (ip_len < minlen) {
210 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
211 m_freem(m);
212 return;
213 }
214 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
215 || m->m_len < minlen) {
216 if ((m = m_pullup(m, minlen)) == NULL) {
217 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
218 return;
219 }
220 ip = mtod(m, struct ip *);
221 }
222
223 /*
224 * Validate checksum
225 */
226 m->m_data += iphlen;
227 m->m_len -= iphlen;
228 igmp = mtod(m, struct igmp *);
229 /* No need to assert alignment here. */
230 if (in_cksum(m, ip_len - iphlen)) {
231 IGMP_STATINC(IGMP_STAT_RCV_BADSUM);
232 m_freem(m);
233 return;
234 }
235 m->m_data -= iphlen;
236 m->m_len += iphlen;
237
238 ifp = m_get_rcvif_psref(m, &psref);
239 if (__predict_false(ifp == NULL))
240 goto drop;
241
242 switch (igmp->igmp_type) {
243
244 case IGMP_HOST_MEMBERSHIP_QUERY:
245 IGMP_STATINC(IGMP_STAT_RCV_QUERIES);
246
247 if (ifp->if_flags & IFF_LOOPBACK)
248 break;
249
250 if (igmp->igmp_code == 0) {
251 struct in_multistep step;
252 router_info_t *rti;
253
254 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
255 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
256 goto drop;
257 }
258
259 in_multi_lock(RW_WRITER);
260 rti = rti_find(ifp);
261 if (rti == NULL) {
262 in_multi_unlock();
263 break;
264 }
265 rti->rti_type = IGMP_v1_ROUTER;
266 rti->rti_age = 0;
267
268 /*
269 * Start the timers in all of our membership records
270 * for the interface on which the query arrived,
271 * except those that are already running and those
272 * that belong to a "local" group (224.0.0.X).
273 */
274
275 inm = in_first_multi(&step);
276 while (inm != NULL) {
277 if (inm->inm_ifp == ifp &&
278 inm->inm_timer == 0 &&
279 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
280 inm->inm_state = IGMP_DELAYING_MEMBER;
281 inm->inm_timer = IGMP_RANDOM_DELAY(
282 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
283 igmp_timers_on = true;
284 }
285 inm = in_next_multi(&step);
286 }
287 in_multi_unlock();
288 } else {
289 struct in_multistep step;
290
291 if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
292 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
293 goto drop;
294 }
295
296 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
297 if (timer == 0)
298 timer = 1;
299
300 /*
301 * Start the timers in all of our membership records
302 * for the interface on which the query arrived,
303 * except those that are already running and those
304 * that belong to a "local" group (224.0.0.X). For
305 * timers already running, check if they need to be
306 * reset.
307 */
308 in_multi_lock(RW_WRITER);
309 inm = in_first_multi(&step);
310 while (inm != NULL) {
311 if (inm->inm_ifp == ifp &&
312 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
313 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
314 in_hosteq(ip->ip_dst, inm->inm_addr))) {
315 switch (inm->inm_state) {
316 case IGMP_DELAYING_MEMBER:
317 if (inm->inm_timer <= timer)
318 break;
319 /* FALLTHROUGH */
320 case IGMP_IDLE_MEMBER:
321 case IGMP_LAZY_MEMBER:
322 case IGMP_AWAKENING_MEMBER:
323 inm->inm_state =
324 IGMP_DELAYING_MEMBER;
325 inm->inm_timer =
326 IGMP_RANDOM_DELAY(timer);
327 igmp_timers_on = true;
328 break;
329 case IGMP_SLEEPING_MEMBER:
330 inm->inm_state =
331 IGMP_AWAKENING_MEMBER;
332 break;
333 }
334 }
335 inm = in_next_multi(&step);
336 }
337 in_multi_unlock();
338 }
339
340 break;
341
342 case IGMP_v1_HOST_MEMBERSHIP_REPORT:
343 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
344
345 if (ifp->if_flags & IFF_LOOPBACK)
346 break;
347
348 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
349 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
350 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
351 goto drop;
352 }
353
354 /*
355 * KLUDGE: if the IP source address of the report has an
356 * unspecified (i.e., zero) subnet number, as is allowed for
357 * a booting host, replace it with the correct subnet number
358 * so that a process-level multicast routing daemon can
359 * determine which subnet it arrived from. This is necessary
360 * to compensate for the lack of any way for a process to
361 * determine the arrival interface of an incoming packet.
362 */
363 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
364 int s = pserialize_read_enter();
365 ia = in_get_ia_from_ifp(ifp); /* XXX */
366 if (ia)
367 ip->ip_src.s_addr = ia->ia_subnet;
368 pserialize_read_exit(s);
369 }
370
371 /*
372 * If we belong to the group being reported, stop
373 * our timer for that group.
374 */
375 in_multi_lock(RW_WRITER);
376 inm = in_lookup_multi(igmp->igmp_group, ifp);
377 if (inm != NULL) {
378 inm->inm_timer = 0;
379 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
380
381 switch (inm->inm_state) {
382 case IGMP_IDLE_MEMBER:
383 case IGMP_LAZY_MEMBER:
384 case IGMP_AWAKENING_MEMBER:
385 case IGMP_SLEEPING_MEMBER:
386 inm->inm_state = IGMP_SLEEPING_MEMBER;
387 break;
388 case IGMP_DELAYING_MEMBER:
389 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
390 inm->inm_state = IGMP_LAZY_MEMBER;
391 else
392 inm->inm_state = IGMP_SLEEPING_MEMBER;
393 break;
394 }
395 }
396 in_multi_unlock();
397 break;
398
399 case IGMP_v2_HOST_MEMBERSHIP_REPORT: {
400 int s = pserialize_read_enter();
401 #ifdef MROUTING
402 /*
403 * Make sure we don't hear our own membership report. Fast
404 * leave requires knowing that we are the only member of a
405 * group.
406 */
407 ia = in_get_ia_from_ifp(ifp); /* XXX */
408 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) {
409 pserialize_read_exit(s);
410 break;
411 }
412 #endif
413
414 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
415
416 if (ifp->if_flags & IFF_LOOPBACK) {
417 pserialize_read_exit(s);
418 break;
419 }
420
421 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
422 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
423 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
424 pserialize_read_exit(s);
425 goto drop;
426 }
427
428 /*
429 * KLUDGE: if the IP source address of the report has an
430 * unspecified (i.e., zero) subnet number, as is allowed for
431 * a booting host, replace it with the correct subnet number
432 * so that a process-level multicast routing daemon can
433 * determine which subnet it arrived from. This is necessary
434 * to compensate for the lack of any way for a process to
435 * determine the arrival interface of an incoming packet.
436 */
437 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
438 #ifndef MROUTING
439 ia = in_get_ia_from_ifp(ifp); /* XXX */
440 #endif
441 if (ia)
442 ip->ip_src.s_addr = ia->ia_subnet;
443 }
444 pserialize_read_exit(s);
445
446 /*
447 * If we belong to the group being reported, stop
448 * our timer for that group.
449 */
450 in_multi_lock(RW_WRITER);
451 inm = in_lookup_multi(igmp->igmp_group, ifp);
452 if (inm != NULL) {
453 inm->inm_timer = 0;
454 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
455
456 switch (inm->inm_state) {
457 case IGMP_DELAYING_MEMBER:
458 case IGMP_IDLE_MEMBER:
459 case IGMP_AWAKENING_MEMBER:
460 inm->inm_state = IGMP_LAZY_MEMBER;
461 break;
462 case IGMP_LAZY_MEMBER:
463 case IGMP_SLEEPING_MEMBER:
464 break;
465 }
466 }
467 in_multi_unlock();
468 break;
469 }
470 }
471 m_put_rcvif_psref(ifp, &psref);
472
473 /*
474 * Pass all valid IGMP packets up to any process(es) listening
475 * on a raw IGMP socket.
476 */
477 rip_input(m, iphlen, proto);
478 return;
479
480 drop:
481 m_put_rcvif_psref(ifp, &psref);
482 m_freem(m);
483 return;
484 }
485
486 int
487 igmp_joingroup(struct in_multi *inm)
488 {
489 KASSERT(in_multi_lock_held());
490 inm->inm_state = IGMP_IDLE_MEMBER;
491
492 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
493 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
494 int report_type;
495
496 report_type = rti_fill(inm);
497 if (report_type == 0) {
498 return ENOMEM;
499 }
500 igmp_sendpkt(inm, report_type);
501 inm->inm_state = IGMP_DELAYING_MEMBER;
502 inm->inm_timer = IGMP_RANDOM_DELAY(
503 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
504 igmp_timers_on = true;
505 } else
506 inm->inm_timer = 0;
507
508 return 0;
509 }
510
511 void
512 igmp_leavegroup(struct in_multi *inm)
513 {
514 KASSERT(in_multi_lock_held());
515
516 switch (inm->inm_state) {
517 case IGMP_DELAYING_MEMBER:
518 case IGMP_IDLE_MEMBER:
519 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
520 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
521 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
522 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
523 break;
524 case IGMP_LAZY_MEMBER:
525 case IGMP_AWAKENING_MEMBER:
526 case IGMP_SLEEPING_MEMBER:
527 break;
528 }
529 }
530
531 void
532 igmp_fasttimo(void)
533 {
534 struct in_multi *inm;
535 struct in_multistep step;
536
537 /*
538 * Quick check to see if any work needs to be done, in order
539 * to minimize the overhead of fasttimo processing.
540 */
541 if (!igmp_timers_on) {
542 return;
543 }
544
545 /* XXX: Needed for ip_output(). */
546 SOFTNET_LOCK_UNLESS_NET_MPSAFE();
547
548 in_multi_lock(RW_WRITER);
549 igmp_timers_on = false;
550 inm = in_first_multi(&step);
551 while (inm != NULL) {
552 if (inm->inm_timer == 0) {
553 /* do nothing */
554 } else if (--inm->inm_timer == 0) {
555 if (inm->inm_state == IGMP_DELAYING_MEMBER) {
556 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
557 igmp_sendpkt(inm,
558 IGMP_v1_HOST_MEMBERSHIP_REPORT);
559 else
560 igmp_sendpkt(inm,
561 IGMP_v2_HOST_MEMBERSHIP_REPORT);
562 inm->inm_state = IGMP_IDLE_MEMBER;
563 }
564 } else {
565 igmp_timers_on = true;
566 }
567 inm = in_next_multi(&step);
568 }
569 in_multi_unlock();
570 SOFTNET_UNLOCK_UNLESS_NET_MPSAFE();
571 }
572
573 void
574 igmp_slowtimo(void)
575 {
576 router_info_t *rti;
577
578 in_multi_lock(RW_WRITER);
579 LIST_FOREACH(rti, &rti_head, rti_link) {
580 if (rti->rti_type == IGMP_v1_ROUTER &&
581 ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
582 rti->rti_type = IGMP_v2_ROUTER;
583 }
584 }
585 in_multi_unlock();
586 }
587
588 /*
589 * igmp_sendpkt: construct an IGMP packet, given the multicast structure
590 * and the type, and send the datagram.
591 */
592 static void
593 igmp_sendpkt(struct in_multi *inm, int type)
594 {
595 struct mbuf *m;
596 struct igmp *igmp;
597 struct ip *ip;
598 struct ip_moptions imo;
599
600 KASSERT(in_multi_lock_held());
601
602 MGETHDR(m, M_DONTWAIT, MT_HEADER);
603 if (m == NULL)
604 return;
605 KASSERT(max_linkhdr + sizeof(struct ip) + IGMP_MINLEN <= MHLEN);
606
607 m->m_data += max_linkhdr;
608 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
609 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
610
611 ip = mtod(m, struct ip *);
612 ip->ip_tos = 0;
613 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
614 ip->ip_off = htons(0);
615 ip->ip_p = IPPROTO_IGMP;
616 ip->ip_src = zeroin_addr;
617 ip->ip_dst = inm->inm_addr;
618
619 m->m_data += sizeof(struct ip);
620 m->m_len -= sizeof(struct ip);
621 igmp = mtod(m, struct igmp *);
622 igmp->igmp_type = type;
623 igmp->igmp_code = 0;
624 igmp->igmp_group = inm->inm_addr;
625 igmp->igmp_cksum = 0;
626 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
627 m->m_data -= sizeof(struct ip);
628 m->m_len += sizeof(struct ip);
629
630 imo.imo_multicast_if_index = if_get_index(inm->inm_ifp);
631 imo.imo_multicast_ttl = 1;
632
633 /*
634 * Request loopback of the report if we are acting as a multicast
635 * router, so that the process-level routing demon can hear it.
636 */
637 #ifdef MROUTING
638 extern struct socket *ip_mrouter;
639 imo.imo_multicast_loop = (ip_mrouter != NULL);
640 #else
641 imo.imo_multicast_loop = 0;
642 #endif
643
644 /*
645 * Note: IP_IGMP_MCAST indicates that in_multilock is held.
646 * The caller must still acquire softnet_lock for ip_output().
647 */
648 #ifndef NET_MPSAFE
649 KASSERT(mutex_owned(softnet_lock));
650 #endif
651 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL);
652 IGMP_STATINC(IGMP_STAT_SND_REPORTS);
653 }
654
655 void
656 igmp_purgeif(ifnet_t *ifp)
657 {
658 in_multi_lock(RW_WRITER);
659 rti_delete(ifp);
660 in_multi_unlock();
661 }
662
663 static int
664 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
665 {
666 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS);
667 }
668
669 static void
670 sysctl_net_inet_igmp_setup(struct sysctllog **clog)
671 {
672 sysctl_createv(clog, 0, NULL, NULL,
673 CTLFLAG_PERMANENT,
674 CTLTYPE_NODE, "inet", NULL,
675 NULL, 0, NULL, 0,
676 CTL_NET, PF_INET, CTL_EOL);
677 sysctl_createv(clog, 0, NULL, NULL,
678 CTLFLAG_PERMANENT,
679 CTLTYPE_NODE, "igmp",
680 SYSCTL_DESCR("Internet Group Management Protocol"),
681 NULL, 0, NULL, 0,
682 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL);
683 sysctl_createv(clog, 0, NULL, NULL,
684 CTLFLAG_PERMANENT,
685 CTLTYPE_STRUCT, "stats",
686 SYSCTL_DESCR("IGMP statistics"),
687 sysctl_net_inet_igmp_stats, 0, NULL, 0,
688 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL);
689 }
690