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