1 /* $OpenBSD: igmp.c,v 1.42 2014/07/12 18:44:23 tedu Exp $ */ 2 /* $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1988 Stephen Deering. 35 * Copyright (c) 1992, 1993 36 * The Regents of the University of California. All rights reserved. 37 * 38 * This code is derived from software contributed to Berkeley by 39 * Stephen Deering of Stanford University. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. Neither the name of the University nor the names of its contributors 50 * may be used to endorse or promote products derived from this software 51 * without specific prior written permission. 52 * 53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 * SUCH DAMAGE. 64 * 65 * @(#)igmp.c 8.2 (Berkeley) 5/3/95 66 */ 67 68 /* 69 * Internet Group Management Protocol (IGMP) routines. 70 * 71 * Written by Steve Deering, Stanford, May 1988. 72 * Modified by Rosen Sharma, Stanford, Aug 1994. 73 * Modified by Bill Fenner, Xerox PARC, Feb 1995. 74 * 75 * MULTICAST Revision: 1.3 76 */ 77 78 #include <sys/param.h> 79 #include <sys/mbuf.h> 80 #include <sys/systm.h> 81 #include <sys/socket.h> 82 #include <sys/protosw.h> 83 #include <sys/proc.h> 84 #include <sys/sysctl.h> 85 86 #include <net/if.h> 87 #include <net/if_var.h> 88 #include <net/route.h> 89 90 #include <netinet/in.h> 91 #include <netinet/in_var.h> 92 #include <netinet/in_systm.h> 93 #include <netinet/ip.h> 94 #include <netinet/ip_var.h> 95 #include <netinet/igmp.h> 96 #include <netinet/igmp_var.h> 97 #include <dev/rndvar.h> 98 99 #include <sys/stdarg.h> 100 101 #define IP_MULTICASTOPTS 0 102 103 int *igmpctl_vars[IGMPCTL_MAXID] = IGMPCTL_VARS; 104 105 int igmp_timers_are_running; 106 static struct router_info *rti_head; 107 static struct mbuf *router_alert; 108 struct igmpstat igmpstat; 109 110 void igmp_checktimer(struct ifnet *); 111 void igmp_sendpkt(struct in_multi *, int, in_addr_t); 112 int rti_fill(struct in_multi *); 113 struct router_info * rti_find(struct ifnet *); 114 115 void 116 igmp_init(void) 117 { 118 struct ipoption *ra; 119 120 igmp_timers_are_running = 0; 121 rti_head = 0; 122 123 router_alert = m_get(M_DONTWAIT, MT_DATA); 124 if (router_alert == NULL) { 125 printf("%s: no mbuf\n", __func__); 126 return; 127 } 128 129 /* 130 * Construct a Router Alert option (RAO) to use in report 131 * messages as required by RFC2236. This option has the 132 * following format: 133 * 134 * | 10010100 | 00000100 | 2 octet value | 135 * 136 * where a value of "0" indicates that routers shall examine 137 * the packet. 138 */ 139 ra = mtod(router_alert, struct ipoption *); 140 ra->ipopt_dst.s_addr = INADDR_ANY; 141 ra->ipopt_list[0] = IPOPT_RA; 142 ra->ipopt_list[1] = 0x04; 143 ra->ipopt_list[2] = 0x00; 144 ra->ipopt_list[3] = 0x00; 145 router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; 146 } 147 148 /* Return -1 for error. */ 149 int 150 rti_fill(struct in_multi *inm) 151 { 152 struct router_info *rti; 153 154 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 155 if (rti->rti_ifp->if_index == inm->inm_ifidx) { 156 inm->inm_rti = rti; 157 if (rti->rti_type == IGMP_v1_ROUTER) 158 return (IGMP_v1_HOST_MEMBERSHIP_REPORT); 159 else 160 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 161 } 162 } 163 164 rti = (struct router_info *)malloc(sizeof(struct router_info), 165 M_MRTABLE, M_NOWAIT); 166 if (rti == NULL) 167 return (-1); 168 rti->rti_ifp = if_get(inm->inm_ifidx); 169 rti->rti_type = IGMP_v2_ROUTER; 170 rti->rti_next = rti_head; 171 rti_head = rti; 172 inm->inm_rti = rti; 173 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 174 } 175 176 struct router_info * 177 rti_find(struct ifnet *ifp) 178 { 179 struct router_info *rti; 180 181 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 182 if (rti->rti_ifp == ifp) 183 return (rti); 184 } 185 186 rti = (struct router_info *)malloc(sizeof(struct router_info), 187 M_MRTABLE, M_NOWAIT); 188 if (rti == NULL) 189 return (NULL); 190 rti->rti_ifp = ifp; 191 rti->rti_type = IGMP_v2_ROUTER; 192 rti->rti_next = rti_head; 193 rti_head = rti; 194 return (rti); 195 } 196 197 void 198 rti_delete(struct ifnet *ifp) 199 { 200 struct router_info *rti, **prti = &rti_head; 201 202 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 203 if (rti->rti_ifp == ifp) { 204 *prti = rti->rti_next; 205 free(rti, M_MRTABLE, 0); 206 break; 207 } 208 prti = &rti->rti_next; 209 } 210 } 211 212 void 213 igmp_input(struct mbuf *m, ...) 214 { 215 int iphlen; 216 struct ifnet *ifp = m->m_pkthdr.rcvif; 217 struct ip *ip = mtod(m, struct ip *); 218 struct igmp *igmp; 219 int igmplen; 220 int minlen; 221 struct ifmaddr *ifma; 222 struct in_multi *inm; 223 struct router_info *rti; 224 struct in_ifaddr *ia; 225 int timer; 226 va_list ap; 227 228 va_start(ap, m); 229 iphlen = va_arg(ap, int); 230 va_end(ap); 231 232 ++igmpstat.igps_rcv_total; 233 234 igmplen = ntohs(ip->ip_len) - iphlen; 235 236 /* 237 * Validate lengths 238 */ 239 if (igmplen < IGMP_MINLEN) { 240 ++igmpstat.igps_rcv_tooshort; 241 m_freem(m); 242 return; 243 } 244 minlen = iphlen + IGMP_MINLEN; 245 if ((m->m_flags & M_EXT || m->m_len < minlen) && 246 (m = m_pullup(m, minlen)) == NULL) { 247 ++igmpstat.igps_rcv_tooshort; 248 return; 249 } 250 251 /* 252 * Validate checksum 253 */ 254 m->m_data += iphlen; 255 m->m_len -= iphlen; 256 igmp = mtod(m, struct igmp *); 257 if (in_cksum(m, igmplen)) { 258 ++igmpstat.igps_rcv_badsum; 259 m_freem(m); 260 return; 261 } 262 m->m_data -= iphlen; 263 m->m_len += iphlen; 264 ip = mtod(m, struct ip *); 265 266 switch (igmp->igmp_type) { 267 268 case IGMP_HOST_MEMBERSHIP_QUERY: 269 ++igmpstat.igps_rcv_queries; 270 271 if (ifp->if_flags & IFF_LOOPBACK) 272 break; 273 274 if (igmp->igmp_code == 0) { 275 rti = rti_find(ifp); 276 if (rti == NULL) { 277 m_freem(m); 278 return; 279 } 280 rti->rti_type = IGMP_v1_ROUTER; 281 rti->rti_age = 0; 282 283 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { 284 ++igmpstat.igps_rcv_badqueries; 285 m_freem(m); 286 return; 287 } 288 289 /* 290 * Start the timers in all of our membership records 291 * for the interface on which the query arrived, 292 * except those that are already running and those 293 * that belong to a "local" group (224.0.0.X). 294 */ 295 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 296 if (ifma->ifma_addr->sa_family != AF_INET) 297 continue; 298 inm = ifmatoinm(ifma); 299 if (inm->inm_timer == 0 && 300 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 301 inm->inm_state = IGMP_DELAYING_MEMBER; 302 inm->inm_timer = IGMP_RANDOM_DELAY( 303 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 304 igmp_timers_are_running = 1; 305 } 306 } 307 } else { 308 if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 309 ++igmpstat.igps_rcv_badqueries; 310 m_freem(m); 311 return; 312 } 313 314 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 315 if (timer == 0) 316 timer = 1; 317 318 /* 319 * Start the timers in all of our membership records 320 * for the interface on which the query arrived, 321 * except those that are already running and those 322 * that belong to a "local" group (224.0.0.X). For 323 * timers already running, check if they need to be 324 * reset. 325 */ 326 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 327 if (ifma->ifma_addr->sa_family != AF_INET) 328 continue; 329 inm = ifmatoinm(ifma); 330 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 331 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 332 ip->ip_dst.s_addr == inm->inm_addr.s_addr)) { 333 switch (inm->inm_state) { 334 case IGMP_DELAYING_MEMBER: 335 if (inm->inm_timer <= timer) 336 break; 337 /* FALLTHROUGH */ 338 case IGMP_IDLE_MEMBER: 339 case IGMP_LAZY_MEMBER: 340 case IGMP_AWAKENING_MEMBER: 341 inm->inm_state = 342 IGMP_DELAYING_MEMBER; 343 inm->inm_timer = 344 IGMP_RANDOM_DELAY(timer); 345 igmp_timers_are_running = 1; 346 break; 347 case IGMP_SLEEPING_MEMBER: 348 inm->inm_state = 349 IGMP_AWAKENING_MEMBER; 350 break; 351 } 352 } 353 } 354 } 355 356 break; 357 358 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 359 ++igmpstat.igps_rcv_reports; 360 361 if (ifp->if_flags & IFF_LOOPBACK) 362 break; 363 364 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 365 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 366 ++igmpstat.igps_rcv_badreports; 367 m_freem(m); 368 return; 369 } 370 371 /* 372 * KLUDGE: if the IP source address of the report has an 373 * unspecified (i.e., zero) subnet number, as is allowed for 374 * a booting host, replace it with the correct subnet number 375 * so that a process-level multicast routing daemon can 376 * determine which subnet it arrived from. This is necessary 377 * to compensate for the lack of any way for a process to 378 * determine the arrival interface of an incoming packet. 379 */ 380 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 381 IFP_TO_IA(ifp, ia); 382 if (ia) 383 ip->ip_src.s_addr = ia->ia_net; 384 } 385 386 /* 387 * If we belong to the group being reported, stop 388 * our timer for that group. 389 */ 390 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 391 if (inm != NULL) { 392 inm->inm_timer = 0; 393 ++igmpstat.igps_rcv_ourreports; 394 395 switch (inm->inm_state) { 396 case IGMP_IDLE_MEMBER: 397 case IGMP_LAZY_MEMBER: 398 case IGMP_AWAKENING_MEMBER: 399 case IGMP_SLEEPING_MEMBER: 400 inm->inm_state = IGMP_SLEEPING_MEMBER; 401 break; 402 case IGMP_DELAYING_MEMBER: 403 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 404 inm->inm_state = IGMP_LAZY_MEMBER; 405 else 406 inm->inm_state = IGMP_SLEEPING_MEMBER; 407 break; 408 } 409 } 410 411 break; 412 413 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 414 #ifdef MROUTING 415 /* 416 * Make sure we don't hear our own membership report. Fast 417 * leave requires knowing that we are the only member of a 418 * group. 419 */ 420 IFP_TO_IA(ifp, ia); 421 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr) 422 break; 423 #endif 424 425 ++igmpstat.igps_rcv_reports; 426 427 if (ifp->if_flags & IFF_LOOPBACK) 428 break; 429 430 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 431 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 432 ++igmpstat.igps_rcv_badreports; 433 m_freem(m); 434 return; 435 } 436 437 /* 438 * KLUDGE: if the IP source address of the report has an 439 * unspecified (i.e., zero) subnet number, as is allowed for 440 * a booting host, replace it with the correct subnet number 441 * so that a process-level multicast routing daemon can 442 * determine which subnet it arrived from. This is necessary 443 * to compensate for the lack of any way for a process to 444 * determine the arrival interface of an incoming packet. 445 */ 446 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 447 #ifndef MROUTING 448 IFP_TO_IA(ifp, ia); 449 #endif 450 if (ia) 451 ip->ip_src.s_addr = ia->ia_net; 452 } 453 454 /* 455 * If we belong to the group being reported, stop 456 * our timer for that group. 457 */ 458 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 459 if (inm != NULL) { 460 inm->inm_timer = 0; 461 ++igmpstat.igps_rcv_ourreports; 462 463 switch (inm->inm_state) { 464 case IGMP_DELAYING_MEMBER: 465 case IGMP_IDLE_MEMBER: 466 case IGMP_AWAKENING_MEMBER: 467 inm->inm_state = IGMP_LAZY_MEMBER; 468 break; 469 case IGMP_LAZY_MEMBER: 470 case IGMP_SLEEPING_MEMBER: 471 break; 472 } 473 } 474 475 break; 476 477 } 478 479 /* 480 * Pass all valid IGMP packets up to any process(es) listening 481 * on a raw IGMP socket. 482 */ 483 rip_input(m); 484 } 485 486 void 487 igmp_joingroup(struct in_multi *inm) 488 { 489 struct ifnet* ifp; 490 int i, s; 491 492 ifp = if_get(inm->inm_ifidx); 493 s = splsoftnet(); 494 495 inm->inm_state = IGMP_IDLE_MEMBER; 496 497 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 498 ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) { 499 if ((i = rti_fill(inm)) == -1) { 500 splx(s); 501 return; 502 } 503 igmp_sendpkt(inm, i, 0); 504 inm->inm_state = IGMP_DELAYING_MEMBER; 505 inm->inm_timer = IGMP_RANDOM_DELAY( 506 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 507 igmp_timers_are_running = 1; 508 } else 509 inm->inm_timer = 0; 510 splx(s); 511 } 512 513 void 514 igmp_leavegroup(struct in_multi *inm) 515 { 516 struct ifnet* ifp; 517 int s; 518 519 ifp = if_get(inm->inm_ifidx); 520 s = splsoftnet(); 521 522 switch (inm->inm_state) { 523 case IGMP_DELAYING_MEMBER: 524 case IGMP_IDLE_MEMBER: 525 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 526 ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) 527 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 528 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE, 529 INADDR_ALLROUTERS_GROUP); 530 break; 531 case IGMP_LAZY_MEMBER: 532 case IGMP_AWAKENING_MEMBER: 533 case IGMP_SLEEPING_MEMBER: 534 break; 535 } 536 splx(s); 537 } 538 539 void 540 igmp_fasttimo(void) 541 { 542 struct ifnet *ifp; 543 int s; 544 545 /* 546 * Quick check to see if any work needs to be done, in order 547 * to minimize the overhead of fasttimo processing. 548 */ 549 if (!igmp_timers_are_running) 550 return; 551 552 s = splsoftnet(); 553 igmp_timers_are_running = 0; 554 TAILQ_FOREACH(ifp, &ifnet, if_list) 555 igmp_checktimer(ifp); 556 splx(s); 557 } 558 559 560 void 561 igmp_checktimer(struct ifnet *ifp) 562 { 563 struct in_multi *inm; 564 struct ifmaddr *ifma; 565 566 splsoftassert(IPL_SOFTNET); 567 568 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 569 if (ifma->ifma_addr->sa_family != AF_INET) 570 continue; 571 inm = ifmatoinm(ifma); 572 if (inm->inm_timer == 0) { 573 /* do nothing */ 574 } else if (--inm->inm_timer == 0) { 575 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 576 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 577 igmp_sendpkt(inm, 578 IGMP_v1_HOST_MEMBERSHIP_REPORT, 0); 579 else 580 igmp_sendpkt(inm, 581 IGMP_v2_HOST_MEMBERSHIP_REPORT, 0); 582 inm->inm_state = IGMP_IDLE_MEMBER; 583 } 584 } else { 585 igmp_timers_are_running = 1; 586 } 587 } 588 } 589 590 void 591 igmp_slowtimo(void) 592 { 593 struct router_info *rti; 594 int s; 595 596 s = splsoftnet(); 597 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 598 if (rti->rti_type == IGMP_v1_ROUTER && 599 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 600 rti->rti_type = IGMP_v2_ROUTER; 601 } 602 } 603 splx(s); 604 } 605 606 void 607 igmp_sendpkt(struct in_multi *inm, int type, in_addr_t addr) 608 { 609 struct mbuf *m; 610 struct igmp *igmp; 611 struct ip *ip; 612 struct ip_moptions imo; 613 614 MGETHDR(m, M_DONTWAIT, MT_HEADER); 615 if (m == NULL) 616 return; 617 /* 618 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 619 * is smaller than mbuf size returned by MGETHDR. 620 */ 621 m->m_data += max_linkhdr; 622 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 623 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 624 625 ip = mtod(m, struct ip *); 626 ip->ip_tos = 0; 627 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 628 ip->ip_off = 0; 629 ip->ip_p = IPPROTO_IGMP; 630 ip->ip_src.s_addr = INADDR_ANY; 631 if (addr) { 632 ip->ip_dst.s_addr = addr; 633 } else { 634 ip->ip_dst = inm->inm_addr; 635 } 636 637 m->m_data += sizeof(struct ip); 638 m->m_len -= sizeof(struct ip); 639 igmp = mtod(m, struct igmp *); 640 igmp->igmp_type = type; 641 igmp->igmp_code = 0; 642 igmp->igmp_group = inm->inm_addr; 643 igmp->igmp_cksum = 0; 644 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 645 m->m_data -= sizeof(struct ip); 646 m->m_len += sizeof(struct ip); 647 648 imo.imo_multicast_ifp = if_get(inm->inm_ifidx); 649 imo.imo_multicast_ttl = 1; 650 651 /* 652 * Request loopback of the report if we are acting as a multicast 653 * router, so that the process-level routing daemon can hear it. 654 */ 655 #ifdef MROUTING 656 imo.imo_multicast_loop = (ip_mrouter != NULL); 657 #else 658 imo.imo_multicast_loop = 0; 659 #endif /* MROUTING */ 660 661 ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0); 662 663 ++igmpstat.igps_snd_reports; 664 } 665 666 /* 667 * Sysctl for igmp variables. 668 */ 669 int 670 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 671 void *newp, size_t newlen) 672 { 673 /* All sysctl names at this level are terminal. */ 674 if (namelen != 1) 675 return (ENOTDIR); 676 677 switch (name[0]) { 678 case IGMPCTL_STATS: 679 if (newp != NULL) 680 return (EPERM); 681 return (sysctl_struct(oldp, oldlenp, newp, newlen, 682 &igmpstat, sizeof(igmpstat))); 683 default: 684 if (name[0] < IGMPCTL_MAXID) 685 return (sysctl_int_arr(igmpctl_vars, name, namelen, 686 oldp, oldlenp, newp, newlen)); 687 return (ENOPROTOOPT); 688 } 689 /* NOTREACHED */ 690 } 691