1 /* $NetBSD: igmp.c,v 1.68 2018/06/21 10:37:50 knakahara 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.68 2018/06/21 10:37:50 knakahara 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, ...) 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 int s = pserialize_read_enter(); 365 ia = in_get_ia_from_ifp(ifp); /* XXX */ 366 if (ia) 367 ip->ip_src.s_addr = ia->ia_subnet; 368 pserialize_read_exit(s); 369 } 370 371 /* 372 * If we belong to the group being reported, stop 373 * our timer for that group. 374 */ 375 in_multi_lock(RW_WRITER); 376 inm = in_lookup_multi(igmp->igmp_group, ifp); 377 if (inm != NULL) { 378 inm->inm_timer = 0; 379 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 380 381 switch (inm->inm_state) { 382 case IGMP_IDLE_MEMBER: 383 case IGMP_LAZY_MEMBER: 384 case IGMP_AWAKENING_MEMBER: 385 case IGMP_SLEEPING_MEMBER: 386 inm->inm_state = IGMP_SLEEPING_MEMBER; 387 break; 388 case IGMP_DELAYING_MEMBER: 389 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 390 inm->inm_state = IGMP_LAZY_MEMBER; 391 else 392 inm->inm_state = IGMP_SLEEPING_MEMBER; 393 break; 394 } 395 } 396 in_multi_unlock(); 397 break; 398 399 case IGMP_v2_HOST_MEMBERSHIP_REPORT: { 400 int s = pserialize_read_enter(); 401 #ifdef MROUTING 402 /* 403 * Make sure we don't hear our own membership report. Fast 404 * leave requires knowing that we are the only member of a 405 * group. 406 */ 407 ia = in_get_ia_from_ifp(ifp); /* XXX */ 408 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) { 409 pserialize_read_exit(s); 410 break; 411 } 412 #endif 413 414 IGMP_STATINC(IGMP_STAT_RCV_REPORTS); 415 416 if (ifp->if_flags & IFF_LOOPBACK) { 417 pserialize_read_exit(s); 418 break; 419 } 420 421 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 422 !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 423 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); 424 pserialize_read_exit(s); 425 goto drop; 426 } 427 428 /* 429 * KLUDGE: if the IP source address of the report has an 430 * unspecified (i.e., zero) subnet number, as is allowed for 431 * a booting host, replace it with the correct subnet number 432 * so that a process-level multicast routing daemon can 433 * determine which subnet it arrived from. This is necessary 434 * to compensate for the lack of any way for a process to 435 * determine the arrival interface of an incoming packet. 436 */ 437 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 438 #ifndef MROUTING 439 ia = in_get_ia_from_ifp(ifp); /* XXX */ 440 #endif 441 if (ia) 442 ip->ip_src.s_addr = ia->ia_subnet; 443 } 444 pserialize_read_exit(s); 445 446 /* 447 * If we belong to the group being reported, stop 448 * our timer for that group. 449 */ 450 in_multi_lock(RW_WRITER); 451 inm = in_lookup_multi(igmp->igmp_group, ifp); 452 if (inm != NULL) { 453 inm->inm_timer = 0; 454 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS); 455 456 switch (inm->inm_state) { 457 case IGMP_DELAYING_MEMBER: 458 case IGMP_IDLE_MEMBER: 459 case IGMP_AWAKENING_MEMBER: 460 inm->inm_state = IGMP_LAZY_MEMBER; 461 break; 462 case IGMP_LAZY_MEMBER: 463 case IGMP_SLEEPING_MEMBER: 464 break; 465 } 466 } 467 in_multi_unlock(); 468 break; 469 } 470 } 471 m_put_rcvif_psref(ifp, &psref); 472 473 /* 474 * Pass all valid IGMP packets up to any process(es) listening 475 * on a raw IGMP socket. 476 */ 477 /* 478 * Currently, igmp_input() is always called holding softnet_lock 479 * by ipintr()(!NET_MPSAFE) or PR_INPUT_WRAP()(NET_MPSAFE). 480 */ 481 KASSERT(mutex_owned(softnet_lock)); 482 rip_input(m, iphlen, proto); 483 return; 484 485 drop: 486 m_put_rcvif_psref(ifp, &psref); 487 m_freem(m); 488 return; 489 } 490 491 int 492 igmp_joingroup(struct in_multi *inm) 493 { 494 KASSERT(in_multi_lock_held()); 495 inm->inm_state = IGMP_IDLE_MEMBER; 496 497 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 498 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { 499 int report_type; 500 501 report_type = rti_fill(inm); 502 if (report_type == 0) { 503 return ENOMEM; 504 } 505 igmp_sendpkt(inm, report_type); 506 inm->inm_state = IGMP_DELAYING_MEMBER; 507 inm->inm_timer = IGMP_RANDOM_DELAY( 508 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 509 igmp_timers_on = true; 510 } else 511 inm->inm_timer = 0; 512 513 return 0; 514 } 515 516 void 517 igmp_leavegroup(struct in_multi *inm) 518 { 519 KASSERT(in_multi_lock_held()); 520 521 switch (inm->inm_state) { 522 case IGMP_DELAYING_MEMBER: 523 case IGMP_IDLE_MEMBER: 524 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 525 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) 526 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 527 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 528 break; 529 case IGMP_LAZY_MEMBER: 530 case IGMP_AWAKENING_MEMBER: 531 case IGMP_SLEEPING_MEMBER: 532 break; 533 } 534 } 535 536 void 537 igmp_fasttimo(void) 538 { 539 struct in_multi *inm; 540 struct in_multistep step; 541 542 /* 543 * Quick check to see if any work needs to be done, in order 544 * to minimize the overhead of fasttimo processing. 545 */ 546 if (!igmp_timers_on) { 547 return; 548 } 549 550 /* XXX: Needed for ip_output(). */ 551 SOFTNET_LOCK_UNLESS_NET_MPSAFE(); 552 553 in_multi_lock(RW_WRITER); 554 igmp_timers_on = false; 555 inm = in_first_multi(&step); 556 while (inm != NULL) { 557 if (inm->inm_timer == 0) { 558 /* do nothing */ 559 } else if (--inm->inm_timer == 0) { 560 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 561 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 562 igmp_sendpkt(inm, 563 IGMP_v1_HOST_MEMBERSHIP_REPORT); 564 else 565 igmp_sendpkt(inm, 566 IGMP_v2_HOST_MEMBERSHIP_REPORT); 567 inm->inm_state = IGMP_IDLE_MEMBER; 568 } 569 } else { 570 igmp_timers_on = true; 571 } 572 inm = in_next_multi(&step); 573 } 574 in_multi_unlock(); 575 SOFTNET_UNLOCK_UNLESS_NET_MPSAFE(); 576 } 577 578 void 579 igmp_slowtimo(void) 580 { 581 router_info_t *rti; 582 583 in_multi_lock(RW_WRITER); 584 LIST_FOREACH(rti, &rti_head, rti_link) { 585 if (rti->rti_type == IGMP_v1_ROUTER && 586 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 587 rti->rti_type = IGMP_v2_ROUTER; 588 } 589 } 590 in_multi_unlock(); 591 } 592 593 /* 594 * igmp_sendpkt: construct an IGMP packet, given the multicast structure 595 * and the type, and send the datagram. 596 */ 597 static void 598 igmp_sendpkt(struct in_multi *inm, int type) 599 { 600 struct mbuf *m; 601 struct igmp *igmp; 602 struct ip *ip; 603 struct ip_moptions imo; 604 605 KASSERT(in_multi_lock_held()); 606 607 MGETHDR(m, M_DONTWAIT, MT_HEADER); 608 if (m == NULL) 609 return; 610 KASSERT(max_linkhdr + sizeof(struct ip) + IGMP_MINLEN <= MHLEN); 611 612 m->m_data += max_linkhdr; 613 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 614 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 615 616 ip = mtod(m, struct ip *); 617 ip->ip_tos = 0; 618 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 619 ip->ip_off = htons(0); 620 ip->ip_p = IPPROTO_IGMP; 621 ip->ip_src = zeroin_addr; 622 ip->ip_dst = inm->inm_addr; 623 624 m->m_data += sizeof(struct ip); 625 m->m_len -= sizeof(struct ip); 626 igmp = mtod(m, struct igmp *); 627 igmp->igmp_type = type; 628 igmp->igmp_code = 0; 629 igmp->igmp_group = inm->inm_addr; 630 igmp->igmp_cksum = 0; 631 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 632 m->m_data -= sizeof(struct ip); 633 m->m_len += sizeof(struct ip); 634 635 imo.imo_multicast_if_index = if_get_index(inm->inm_ifp); 636 imo.imo_multicast_ttl = 1; 637 638 /* 639 * Request loopback of the report if we are acting as a multicast 640 * router, so that the process-level routing demon can hear it. 641 */ 642 #ifdef MROUTING 643 extern struct socket *ip_mrouter; 644 imo.imo_multicast_loop = (ip_mrouter != NULL); 645 #else 646 imo.imo_multicast_loop = 0; 647 #endif 648 649 /* 650 * Note: IP_IGMP_MCAST indicates that in_multilock is held. 651 * The caller must still acquire softnet_lock for ip_output(). 652 */ 653 #ifndef NET_MPSAFE 654 KASSERT(mutex_owned(softnet_lock)); 655 #endif 656 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL); 657 IGMP_STATINC(IGMP_STAT_SND_REPORTS); 658 } 659 660 void 661 igmp_purgeif(ifnet_t *ifp) 662 { 663 in_multi_lock(RW_WRITER); 664 rti_delete(ifp); 665 in_multi_unlock(); 666 } 667 668 static int 669 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS) 670 { 671 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS); 672 } 673 674 static void 675 sysctl_net_inet_igmp_setup(struct sysctllog **clog) 676 { 677 sysctl_createv(clog, 0, NULL, NULL, 678 CTLFLAG_PERMANENT, 679 CTLTYPE_NODE, "inet", NULL, 680 NULL, 0, NULL, 0, 681 CTL_NET, PF_INET, CTL_EOL); 682 sysctl_createv(clog, 0, NULL, NULL, 683 CTLFLAG_PERMANENT, 684 CTLTYPE_NODE, "igmp", 685 SYSCTL_DESCR("Internet Group Management Protocol"), 686 NULL, 0, NULL, 0, 687 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL); 688 sysctl_createv(clog, 0, NULL, NULL, 689 CTLFLAG_PERMANENT, 690 CTLTYPE_STRUCT, "stats", 691 SYSCTL_DESCR("IGMP statistics"), 692 sysctl_net_inet_igmp_stats, 0, NULL, 0, 693 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL); 694 } 695