igmp.c revision 1.60 1 /* $NetBSD: igmp.c,v 1.60 2016/06/28 02:02:56 ozaki-r 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.60 2016/06/28 02:02:56 ozaki-r Exp $");
44
45 #ifdef _KERNEL_OPT
46 #include "opt_mrouting.h"
47 #endif
48
49 #include <sys/param.h>
50 #include <sys/mbuf.h>
51 #include <sys/socket.h>
52 #include <sys/socketvar.h>
53 #include <sys/protosw.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 IFP_TO_IA(ifp, ia); /* XXX */
365 if (ia)
366 ip->ip_src.s_addr = ia->ia_subnet;
367 }
368
369 /*
370 * If we belong to the group being reported, stop
371 * our timer for that group.
372 */
373 in_multi_lock(RW_WRITER);
374 inm = in_lookup_multi(igmp->igmp_group, ifp);
375 if (inm != NULL) {
376 inm->inm_timer = 0;
377 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
378
379 switch (inm->inm_state) {
380 case IGMP_IDLE_MEMBER:
381 case IGMP_LAZY_MEMBER:
382 case IGMP_AWAKENING_MEMBER:
383 case IGMP_SLEEPING_MEMBER:
384 inm->inm_state = IGMP_SLEEPING_MEMBER;
385 break;
386 case IGMP_DELAYING_MEMBER:
387 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
388 inm->inm_state = IGMP_LAZY_MEMBER;
389 else
390 inm->inm_state = IGMP_SLEEPING_MEMBER;
391 break;
392 }
393 }
394 in_multi_unlock();
395 break;
396
397 case IGMP_v2_HOST_MEMBERSHIP_REPORT:
398 #ifdef MROUTING
399 /*
400 * Make sure we don't hear our own membership report. Fast
401 * leave requires knowing that we are the only member of a
402 * group.
403 */
404 IFP_TO_IA(ifp, ia); /* XXX */
405 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr))
406 break;
407 #endif
408
409 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
410
411 if (ifp->if_flags & IFF_LOOPBACK)
412 break;
413
414 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
415 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
416 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
417 goto drop;
418 }
419
420 /*
421 * KLUDGE: if the IP source address of the report has an
422 * unspecified (i.e., zero) subnet number, as is allowed for
423 * a booting host, replace it with the correct subnet number
424 * so that a process-level multicast routing daemon can
425 * determine which subnet it arrived from. This is necessary
426 * to compensate for the lack of any way for a process to
427 * determine the arrival interface of an incoming packet.
428 */
429 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
430 #ifndef MROUTING
431 IFP_TO_IA(ifp, ia); /* XXX */
432 #endif
433 if (ia)
434 ip->ip_src.s_addr = ia->ia_subnet;
435 }
436
437 /*
438 * If we belong to the group being reported, stop
439 * our timer for that group.
440 */
441 in_multi_lock(RW_WRITER);
442 inm = in_lookup_multi(igmp->igmp_group, ifp);
443 if (inm != NULL) {
444 inm->inm_timer = 0;
445 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
446
447 switch (inm->inm_state) {
448 case IGMP_DELAYING_MEMBER:
449 case IGMP_IDLE_MEMBER:
450 case IGMP_AWAKENING_MEMBER:
451 inm->inm_state = IGMP_LAZY_MEMBER;
452 break;
453 case IGMP_LAZY_MEMBER:
454 case IGMP_SLEEPING_MEMBER:
455 break;
456 }
457 }
458 in_multi_unlock();
459 break;
460
461 }
462 m_put_rcvif_psref(ifp, &psref);
463
464 /*
465 * Pass all valid IGMP packets up to any process(es) listening
466 * on a raw IGMP socket.
467 */
468 rip_input(m, iphlen, proto);
469 return;
470
471 drop:
472 m_put_rcvif_psref(ifp, &psref);
473 m_freem(m);
474 return;
475 }
476
477 int
478 igmp_joingroup(struct in_multi *inm)
479 {
480 KASSERT(in_multi_lock_held());
481 inm->inm_state = IGMP_IDLE_MEMBER;
482
483 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
484 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
485 int report_type;
486
487 report_type = rti_fill(inm);
488 if (report_type == 0) {
489 return ENOMEM;
490 }
491 igmp_sendpkt(inm, report_type);
492 inm->inm_state = IGMP_DELAYING_MEMBER;
493 inm->inm_timer = IGMP_RANDOM_DELAY(
494 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
495 igmp_timers_on = true;
496 } else
497 inm->inm_timer = 0;
498
499 return 0;
500 }
501
502 void
503 igmp_leavegroup(struct in_multi *inm)
504 {
505 KASSERT(in_multi_lock_held());
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 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
512 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
513 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
514 break;
515 case IGMP_LAZY_MEMBER:
516 case IGMP_AWAKENING_MEMBER:
517 case IGMP_SLEEPING_MEMBER:
518 break;
519 }
520 }
521
522 void
523 igmp_fasttimo(void)
524 {
525 struct in_multi *inm;
526 struct in_multistep step;
527
528 /*
529 * Quick check to see if any work needs to be done, in order
530 * to minimize the overhead of fasttimo processing.
531 */
532 if (!igmp_timers_on) {
533 return;
534 }
535
536 /* XXX: Needed for ip_output(). */
537 mutex_enter(softnet_lock);
538
539 in_multi_lock(RW_WRITER);
540 igmp_timers_on = false;
541 inm = in_first_multi(&step);
542 while (inm != NULL) {
543 if (inm->inm_timer == 0) {
544 /* do nothing */
545 } else if (--inm->inm_timer == 0) {
546 if (inm->inm_state == IGMP_DELAYING_MEMBER) {
547 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
548 igmp_sendpkt(inm,
549 IGMP_v1_HOST_MEMBERSHIP_REPORT);
550 else
551 igmp_sendpkt(inm,
552 IGMP_v2_HOST_MEMBERSHIP_REPORT);
553 inm->inm_state = IGMP_IDLE_MEMBER;
554 }
555 } else {
556 igmp_timers_on = true;
557 }
558 inm = in_next_multi(&step);
559 }
560 in_multi_unlock();
561 mutex_exit(softnet_lock);
562 }
563
564 void
565 igmp_slowtimo(void)
566 {
567 router_info_t *rti;
568
569 in_multi_lock(RW_WRITER);
570 LIST_FOREACH(rti, &rti_head, rti_link) {
571 if (rti->rti_type == IGMP_v1_ROUTER &&
572 ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
573 rti->rti_type = IGMP_v2_ROUTER;
574 }
575 }
576 in_multi_unlock();
577 }
578
579 /*
580 * igmp_sendpkt: construct an IGMP packet, given the multicast structure
581 * and the type, and send the datagram.
582 */
583 static void
584 igmp_sendpkt(struct in_multi *inm, int type)
585 {
586 struct mbuf *m;
587 struct igmp *igmp;
588 struct ip *ip;
589 struct ip_moptions imo;
590
591 KASSERT(in_multi_lock_held());
592
593 MGETHDR(m, M_DONTWAIT, MT_HEADER);
594 if (m == NULL)
595 return;
596
597 /*
598 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
599 * is smaller than mbuf size returned by MGETHDR.
600 */
601 m->m_data += max_linkhdr;
602 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
603 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
604
605 ip = mtod(m, struct ip *);
606 ip->ip_tos = 0;
607 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
608 ip->ip_off = htons(0);
609 ip->ip_p = IPPROTO_IGMP;
610 ip->ip_src = zeroin_addr;
611 ip->ip_dst = inm->inm_addr;
612
613 m->m_data += sizeof(struct ip);
614 m->m_len -= sizeof(struct ip);
615 igmp = mtod(m, struct igmp *);
616 igmp->igmp_type = type;
617 igmp->igmp_code = 0;
618 igmp->igmp_group = inm->inm_addr;
619 igmp->igmp_cksum = 0;
620 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
621 m->m_data -= sizeof(struct ip);
622 m->m_len += sizeof(struct ip);
623
624 imo.imo_multicast_if_index = if_get_index(inm->inm_ifp);
625 imo.imo_multicast_ttl = 1;
626 #ifdef RSVP_ISI
627 imo.imo_multicast_vif = -1;
628 #endif
629 /*
630 * Request loopback of the report if we are acting as a multicast
631 * router, so that the process-level routing demon can hear it.
632 */
633 #ifdef MROUTING
634 extern struct socket *ip_mrouter;
635 imo.imo_multicast_loop = (ip_mrouter != NULL);
636 #else
637 imo.imo_multicast_loop = 0;
638 #endif
639
640 /*
641 * Note: IP_IGMP_MCAST indicates that in_multilock is held.
642 * The caller must still acquire softnet_lock for ip_output().
643 */
644 KASSERT(mutex_owned(softnet_lock));
645 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL);
646 IGMP_STATINC(IGMP_STAT_SND_REPORTS);
647 }
648
649 void
650 igmp_purgeif(ifnet_t *ifp)
651 {
652 in_multi_lock(RW_WRITER);
653 rti_delete(ifp);
654 in_multi_unlock();
655 }
656
657 static int
658 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
659 {
660 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS);
661 }
662
663 static void
664 sysctl_net_inet_igmp_setup(struct sysctllog **clog)
665 {
666 sysctl_createv(clog, 0, NULL, NULL,
667 CTLFLAG_PERMANENT,
668 CTLTYPE_NODE, "inet", NULL,
669 NULL, 0, NULL, 0,
670 CTL_NET, PF_INET, CTL_EOL);
671 sysctl_createv(clog, 0, NULL, NULL,
672 CTLFLAG_PERMANENT,
673 CTLTYPE_NODE, "igmp",
674 SYSCTL_DESCR("Internet Group Management Protocol"),
675 NULL, 0, NULL, 0,
676 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL);
677 sysctl_createv(clog, 0, NULL, NULL,
678 CTLFLAG_PERMANENT,
679 CTLTYPE_STRUCT, "stats",
680 SYSCTL_DESCR("IGMP statistics"),
681 sysctl_net_inet_igmp_stats, 0, NULL, 0,
682 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL);
683 }
684