1 /* $OpenBSD: igmp.c,v 1.85 2024/08/20 07:46:27 mvs 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; /* [a] shortcut for fast timer */ 100 static LIST_HEAD(, router_info) rti_head; 101 static struct mbuf *router_alert; 102 struct cpumem *igmpcounters; 103 104 int 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, running = 0; 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 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 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 if (running) { 479 membar_producer(); 480 atomic_store_int(&igmp_timers_are_running, running); 481 } 482 483 /* 484 * Pass all valid IGMP packets up to any process(es) listening 485 * on a raw IGMP socket. 486 */ 487 return rip_input(mp, offp, proto, af); 488 } 489 490 void 491 igmp_joingroup(struct in_multi *inm, struct ifnet *ifp) 492 { 493 int i, running = 0; 494 495 inm->inm_state = IGMP_IDLE_MEMBER; 496 497 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 498 (ifp->if_flags & IFF_LOOPBACK) == 0) { 499 i = rti_fill(inm); 500 igmp_sendpkt(ifp, inm, i, 0); 501 inm->inm_state = IGMP_DELAYING_MEMBER; 502 inm->inm_timer = IGMP_RANDOM_DELAY( 503 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 504 running = 1; 505 } else 506 inm->inm_timer = 0; 507 508 if (running) { 509 membar_producer(); 510 atomic_store_int(&igmp_timers_are_running, running); 511 } 512 } 513 514 void 515 igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp) 516 { 517 switch (inm->inm_state) { 518 case IGMP_DELAYING_MEMBER: 519 case IGMP_IDLE_MEMBER: 520 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 521 (ifp->if_flags & IFF_LOOPBACK) == 0) 522 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 523 igmp_sendpkt(ifp, inm, 524 IGMP_HOST_LEAVE_MESSAGE, 525 INADDR_ALLROUTERS_GROUP); 526 break; 527 case IGMP_LAZY_MEMBER: 528 case IGMP_AWAKENING_MEMBER: 529 case IGMP_SLEEPING_MEMBER: 530 break; 531 } 532 } 533 534 void 535 igmp_fasttimo(void) 536 { 537 struct ifnet *ifp; 538 int running = 0; 539 540 /* 541 * Quick check to see if any work needs to be done, in order 542 * to minimize the overhead of fasttimo processing. 543 * Variable igmp_timers_are_running is read atomically, but without 544 * lock intentionally. In case it is not set due to MP races, we may 545 * miss to check the timers. Then run the loop at next fast timeout. 546 */ 547 if (!atomic_load_int(&igmp_timers_are_running)) 548 return; 549 membar_consumer(); 550 551 NET_LOCK(); 552 553 TAILQ_FOREACH(ifp, &ifnetlist, if_list) { 554 if (igmp_checktimer(ifp)) 555 running = 1; 556 } 557 558 membar_producer(); 559 atomic_store_int(&igmp_timers_are_running, running); 560 561 NET_UNLOCK(); 562 } 563 564 int 565 igmp_checktimer(struct ifnet *ifp) 566 { 567 struct in_multi *inm; 568 struct ifmaddr *ifma; 569 int running = 0; 570 571 NET_ASSERT_LOCKED(); 572 573 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 574 if (ifma->ifma_addr->sa_family != AF_INET) 575 continue; 576 inm = ifmatoinm(ifma); 577 if (inm->inm_timer == 0) { 578 /* do nothing */ 579 } else if (--inm->inm_timer == 0) { 580 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 581 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 582 igmp_sendpkt(ifp, inm, 583 IGMP_v1_HOST_MEMBERSHIP_REPORT, 0); 584 else 585 igmp_sendpkt(ifp, inm, 586 IGMP_v2_HOST_MEMBERSHIP_REPORT, 0); 587 inm->inm_state = IGMP_IDLE_MEMBER; 588 } 589 } else { 590 running = 1; 591 } 592 } 593 594 return (running); 595 } 596 597 void 598 igmp_slowtimo(void) 599 { 600 struct router_info *rti; 601 602 NET_LOCK(); 603 604 LIST_FOREACH(rti, &rti_head, rti_list) { 605 if (rti->rti_type == IGMP_v1_ROUTER && 606 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 607 rti->rti_type = IGMP_v2_ROUTER; 608 } 609 } 610 611 NET_UNLOCK(); 612 } 613 614 void 615 igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type, 616 in_addr_t addr) 617 { 618 struct mbuf *m; 619 struct igmp *igmp; 620 struct ip *ip; 621 struct ip_moptions imo; 622 623 MGETHDR(m, M_DONTWAIT, MT_HEADER); 624 if (m == NULL) 625 return; 626 627 /* 628 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 629 * is smaller than mbuf size returned by MGETHDR. 630 */ 631 m->m_data += max_linkhdr; 632 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 633 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 634 635 ip = mtod(m, struct ip *); 636 ip->ip_tos = 0; 637 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 638 ip->ip_off = 0; 639 ip->ip_p = IPPROTO_IGMP; 640 ip->ip_src.s_addr = INADDR_ANY; 641 if (addr) { 642 ip->ip_dst.s_addr = addr; 643 } else { 644 ip->ip_dst = inm->inm_addr; 645 } 646 647 m->m_data += sizeof(struct ip); 648 m->m_len -= sizeof(struct ip); 649 igmp = mtod(m, struct igmp *); 650 igmp->igmp_type = type; 651 igmp->igmp_code = 0; 652 igmp->igmp_group = inm->inm_addr; 653 igmp->igmp_cksum = 0; 654 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 655 m->m_data -= sizeof(struct ip); 656 m->m_len += sizeof(struct ip); 657 658 m->m_pkthdr.ph_rtableid = ifp->if_rdomain; 659 imo.imo_ifidx = inm->inm_ifidx; 660 imo.imo_ttl = 1; 661 662 /* 663 * Request loopback of the report if we are acting as a multicast 664 * router, so that the process-level routing daemon can hear it. 665 */ 666 #ifdef MROUTING 667 imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL); 668 #else 669 imo.imo_loop = 0; 670 #endif /* MROUTING */ 671 672 ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0); 673 674 igmpstat_inc(igps_snd_reports); 675 } 676 677 /* 678 * Sysctl for igmp variables. 679 */ 680 int 681 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 682 void *newp, size_t newlen) 683 { 684 /* All sysctl names at this level are terminal. */ 685 if (namelen != 1) 686 return (ENOTDIR); 687 688 switch (name[0]) { 689 case IGMPCTL_STATS: 690 return (igmp_sysctl_igmpstat(oldp, oldlenp, newp)); 691 default: 692 return (EOPNOTSUPP); 693 } 694 /* NOTREACHED */ 695 } 696 697 int 698 igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp) 699 { 700 uint64_t counters[igps_ncounters]; 701 struct igmpstat igmpstat; 702 u_long *words = (u_long *)&igmpstat; 703 int i; 704 705 CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long))); 706 memset(&igmpstat, 0, sizeof igmpstat); 707 counters_read(igmpcounters, counters, nitems(counters), NULL); 708 709 for (i = 0; i < nitems(counters); i++) 710 words[i] = (u_long)counters[i]; 711 712 return (sysctl_rdstruct(oldp, oldlenp, newp, 713 &igmpstat, sizeof(igmpstat))); 714 } 715