1 /* $NetBSD: igmp.c,v 1.57 2016/04/26 08:44:44 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.57 2016/04/26 08:44:44 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 = m->m_pkthdr.rcvif; 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 196 va_start(ap, m); 197 iphlen = va_arg(ap, int); 198 proto = va_arg(ap, int); 199 va_end(ap); 200 201 IGMP_STATINC(IGMP_STAT_RCV_TOTAL); 202 203 /* 204 * Validate lengths 205 */ 206 minlen = iphlen + IGMP_MINLEN; 207 ip_len = ntohs(ip->ip_len); 208 if (ip_len < minlen) { 209 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); 210 m_freem(m); 211 return; 212 } 213 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0) 214 || m->m_len < minlen) { 215 if ((m = m_pullup(m, minlen)) == NULL) { 216 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); 217 return; 218 } 219 ip = mtod(m, struct ip *); 220 } 221 222 /* 223 * Validate checksum 224 */ 225 m->m_data += iphlen; 226 m->m_len -= iphlen; 227 igmp = mtod(m, struct igmp *); 228 /* No need to assert alignment here. */ 229 if (in_cksum(m, ip_len - iphlen)) { 230 IGMP_STATINC(IGMP_STAT_RCV_BADSUM); 231 m_freem(m); 232 return; 233 } 234 m->m_data -= iphlen; 235 m->m_len += iphlen; 236 237 switch (igmp->igmp_type) { 238 239 case IGMP_HOST_MEMBERSHIP_QUERY: 240 IGMP_STATINC(IGMP_STAT_RCV_QUERIES); 241 242 if (ifp->if_flags & IFF_LOOPBACK) 243 break; 244 245 if (igmp->igmp_code == 0) { 246 struct in_multistep step; 247 router_info_t *rti; 248 249 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { 250 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES); 251 m_freem(m); 252 return; 253 } 254 255 in_multi_lock(RW_WRITER); 256 rti = rti_find(ifp); 257 if (rti == NULL) { 258 in_multi_unlock(); 259 break; 260 } 261 rti->rti_type = IGMP_v1_ROUTER; 262 rti->rti_age = 0; 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). 269 */ 270 271 inm = in_first_multi(&step); 272 while (inm != NULL) { 273 if (inm->inm_ifp == ifp && 274 inm->inm_timer == 0 && 275 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 276 inm->inm_state = IGMP_DELAYING_MEMBER; 277 inm->inm_timer = IGMP_RANDOM_DELAY( 278 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 279 igmp_timers_on = true; 280 } 281 inm = in_next_multi(&step); 282 } 283 in_multi_unlock(); 284 } else { 285 struct in_multistep step; 286 287 if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 288 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES); 289 m_freem(m); 290 return; 291 } 292 293 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 294 if (timer == 0) 295 timer = 1; 296 297 /* 298 * Start the timers in all of our membership records 299 * for the interface on which the query arrived, 300 * except those that are already running and those 301 * that belong to a "local" group (224.0.0.X). For 302 * timers already running, check if they need to be 303 * reset. 304 */ 305 in_multi_lock(RW_WRITER); 306 inm = in_first_multi(&step); 307 while (inm != NULL) { 308 if (inm->inm_ifp == ifp && 309 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 310 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 311 in_hosteq(ip->ip_dst, inm->inm_addr))) { 312 switch (inm->inm_state) { 313 case IGMP_DELAYING_MEMBER: 314 if (inm->inm_timer <= timer) 315 break; 316 /* FALLTHROUGH */ 317 case IGMP_IDLE_MEMBER: 318 case IGMP_LAZY_MEMBER: 319 case IGMP_AWAKENING_MEMBER: 320 inm->inm_state = 321 IGMP_DELAYING_MEMBER; 322 inm->inm_timer = 323 IGMP_RANDOM_DELAY(timer); 324 igmp_timers_on = true; 325 break; 326 case IGMP_SLEEPING_MEMBER: 327 inm->inm_state = 328 IGMP_AWAKENING_MEMBER; 329 break; 330 } 331 } 332 inm = in_next_multi(&step); 333 } 334 in_multi_unlock(); 335 } 336 337 break; 338 339 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 340 IGMP_STATINC(IGMP_STAT_RCV_REPORTS); 341 342 if (ifp->if_flags & IFF_LOOPBACK) 343 break; 344 345 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 346 !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 347 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); 348 m_freem(m); 349 return; 350 } 351 352 /* 353 * KLUDGE: if the IP source address of the report has an 354 * unspecified (i.e., zero) subnet number, as is allowed for 355 * a booting host, replace it with the correct subnet number 356 * so that a process-level multicast routing daemon can 357 * determine which subnet it arrived from. This is necessary 358 * to compensate for the lack of any way for a process to 359 * determine the arrival interface of an incoming packet. 360 */ 361 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 362 IFP_TO_IA(ifp, ia); /* XXX */ 363 if (ia) 364 ip->ip_src.s_addr = ia->ia_subnet; 365 } 366 367 /* 368 * If we belong to the group being reported, stop 369 * our timer for that group. 370 */ 371 in_multi_lock(RW_WRITER); 372 inm = in_lookup_multi(igmp->igmp_group, ifp); 373 if (inm != NULL) { 374 inm->inm_timer = 0; 375 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 376 377 switch (inm->inm_state) { 378 case IGMP_IDLE_MEMBER: 379 case IGMP_LAZY_MEMBER: 380 case IGMP_AWAKENING_MEMBER: 381 case IGMP_SLEEPING_MEMBER: 382 inm->inm_state = IGMP_SLEEPING_MEMBER; 383 break; 384 case IGMP_DELAYING_MEMBER: 385 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 386 inm->inm_state = IGMP_LAZY_MEMBER; 387 else 388 inm->inm_state = IGMP_SLEEPING_MEMBER; 389 break; 390 } 391 } 392 in_multi_unlock(); 393 break; 394 395 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 396 #ifdef MROUTING 397 /* 398 * Make sure we don't hear our own membership report. Fast 399 * leave requires knowing that we are the only member of a 400 * group. 401 */ 402 IFP_TO_IA(ifp, ia); /* XXX */ 403 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) 404 break; 405 #endif 406 407 IGMP_STATINC(IGMP_STAT_RCV_REPORTS); 408 409 if (ifp->if_flags & IFF_LOOPBACK) 410 break; 411 412 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 413 !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 414 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); 415 m_freem(m); 416 return; 417 } 418 419 /* 420 * KLUDGE: if the IP source address of the report has an 421 * unspecified (i.e., zero) subnet number, as is allowed for 422 * a booting host, replace it with the correct subnet number 423 * so that a process-level multicast routing daemon can 424 * determine which subnet it arrived from. This is necessary 425 * to compensate for the lack of any way for a process to 426 * determine the arrival interface of an incoming packet. 427 */ 428 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 429 #ifndef MROUTING 430 IFP_TO_IA(ifp, ia); /* XXX */ 431 #endif 432 if (ia) 433 ip->ip_src.s_addr = ia->ia_subnet; 434 } 435 436 /* 437 * If we belong to the group being reported, stop 438 * our timer for that group. 439 */ 440 in_multi_lock(RW_WRITER); 441 inm = in_lookup_multi(igmp->igmp_group, ifp); 442 if (inm != NULL) { 443 inm->inm_timer = 0; 444 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 445 446 switch (inm->inm_state) { 447 case IGMP_DELAYING_MEMBER: 448 case IGMP_IDLE_MEMBER: 449 case IGMP_AWAKENING_MEMBER: 450 inm->inm_state = IGMP_LAZY_MEMBER; 451 break; 452 case IGMP_LAZY_MEMBER: 453 case IGMP_SLEEPING_MEMBER: 454 break; 455 } 456 } 457 in_multi_unlock(); 458 break; 459 460 } 461 462 /* 463 * Pass all valid IGMP packets up to any process(es) listening 464 * on a raw IGMP socket. 465 */ 466 rip_input(m, iphlen, proto); 467 return; 468 } 469 470 int 471 igmp_joingroup(struct in_multi *inm) 472 { 473 KASSERT(in_multi_lock_held()); 474 inm->inm_state = IGMP_IDLE_MEMBER; 475 476 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 477 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { 478 int report_type; 479 480 report_type = rti_fill(inm); 481 if (report_type == 0) { 482 return ENOMEM; 483 } 484 igmp_sendpkt(inm, report_type); 485 inm->inm_state = IGMP_DELAYING_MEMBER; 486 inm->inm_timer = IGMP_RANDOM_DELAY( 487 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 488 igmp_timers_on = true; 489 } else 490 inm->inm_timer = 0; 491 492 return 0; 493 } 494 495 void 496 igmp_leavegroup(struct in_multi *inm) 497 { 498 KASSERT(in_multi_lock_held()); 499 500 switch (inm->inm_state) { 501 case IGMP_DELAYING_MEMBER: 502 case IGMP_IDLE_MEMBER: 503 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 504 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) 505 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 506 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 507 break; 508 case IGMP_LAZY_MEMBER: 509 case IGMP_AWAKENING_MEMBER: 510 case IGMP_SLEEPING_MEMBER: 511 break; 512 } 513 } 514 515 void 516 igmp_fasttimo(void) 517 { 518 struct in_multi *inm; 519 struct in_multistep step; 520 521 /* 522 * Quick check to see if any work needs to be done, in order 523 * to minimize the overhead of fasttimo processing. 524 */ 525 if (!igmp_timers_on) { 526 return; 527 } 528 529 /* XXX: Needed for ip_output(). */ 530 mutex_enter(softnet_lock); 531 532 in_multi_lock(RW_WRITER); 533 igmp_timers_on = false; 534 inm = in_first_multi(&step); 535 while (inm != NULL) { 536 if (inm->inm_timer == 0) { 537 /* do nothing */ 538 } else if (--inm->inm_timer == 0) { 539 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 540 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 541 igmp_sendpkt(inm, 542 IGMP_v1_HOST_MEMBERSHIP_REPORT); 543 else 544 igmp_sendpkt(inm, 545 IGMP_v2_HOST_MEMBERSHIP_REPORT); 546 inm->inm_state = IGMP_IDLE_MEMBER; 547 } 548 } else { 549 igmp_timers_on = true; 550 } 551 inm = in_next_multi(&step); 552 } 553 in_multi_unlock(); 554 mutex_exit(softnet_lock); 555 } 556 557 void 558 igmp_slowtimo(void) 559 { 560 router_info_t *rti; 561 562 in_multi_lock(RW_WRITER); 563 LIST_FOREACH(rti, &rti_head, rti_link) { 564 if (rti->rti_type == IGMP_v1_ROUTER && 565 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 566 rti->rti_type = IGMP_v2_ROUTER; 567 } 568 } 569 in_multi_unlock(); 570 } 571 572 /* 573 * igmp_sendpkt: construct an IGMP packet, given the multicast structure 574 * and the type, and send the datagram. 575 */ 576 static void 577 igmp_sendpkt(struct in_multi *inm, int type) 578 { 579 struct mbuf *m; 580 struct igmp *igmp; 581 struct ip *ip; 582 struct ip_moptions imo; 583 584 KASSERT(in_multi_lock_held()); 585 586 MGETHDR(m, M_DONTWAIT, MT_HEADER); 587 if (m == NULL) 588 return; 589 590 /* 591 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 592 * is smaller than mbuf size returned by MGETHDR. 593 */ 594 m->m_data += max_linkhdr; 595 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 596 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 597 598 ip = mtod(m, struct ip *); 599 ip->ip_tos = 0; 600 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 601 ip->ip_off = htons(0); 602 ip->ip_p = IPPROTO_IGMP; 603 ip->ip_src = zeroin_addr; 604 ip->ip_dst = inm->inm_addr; 605 606 m->m_data += sizeof(struct ip); 607 m->m_len -= sizeof(struct ip); 608 igmp = mtod(m, struct igmp *); 609 igmp->igmp_type = type; 610 igmp->igmp_code = 0; 611 igmp->igmp_group = inm->inm_addr; 612 igmp->igmp_cksum = 0; 613 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 614 m->m_data -= sizeof(struct ip); 615 m->m_len += sizeof(struct ip); 616 617 imo.imo_multicast_ifp = inm->inm_ifp; 618 imo.imo_multicast_ttl = 1; 619 #ifdef RSVP_ISI 620 imo.imo_multicast_vif = -1; 621 #endif 622 /* 623 * Request loopback of the report if we are acting as a multicast 624 * router, so that the process-level routing demon can hear it. 625 */ 626 #ifdef MROUTING 627 extern struct socket *ip_mrouter; 628 imo.imo_multicast_loop = (ip_mrouter != NULL); 629 #else 630 imo.imo_multicast_loop = 0; 631 #endif 632 633 /* 634 * Note: IP_IGMP_MCAST indicates that in_multilock is held. 635 * The caller must still acquire softnet_lock for ip_output(). 636 */ 637 KASSERT(mutex_owned(softnet_lock)); 638 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL); 639 IGMP_STATINC(IGMP_STAT_SND_REPORTS); 640 } 641 642 void 643 igmp_purgeif(ifnet_t *ifp) 644 { 645 in_multi_lock(RW_WRITER); 646 rti_delete(ifp); 647 in_multi_unlock(); 648 } 649 650 static int 651 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS) 652 { 653 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS); 654 } 655 656 static void 657 sysctl_net_inet_igmp_setup(struct sysctllog **clog) 658 { 659 sysctl_createv(clog, 0, NULL, NULL, 660 CTLFLAG_PERMANENT, 661 CTLTYPE_NODE, "inet", NULL, 662 NULL, 0, NULL, 0, 663 CTL_NET, PF_INET, CTL_EOL); 664 sysctl_createv(clog, 0, NULL, NULL, 665 CTLFLAG_PERMANENT, 666 CTLTYPE_NODE, "igmp", 667 SYSCTL_DESCR("Internet Group Management Protocol"), 668 NULL, 0, NULL, 0, 669 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL); 670 sysctl_createv(clog, 0, NULL, NULL, 671 CTLFLAG_PERMANENT, 672 CTLTYPE_STRUCT, "stats", 673 SYSCTL_DESCR("IGMP statistics"), 674 sysctl_net_inet_igmp_stats, 0, NULL, 0, 675 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL); 676 } 677