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