1 /* $NetBSD: igmp.c,v 1.64 2017/01/24 07:09:24 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.64 2017/01/24 07:09:24 ozaki-r 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 rip_input(m, iphlen, proto); 478 return; 479 480 drop: 481 m_put_rcvif_psref(ifp, &psref); 482 m_freem(m); 483 return; 484 } 485 486 int 487 igmp_joingroup(struct in_multi *inm) 488 { 489 KASSERT(in_multi_lock_held()); 490 inm->inm_state = IGMP_IDLE_MEMBER; 491 492 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 493 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { 494 int report_type; 495 496 report_type = rti_fill(inm); 497 if (report_type == 0) { 498 return ENOMEM; 499 } 500 igmp_sendpkt(inm, report_type); 501 inm->inm_state = IGMP_DELAYING_MEMBER; 502 inm->inm_timer = IGMP_RANDOM_DELAY( 503 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 504 igmp_timers_on = true; 505 } else 506 inm->inm_timer = 0; 507 508 return 0; 509 } 510 511 void 512 igmp_leavegroup(struct in_multi *inm) 513 { 514 KASSERT(in_multi_lock_held()); 515 516 switch (inm->inm_state) { 517 case IGMP_DELAYING_MEMBER: 518 case IGMP_IDLE_MEMBER: 519 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 520 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) 521 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 522 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 523 break; 524 case IGMP_LAZY_MEMBER: 525 case IGMP_AWAKENING_MEMBER: 526 case IGMP_SLEEPING_MEMBER: 527 break; 528 } 529 } 530 531 void 532 igmp_fasttimo(void) 533 { 534 struct in_multi *inm; 535 struct in_multistep step; 536 537 /* 538 * Quick check to see if any work needs to be done, in order 539 * to minimize the overhead of fasttimo processing. 540 */ 541 if (!igmp_timers_on) { 542 return; 543 } 544 545 #ifndef NET_MPSAFE 546 /* XXX: Needed for ip_output(). */ 547 mutex_enter(softnet_lock); 548 #endif 549 550 in_multi_lock(RW_WRITER); 551 igmp_timers_on = false; 552 inm = in_first_multi(&step); 553 while (inm != NULL) { 554 if (inm->inm_timer == 0) { 555 /* do nothing */ 556 } else if (--inm->inm_timer == 0) { 557 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 558 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 559 igmp_sendpkt(inm, 560 IGMP_v1_HOST_MEMBERSHIP_REPORT); 561 else 562 igmp_sendpkt(inm, 563 IGMP_v2_HOST_MEMBERSHIP_REPORT); 564 inm->inm_state = IGMP_IDLE_MEMBER; 565 } 566 } else { 567 igmp_timers_on = true; 568 } 569 inm = in_next_multi(&step); 570 } 571 in_multi_unlock(); 572 #ifndef NET_MPSAFE 573 mutex_exit(softnet_lock); 574 #endif 575 } 576 577 void 578 igmp_slowtimo(void) 579 { 580 router_info_t *rti; 581 582 in_multi_lock(RW_WRITER); 583 LIST_FOREACH(rti, &rti_head, rti_link) { 584 if (rti->rti_type == IGMP_v1_ROUTER && 585 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 586 rti->rti_type = IGMP_v2_ROUTER; 587 } 588 } 589 in_multi_unlock(); 590 } 591 592 /* 593 * igmp_sendpkt: construct an IGMP packet, given the multicast structure 594 * and the type, and send the datagram. 595 */ 596 static void 597 igmp_sendpkt(struct in_multi *inm, int type) 598 { 599 struct mbuf *m; 600 struct igmp *igmp; 601 struct ip *ip; 602 struct ip_moptions imo; 603 604 KASSERT(in_multi_lock_held()); 605 606 MGETHDR(m, M_DONTWAIT, MT_HEADER); 607 if (m == NULL) 608 return; 609 610 /* 611 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 612 * is smaller than mbuf size returned by MGETHDR. 613 */ 614 m->m_data += max_linkhdr; 615 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 616 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 617 618 ip = mtod(m, struct ip *); 619 ip->ip_tos = 0; 620 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 621 ip->ip_off = htons(0); 622 ip->ip_p = IPPROTO_IGMP; 623 ip->ip_src = zeroin_addr; 624 ip->ip_dst = inm->inm_addr; 625 626 m->m_data += sizeof(struct ip); 627 m->m_len -= sizeof(struct ip); 628 igmp = mtod(m, struct igmp *); 629 igmp->igmp_type = type; 630 igmp->igmp_code = 0; 631 igmp->igmp_group = inm->inm_addr; 632 igmp->igmp_cksum = 0; 633 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 634 m->m_data -= sizeof(struct ip); 635 m->m_len += sizeof(struct ip); 636 637 imo.imo_multicast_if_index = if_get_index(inm->inm_ifp); 638 imo.imo_multicast_ttl = 1; 639 #ifdef RSVP_ISI 640 imo.imo_multicast_vif = -1; 641 #endif 642 /* 643 * Request loopback of the report if we are acting as a multicast 644 * router, so that the process-level routing demon can hear it. 645 */ 646 #ifdef MROUTING 647 extern struct socket *ip_mrouter; 648 imo.imo_multicast_loop = (ip_mrouter != NULL); 649 #else 650 imo.imo_multicast_loop = 0; 651 #endif 652 653 /* 654 * Note: IP_IGMP_MCAST indicates that in_multilock is held. 655 * The caller must still acquire softnet_lock for ip_output(). 656 */ 657 #ifndef NET_MPSAFE 658 KASSERT(mutex_owned(softnet_lock)); 659 #endif 660 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL); 661 IGMP_STATINC(IGMP_STAT_SND_REPORTS); 662 } 663 664 void 665 igmp_purgeif(ifnet_t *ifp) 666 { 667 in_multi_lock(RW_WRITER); 668 rti_delete(ifp); 669 in_multi_unlock(); 670 } 671 672 static int 673 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS) 674 { 675 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS); 676 } 677 678 static void 679 sysctl_net_inet_igmp_setup(struct sysctllog **clog) 680 { 681 sysctl_createv(clog, 0, NULL, NULL, 682 CTLFLAG_PERMANENT, 683 CTLTYPE_NODE, "inet", NULL, 684 NULL, 0, NULL, 0, 685 CTL_NET, PF_INET, CTL_EOL); 686 sysctl_createv(clog, 0, NULL, NULL, 687 CTLFLAG_PERMANENT, 688 CTLTYPE_NODE, "igmp", 689 SYSCTL_DESCR("Internet Group Management Protocol"), 690 NULL, 0, NULL, 0, 691 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL); 692 sysctl_createv(clog, 0, NULL, NULL, 693 CTLFLAG_PERMANENT, 694 CTLTYPE_STRUCT, "stats", 695 SYSCTL_DESCR("IGMP statistics"), 696 sysctl_net_inet_igmp_stats, 0, NULL, 0, 697 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL); 698 } 699