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