1 /* $NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv 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.70 2020/05/15 06:34:34 maxv Exp $"); 44 45 #ifdef _KERNEL_OPT 46 #include "opt_mrouting.h" 47 #include "opt_net_mpsafe.h" 48 #endif 49 50 #include <sys/param.h> 51 #include <sys/mbuf.h> 52 #include <sys/socket.h> 53 #include <sys/socketvar.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, int off, int proto) 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 ip_len, iphlen; 194 struct psref psref; 195 196 iphlen = off; 197 198 IGMP_STATINC(IGMP_STAT_RCV_TOTAL); 199 200 /* 201 * Validate lengths 202 */ 203 minlen = iphlen + IGMP_MINLEN; 204 ip_len = ntohs(ip->ip_len); 205 if (ip_len < minlen) { 206 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); 207 m_freem(m); 208 return; 209 } 210 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0) 211 || m->m_len < minlen) { 212 if ((m = m_pullup(m, minlen)) == NULL) { 213 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT); 214 return; 215 } 216 ip = mtod(m, struct ip *); 217 } 218 219 /* 220 * Validate checksum 221 */ 222 m->m_data += iphlen; 223 m->m_len -= iphlen; 224 igmp = mtod(m, struct igmp *); 225 /* No need to assert alignment here. */ 226 if (in_cksum(m, ip_len - iphlen)) { 227 IGMP_STATINC(IGMP_STAT_RCV_BADSUM); 228 m_freem(m); 229 return; 230 } 231 m->m_data -= iphlen; 232 m->m_len += iphlen; 233 234 ifp = m_get_rcvif_psref(m, &psref); 235 if (__predict_false(ifp == NULL)) 236 goto drop; 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 goto drop; 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 goto drop; 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 goto drop; 348 } 349 350 /* 351 * KLUDGE: if the IP source address of the report has an 352 * unspecified (i.e., zero) subnet number, as is allowed for 353 * a booting host, replace it with the correct subnet number 354 * so that a process-level multicast routing daemon can 355 * determine which subnet it arrived from. This is necessary 356 * to compensate for the lack of any way for a process to 357 * determine the arrival interface of an incoming packet. 358 */ 359 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 360 int s = pserialize_read_enter(); 361 ia = in_get_ia_from_ifp(ifp); /* XXX */ 362 if (ia) 363 ip->ip_src.s_addr = ia->ia_subnet; 364 pserialize_read_exit(s); 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 int s = pserialize_read_enter(); 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 ia = in_get_ia_from_ifp(ifp); /* XXX */ 404 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) { 405 pserialize_read_exit(s); 406 break; 407 } 408 #endif 409 410 IGMP_STATINC(IGMP_STAT_RCV_REPORTS); 411 412 if (ifp->if_flags & IFF_LOOPBACK) { 413 pserialize_read_exit(s); 414 break; 415 } 416 417 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 418 !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 419 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); 420 pserialize_read_exit(s); 421 goto drop; 422 } 423 424 /* 425 * KLUDGE: if the IP source address of the report has an 426 * unspecified (i.e., zero) subnet number, as is allowed for 427 * a booting host, replace it with the correct subnet number 428 * so that a process-level multicast routing daemon can 429 * determine which subnet it arrived from. This is necessary 430 * to compensate for the lack of any way for a process to 431 * determine the arrival interface of an incoming packet. 432 */ 433 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 434 #ifndef MROUTING 435 ia = in_get_ia_from_ifp(ifp); /* XXX */ 436 #endif 437 if (ia) 438 ip->ip_src.s_addr = ia->ia_subnet; 439 } 440 pserialize_read_exit(s); 441 442 /* 443 * If we belong to the group being reported, stop 444 * our timer for that group. 445 */ 446 in_multi_lock(RW_WRITER); 447 inm = in_lookup_multi(igmp->igmp_group, ifp); 448 if (inm != NULL) { 449 inm->inm_timer = 0; 450 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 451 452 switch (inm->inm_state) { 453 case IGMP_DELAYING_MEMBER: 454 case IGMP_IDLE_MEMBER: 455 case IGMP_AWAKENING_MEMBER: 456 inm->inm_state = IGMP_LAZY_MEMBER; 457 break; 458 case IGMP_LAZY_MEMBER: 459 case IGMP_SLEEPING_MEMBER: 460 break; 461 } 462 } 463 in_multi_unlock(); 464 break; 465 } 466 } 467 m_put_rcvif_psref(ifp, &psref); 468 469 /* 470 * Pass all valid IGMP packets up to any process(es) listening 471 * on a raw IGMP socket. 472 */ 473 /* 474 * Currently, igmp_input() is always called holding softnet_lock 475 * by ipintr()(!NET_MPSAFE) or PR_INPUT_WRAP()(NET_MPSAFE). 476 */ 477 KASSERT(mutex_owned(softnet_lock)); 478 rip_input(m, iphlen, proto); 479 return; 480 481 drop: 482 m_put_rcvif_psref(ifp, &psref); 483 m_freem(m); 484 return; 485 } 486 487 int 488 igmp_joingroup(struct in_multi *inm) 489 { 490 KASSERT(in_multi_lock_held()); 491 inm->inm_state = IGMP_IDLE_MEMBER; 492 493 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 494 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { 495 int report_type; 496 497 report_type = rti_fill(inm); 498 if (report_type == 0) { 499 return ENOMEM; 500 } 501 igmp_sendpkt(inm, report_type); 502 inm->inm_state = IGMP_DELAYING_MEMBER; 503 inm->inm_timer = IGMP_RANDOM_DELAY( 504 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 505 igmp_timers_on = true; 506 } else 507 inm->inm_timer = 0; 508 509 return 0; 510 } 511 512 void 513 igmp_leavegroup(struct in_multi *inm) 514 { 515 KASSERT(in_multi_lock_held()); 516 517 switch (inm->inm_state) { 518 case IGMP_DELAYING_MEMBER: 519 case IGMP_IDLE_MEMBER: 520 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 521 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) 522 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 523 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 524 break; 525 case IGMP_LAZY_MEMBER: 526 case IGMP_AWAKENING_MEMBER: 527 case IGMP_SLEEPING_MEMBER: 528 break; 529 } 530 } 531 532 void 533 igmp_fasttimo(void) 534 { 535 struct in_multi *inm; 536 struct in_multistep step; 537 538 /* 539 * Quick check to see if any work needs to be done, in order 540 * to minimize the overhead of fasttimo processing. 541 */ 542 if (!igmp_timers_on) { 543 return; 544 } 545 546 /* XXX: Needed for ip_output(). */ 547 SOFTNET_LOCK_UNLESS_NET_MPSAFE(); 548 549 in_multi_lock(RW_WRITER); 550 igmp_timers_on = false; 551 inm = in_first_multi(&step); 552 while (inm != NULL) { 553 if (inm->inm_timer == 0) { 554 /* do nothing */ 555 } else if (--inm->inm_timer == 0) { 556 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 557 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 558 igmp_sendpkt(inm, 559 IGMP_v1_HOST_MEMBERSHIP_REPORT); 560 else 561 igmp_sendpkt(inm, 562 IGMP_v2_HOST_MEMBERSHIP_REPORT); 563 inm->inm_state = IGMP_IDLE_MEMBER; 564 } 565 } else { 566 igmp_timers_on = true; 567 } 568 inm = in_next_multi(&step); 569 } 570 in_multi_unlock(); 571 SOFTNET_UNLOCK_UNLESS_NET_MPSAFE(); 572 } 573 574 void 575 igmp_slowtimo(void) 576 { 577 router_info_t *rti; 578 579 in_multi_lock(RW_WRITER); 580 LIST_FOREACH(rti, &rti_head, rti_link) { 581 if (rti->rti_type == IGMP_v1_ROUTER && 582 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 583 rti->rti_type = IGMP_v2_ROUTER; 584 } 585 } 586 in_multi_unlock(); 587 } 588 589 /* 590 * igmp_sendpkt: construct an IGMP packet, given the multicast structure 591 * and the type, and send the datagram. 592 */ 593 static void 594 igmp_sendpkt(struct in_multi *inm, int type) 595 { 596 struct mbuf *m; 597 struct igmp *igmp; 598 struct ip *ip; 599 struct ip_moptions imo; 600 601 KASSERT(in_multi_lock_held()); 602 603 MGETHDR(m, M_DONTWAIT, MT_HEADER); 604 if (m == NULL) 605 return; 606 KASSERT(max_linkhdr + sizeof(struct ip) + IGMP_MINLEN <= MHLEN); 607 608 m->m_data += max_linkhdr; 609 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 610 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 611 612 ip = mtod(m, struct ip *); 613 ip->ip_tos = 0; 614 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 615 ip->ip_off = htons(0); 616 ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; 617 ip->ip_p = IPPROTO_IGMP; 618 ip->ip_src = zeroin_addr; 619 ip->ip_dst = inm->inm_addr; 620 621 m->m_data += sizeof(struct ip); 622 m->m_len -= sizeof(struct ip); 623 igmp = mtod(m, struct igmp *); 624 igmp->igmp_type = type; 625 igmp->igmp_code = 0; 626 igmp->igmp_group = inm->inm_addr; 627 igmp->igmp_cksum = 0; 628 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 629 m->m_data -= sizeof(struct ip); 630 m->m_len += sizeof(struct ip); 631 632 imo.imo_multicast_if_index = if_get_index(inm->inm_ifp); 633 imo.imo_multicast_ttl = 1; 634 635 /* 636 * Request loopback of the report if we are acting as a multicast 637 * router, so that the process-level routing demon can hear it. 638 */ 639 #ifdef MROUTING 640 extern struct socket *ip_mrouter; 641 imo.imo_multicast_loop = (ip_mrouter != NULL); 642 #else 643 imo.imo_multicast_loop = 0; 644 #endif 645 646 /* 647 * Note: IP_IGMP_MCAST indicates that in_multilock is held. 648 * The caller must still acquire softnet_lock for ip_output(). 649 */ 650 #ifndef NET_MPSAFE 651 KASSERT(mutex_owned(softnet_lock)); 652 #endif 653 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL); 654 IGMP_STATINC(IGMP_STAT_SND_REPORTS); 655 } 656 657 void 658 igmp_purgeif(ifnet_t *ifp) 659 { 660 in_multi_lock(RW_WRITER); 661 rti_delete(ifp); 662 in_multi_unlock(); 663 } 664 665 static int 666 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS) 667 { 668 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS); 669 } 670 671 static void 672 sysctl_net_inet_igmp_setup(struct sysctllog **clog) 673 { 674 sysctl_createv(clog, 0, NULL, NULL, 675 CTLFLAG_PERMANENT, 676 CTLTYPE_NODE, "inet", NULL, 677 NULL, 0, NULL, 0, 678 CTL_NET, PF_INET, CTL_EOL); 679 sysctl_createv(clog, 0, NULL, NULL, 680 CTLFLAG_PERMANENT, 681 CTLTYPE_NODE, "igmp", 682 SYSCTL_DESCR("Internet Group Management Protocol"), 683 NULL, 0, NULL, 0, 684 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL); 685 sysctl_createv(clog, 0, NULL, NULL, 686 CTLFLAG_PERMANENT, 687 CTLTYPE_STRUCT, "stats", 688 SYSCTL_DESCR("IGMP statistics"), 689 sysctl_net_inet_igmp_stats, 0, NULL, 0, 690 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL); 691 } 692