1 /* $OpenBSD: igmp.c,v 1.54 2015/11/11 10:01:46 mpi 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/sysctl.h> 84 85 #include <net/if.h> 86 #include <net/if_var.h> 87 88 #include <netinet/in.h> 89 #include <netinet/in_var.h> 90 #include <netinet/ip.h> 91 #include <netinet/ip_var.h> 92 #include <netinet/igmp.h> 93 #include <netinet/igmp_var.h> 94 95 #include <sys/stdarg.h> 96 97 #define IP_MULTICASTOPTS 0 98 99 int *igmpctl_vars[IGMPCTL_MAXID] = IGMPCTL_VARS; 100 101 int igmp_timers_are_running; 102 static struct router_info *rti_head; 103 static struct mbuf *router_alert; 104 struct igmpstat igmpstat; 105 106 void igmp_checktimer(struct ifnet *); 107 void igmp_sendpkt(struct in_multi *, int, in_addr_t); 108 int rti_fill(struct in_multi *); 109 struct router_info * rti_find(struct ifnet *); 110 void igmp_input_if(struct ifnet *, struct mbuf *, int); 111 112 void 113 igmp_init(void) 114 { 115 struct ipoption *ra; 116 117 igmp_timers_are_running = 0; 118 rti_head = 0; 119 120 router_alert = m_get(M_DONTWAIT, MT_DATA); 121 if (router_alert == NULL) { 122 printf("%s: no mbuf\n", __func__); 123 return; 124 } 125 126 /* 127 * Construct a Router Alert option (RAO) to use in report 128 * messages as required by RFC2236. This option has the 129 * following format: 130 * 131 * | 10010100 | 00000100 | 2 octet value | 132 * 133 * where a value of "0" indicates that routers shall examine 134 * the packet. 135 */ 136 ra = mtod(router_alert, struct ipoption *); 137 ra->ipopt_dst.s_addr = INADDR_ANY; 138 ra->ipopt_list[0] = IPOPT_RA; 139 ra->ipopt_list[1] = 0x04; 140 ra->ipopt_list[2] = 0x00; 141 ra->ipopt_list[3] = 0x00; 142 router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; 143 } 144 145 /* Return -1 for error. */ 146 int 147 rti_fill(struct in_multi *inm) 148 { 149 struct router_info *rti; 150 151 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 152 if (rti->rti_ifidx == inm->inm_ifidx) { 153 inm->inm_rti = rti; 154 if (rti->rti_type == IGMP_v1_ROUTER) 155 return (IGMP_v1_HOST_MEMBERSHIP_REPORT); 156 else 157 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 158 } 159 } 160 161 rti = (struct router_info *)malloc(sizeof(struct router_info), 162 M_MRTABLE, M_NOWAIT); 163 if (rti == NULL) 164 return (-1); 165 rti->rti_ifidx = inm->inm_ifidx; 166 rti->rti_type = IGMP_v2_ROUTER; 167 rti->rti_next = rti_head; 168 rti_head = rti; 169 inm->inm_rti = rti; 170 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 171 } 172 173 struct router_info * 174 rti_find(struct ifnet *ifp) 175 { 176 struct router_info *rti; 177 178 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 179 if (rti->rti_ifidx == ifp->if_index) 180 return (rti); 181 } 182 183 rti = (struct router_info *)malloc(sizeof(struct router_info), 184 M_MRTABLE, M_NOWAIT); 185 if (rti == NULL) 186 return (NULL); 187 rti->rti_ifidx = ifp->if_index; 188 rti->rti_type = IGMP_v2_ROUTER; 189 rti->rti_next = rti_head; 190 rti_head = rti; 191 return (rti); 192 } 193 194 void 195 rti_delete(struct ifnet *ifp) 196 { 197 struct router_info *rti, **prti = &rti_head; 198 199 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 200 if (rti->rti_ifidx == ifp->if_index) { 201 *prti = rti->rti_next; 202 free(rti, M_MRTABLE, sizeof(*rti)); 203 break; 204 } 205 prti = &rti->rti_next; 206 } 207 } 208 209 void 210 igmp_input(struct mbuf *m, ...) 211 { 212 int iphlen; 213 struct ifnet *ifp; 214 va_list ap; 215 216 va_start(ap, m); 217 iphlen = va_arg(ap, int); 218 va_end(ap); 219 220 ++igmpstat.igps_rcv_total; 221 222 ifp = if_get(m->m_pkthdr.ph_ifidx); 223 if (ifp == NULL) { 224 m_freem(m); 225 return; 226 } 227 228 igmp_input_if(ifp, m, iphlen); 229 if_put(ifp); 230 } 231 232 void 233 igmp_input_if(struct ifnet *ifp, struct mbuf *m, int iphlen) 234 { 235 struct ip *ip = mtod(m, struct ip *); 236 struct igmp *igmp; 237 int igmplen; 238 int minlen; 239 struct ifmaddr *ifma; 240 struct in_multi *inm; 241 struct router_info *rti; 242 struct in_ifaddr *ia; 243 int timer; 244 245 igmplen = ntohs(ip->ip_len) - iphlen; 246 247 /* 248 * Validate lengths 249 */ 250 if (igmplen < IGMP_MINLEN) { 251 ++igmpstat.igps_rcv_tooshort; 252 m_freem(m); 253 return; 254 } 255 minlen = iphlen + IGMP_MINLEN; 256 if ((m->m_flags & M_EXT || m->m_len < minlen) && 257 (m = m_pullup(m, minlen)) == NULL) { 258 ++igmpstat.igps_rcv_tooshort; 259 return; 260 } 261 262 /* 263 * Validate checksum 264 */ 265 m->m_data += iphlen; 266 m->m_len -= iphlen; 267 igmp = mtod(m, struct igmp *); 268 if (in_cksum(m, igmplen)) { 269 ++igmpstat.igps_rcv_badsum; 270 m_freem(m); 271 return; 272 } 273 m->m_data -= iphlen; 274 m->m_len += iphlen; 275 ip = mtod(m, struct ip *); 276 277 switch (igmp->igmp_type) { 278 279 case IGMP_HOST_MEMBERSHIP_QUERY: 280 ++igmpstat.igps_rcv_queries; 281 282 if (ifp->if_flags & IFF_LOOPBACK) 283 break; 284 285 if (igmp->igmp_code == 0) { 286 rti = rti_find(ifp); 287 if (rti == NULL) { 288 m_freem(m); 289 return; 290 } 291 rti->rti_type = IGMP_v1_ROUTER; 292 rti->rti_age = 0; 293 294 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { 295 ++igmpstat.igps_rcv_badqueries; 296 m_freem(m); 297 return; 298 } 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). 305 */ 306 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 307 if (ifma->ifma_addr->sa_family != AF_INET) 308 continue; 309 inm = ifmatoinm(ifma); 310 if (inm->inm_timer == 0 && 311 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 312 inm->inm_state = IGMP_DELAYING_MEMBER; 313 inm->inm_timer = IGMP_RANDOM_DELAY( 314 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 315 igmp_timers_are_running = 1; 316 } 317 } 318 } else { 319 if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 320 ++igmpstat.igps_rcv_badqueries; 321 m_freem(m); 322 return; 323 } 324 325 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 326 if (timer == 0) 327 timer = 1; 328 329 /* 330 * Start the timers in all of our membership records 331 * for the interface on which the query arrived, 332 * except those that are already running and those 333 * that belong to a "local" group (224.0.0.X). For 334 * timers already running, check if they need to be 335 * reset. 336 */ 337 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 338 if (ifma->ifma_addr->sa_family != AF_INET) 339 continue; 340 inm = ifmatoinm(ifma); 341 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 342 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 343 ip->ip_dst.s_addr == inm->inm_addr.s_addr)) { 344 switch (inm->inm_state) { 345 case IGMP_DELAYING_MEMBER: 346 if (inm->inm_timer <= timer) 347 break; 348 /* FALLTHROUGH */ 349 case IGMP_IDLE_MEMBER: 350 case IGMP_LAZY_MEMBER: 351 case IGMP_AWAKENING_MEMBER: 352 inm->inm_state = 353 IGMP_DELAYING_MEMBER; 354 inm->inm_timer = 355 IGMP_RANDOM_DELAY(timer); 356 igmp_timers_are_running = 1; 357 break; 358 case IGMP_SLEEPING_MEMBER: 359 inm->inm_state = 360 IGMP_AWAKENING_MEMBER; 361 break; 362 } 363 } 364 } 365 } 366 367 break; 368 369 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 370 ++igmpstat.igps_rcv_reports; 371 372 if (ifp->if_flags & IFF_LOOPBACK) 373 break; 374 375 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 376 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 377 ++igmpstat.igps_rcv_badreports; 378 m_freem(m); 379 return; 380 } 381 382 /* 383 * KLUDGE: if the IP source address of the report has an 384 * unspecified (i.e., zero) subnet number, as is allowed for 385 * a booting host, replace it with the correct subnet number 386 * so that a process-level multicast routing daemon can 387 * determine which subnet it arrived from. This is necessary 388 * to compensate for the lack of any way for a process to 389 * determine the arrival interface of an incoming packet. 390 */ 391 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 392 IFP_TO_IA(ifp, ia); 393 if (ia) 394 ip->ip_src.s_addr = ia->ia_net; 395 } 396 397 /* 398 * If we belong to the group being reported, stop 399 * our timer for that group. 400 */ 401 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 402 if (inm != NULL) { 403 inm->inm_timer = 0; 404 ++igmpstat.igps_rcv_ourreports; 405 406 switch (inm->inm_state) { 407 case IGMP_IDLE_MEMBER: 408 case IGMP_LAZY_MEMBER: 409 case IGMP_AWAKENING_MEMBER: 410 case IGMP_SLEEPING_MEMBER: 411 inm->inm_state = IGMP_SLEEPING_MEMBER; 412 break; 413 case IGMP_DELAYING_MEMBER: 414 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 415 inm->inm_state = IGMP_LAZY_MEMBER; 416 else 417 inm->inm_state = IGMP_SLEEPING_MEMBER; 418 break; 419 } 420 } 421 422 break; 423 424 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 425 #ifdef MROUTING 426 /* 427 * Make sure we don't hear our own membership report. Fast 428 * leave requires knowing that we are the only member of a 429 * group. 430 */ 431 IFP_TO_IA(ifp, ia); 432 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr) 433 break; 434 #endif 435 436 ++igmpstat.igps_rcv_reports; 437 438 if (ifp->if_flags & IFF_LOOPBACK) 439 break; 440 441 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 442 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 443 ++igmpstat.igps_rcv_badreports; 444 m_freem(m); 445 return; 446 } 447 448 /* 449 * KLUDGE: if the IP source address of the report has an 450 * unspecified (i.e., zero) subnet number, as is allowed for 451 * a booting host, replace it with the correct subnet number 452 * so that a process-level multicast routing daemon can 453 * determine which subnet it arrived from. This is necessary 454 * to compensate for the lack of any way for a process to 455 * determine the arrival interface of an incoming packet. 456 */ 457 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 458 #ifndef MROUTING 459 IFP_TO_IA(ifp, ia); 460 #endif 461 if (ia) 462 ip->ip_src.s_addr = ia->ia_net; 463 } 464 465 /* 466 * If we belong to the group being reported, stop 467 * our timer for that group. 468 */ 469 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 470 if (inm != NULL) { 471 inm->inm_timer = 0; 472 ++igmpstat.igps_rcv_ourreports; 473 474 switch (inm->inm_state) { 475 case IGMP_DELAYING_MEMBER: 476 case IGMP_IDLE_MEMBER: 477 case IGMP_AWAKENING_MEMBER: 478 inm->inm_state = IGMP_LAZY_MEMBER; 479 break; 480 case IGMP_LAZY_MEMBER: 481 case IGMP_SLEEPING_MEMBER: 482 break; 483 } 484 } 485 486 break; 487 488 } 489 490 /* 491 * Pass all valid IGMP packets up to any process(es) listening 492 * on a raw IGMP socket. 493 */ 494 rip_input(m); 495 } 496 497 void 498 igmp_joingroup(struct in_multi *inm) 499 { 500 struct ifnet* ifp; 501 int i, s; 502 503 ifp = if_get(inm->inm_ifidx); 504 s = splsoftnet(); 505 506 inm->inm_state = IGMP_IDLE_MEMBER; 507 508 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 509 ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) { 510 if ((i = rti_fill(inm)) == -1) 511 goto out; 512 513 igmp_sendpkt(inm, i, 0); 514 inm->inm_state = IGMP_DELAYING_MEMBER; 515 inm->inm_timer = IGMP_RANDOM_DELAY( 516 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 517 igmp_timers_are_running = 1; 518 } else 519 inm->inm_timer = 0; 520 521 out: 522 splx(s); 523 if_put(ifp); 524 } 525 526 void 527 igmp_leavegroup(struct in_multi *inm) 528 { 529 struct ifnet* ifp; 530 int s; 531 532 ifp = if_get(inm->inm_ifidx); 533 s = splsoftnet(); 534 535 switch (inm->inm_state) { 536 case IGMP_DELAYING_MEMBER: 537 case IGMP_IDLE_MEMBER: 538 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 539 ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) 540 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 541 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE, 542 INADDR_ALLROUTERS_GROUP); 543 break; 544 case IGMP_LAZY_MEMBER: 545 case IGMP_AWAKENING_MEMBER: 546 case IGMP_SLEEPING_MEMBER: 547 break; 548 } 549 splx(s); 550 if_put(ifp); 551 } 552 553 void 554 igmp_fasttimo(void) 555 { 556 struct ifnet *ifp; 557 int s; 558 559 /* 560 * Quick check to see if any work needs to be done, in order 561 * to minimize the overhead of fasttimo processing. 562 */ 563 if (!igmp_timers_are_running) 564 return; 565 566 s = splsoftnet(); 567 igmp_timers_are_running = 0; 568 TAILQ_FOREACH(ifp, &ifnet, if_list) 569 igmp_checktimer(ifp); 570 splx(s); 571 } 572 573 574 void 575 igmp_checktimer(struct ifnet *ifp) 576 { 577 struct in_multi *inm; 578 struct ifmaddr *ifma; 579 580 splsoftassert(IPL_SOFTNET); 581 582 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 583 if (ifma->ifma_addr->sa_family != AF_INET) 584 continue; 585 inm = ifmatoinm(ifma); 586 if (inm->inm_timer == 0) { 587 /* do nothing */ 588 } else if (--inm->inm_timer == 0) { 589 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 590 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 591 igmp_sendpkt(inm, 592 IGMP_v1_HOST_MEMBERSHIP_REPORT, 0); 593 else 594 igmp_sendpkt(inm, 595 IGMP_v2_HOST_MEMBERSHIP_REPORT, 0); 596 inm->inm_state = IGMP_IDLE_MEMBER; 597 } 598 } else { 599 igmp_timers_are_running = 1; 600 } 601 } 602 } 603 604 void 605 igmp_slowtimo(void) 606 { 607 struct router_info *rti; 608 int s; 609 610 s = splsoftnet(); 611 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 612 if (rti->rti_type == IGMP_v1_ROUTER && 613 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 614 rti->rti_type = IGMP_v2_ROUTER; 615 } 616 } 617 splx(s); 618 } 619 620 void 621 igmp_sendpkt(struct in_multi *inm, int type, in_addr_t addr) 622 { 623 struct mbuf *m; 624 struct igmp *igmp; 625 struct ip *ip; 626 struct ip_moptions imo; 627 628 MGETHDR(m, M_DONTWAIT, MT_HEADER); 629 if (m == NULL) 630 return; 631 /* 632 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 633 * is smaller than mbuf size returned by MGETHDR. 634 */ 635 m->m_data += max_linkhdr; 636 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 637 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 638 639 ip = mtod(m, struct ip *); 640 ip->ip_tos = 0; 641 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 642 ip->ip_off = 0; 643 ip->ip_p = IPPROTO_IGMP; 644 ip->ip_src.s_addr = INADDR_ANY; 645 if (addr) { 646 ip->ip_dst.s_addr = addr; 647 } else { 648 ip->ip_dst = inm->inm_addr; 649 } 650 651 m->m_data += sizeof(struct ip); 652 m->m_len -= sizeof(struct ip); 653 igmp = mtod(m, struct igmp *); 654 igmp->igmp_type = type; 655 igmp->igmp_code = 0; 656 igmp->igmp_group = inm->inm_addr; 657 igmp->igmp_cksum = 0; 658 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 659 m->m_data -= sizeof(struct ip); 660 m->m_len += sizeof(struct ip); 661 662 imo.imo_ifidx = inm->inm_ifidx; 663 imo.imo_ttl = 1; 664 665 /* 666 * Request loopback of the report if we are acting as a multicast 667 * router, so that the process-level routing daemon can hear it. 668 */ 669 #ifdef MROUTING 670 imo.imo_loop = (ip_mrouter != NULL); 671 #else 672 imo.imo_loop = 0; 673 #endif /* MROUTING */ 674 675 ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0); 676 677 ++igmpstat.igps_snd_reports; 678 } 679 680 /* 681 * Sysctl for igmp variables. 682 */ 683 int 684 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 685 void *newp, size_t newlen) 686 { 687 /* All sysctl names at this level are terminal. */ 688 if (namelen != 1) 689 return (ENOTDIR); 690 691 switch (name[0]) { 692 case IGMPCTL_STATS: 693 if (newp != NULL) 694 return (EPERM); 695 return (sysctl_struct(oldp, oldlenp, newp, newlen, 696 &igmpstat, sizeof(igmpstat))); 697 default: 698 if (name[0] < IGMPCTL_MAXID) 699 return (sysctl_int_arr(igmpctl_vars, name, namelen, 700 oldp, oldlenp, newp, newlen)); 701 return (ENOPROTOOPT); 702 } 703 /* NOTREACHED */ 704 } 705