1 /* $NetBSD: igmp.c,v 1.61 2016/07/08 04:33:30 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.61 2016/07/08 04:33:30 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 ia = in_get_ia_from_ifp(ifp); /* 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 ia = in_get_ia_from_ifp(ifp); /* 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 ia = in_get_ia_from_ifp(ifp); /* 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