1 /* $OpenBSD: igmp.c,v 1.76 2020/08/17 16:25:34 gnezdo 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; 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_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 LIST_FOREACH(rti, &rti_head, rti_list) { 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 = malloc(sizeof(*rti), M_MRTABLE, M_NOWAIT); 162 if (rti == NULL) 163 return (-1); 164 rti->rti_ifidx = inm->inm_ifidx; 165 rti->rti_type = IGMP_v2_ROUTER; 166 LIST_INSERT_HEAD(&rti_head, rti, rti_list); 167 inm->inm_rti = rti; 168 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 169 } 170 171 struct router_info * 172 rti_find(struct ifnet *ifp) 173 { 174 struct router_info *rti; 175 176 KERNEL_ASSERT_LOCKED(); 177 LIST_FOREACH(rti, &rti_head, rti_list) { 178 if (rti->rti_ifidx == ifp->if_index) 179 return (rti); 180 } 181 182 rti = malloc(sizeof(*rti), M_MRTABLE, M_NOWAIT); 183 if (rti == NULL) 184 return (NULL); 185 rti->rti_ifidx = ifp->if_index; 186 rti->rti_type = IGMP_v2_ROUTER; 187 LIST_INSERT_HEAD(&rti_head, rti, rti_list); 188 return (rti); 189 } 190 191 void 192 rti_delete(struct ifnet *ifp) 193 { 194 struct router_info *rti, *trti; 195 196 LIST_FOREACH_SAFE(rti, &rti_head, rti_list, trti) { 197 if (rti->rti_ifidx == ifp->if_index) { 198 LIST_REMOVE(rti, rti_list); 199 free(rti, M_MRTABLE, sizeof(*rti)); 200 break; 201 } 202 } 203 } 204 205 int 206 igmp_input(struct mbuf **mp, int *offp, int proto, int af) 207 { 208 struct ifnet *ifp; 209 210 igmpstat_inc(igps_rcv_total); 211 212 ifp = if_get((*mp)->m_pkthdr.ph_ifidx); 213 if (ifp == NULL) { 214 m_freemp(mp); 215 return IPPROTO_DONE; 216 } 217 218 KERNEL_LOCK(); 219 proto = igmp_input_if(ifp, mp, offp, proto, af); 220 KERNEL_UNLOCK(); 221 if_put(ifp); 222 return proto; 223 } 224 225 int 226 igmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af) 227 { 228 struct mbuf *m = *mp; 229 int iphlen = *offp; 230 struct ip *ip = mtod(m, struct ip *); 231 struct igmp *igmp; 232 int igmplen; 233 int minlen; 234 struct ifmaddr *ifma; 235 struct in_multi *inm; 236 struct router_info *rti; 237 struct in_ifaddr *ia; 238 int timer; 239 240 igmplen = ntohs(ip->ip_len) - iphlen; 241 242 /* 243 * Validate lengths 244 */ 245 if (igmplen < IGMP_MINLEN) { 246 igmpstat_inc(igps_rcv_tooshort); 247 m_freem(m); 248 return IPPROTO_DONE; 249 } 250 minlen = iphlen + IGMP_MINLEN; 251 if ((m->m_flags & M_EXT || m->m_len < minlen) && 252 (m = *mp = m_pullup(m, minlen)) == NULL) { 253 igmpstat_inc(igps_rcv_tooshort); 254 return IPPROTO_DONE; 255 } 256 257 /* 258 * Validate checksum 259 */ 260 m->m_data += iphlen; 261 m->m_len -= iphlen; 262 igmp = mtod(m, struct igmp *); 263 if (in_cksum(m, igmplen)) { 264 igmpstat_inc(igps_rcv_badsum); 265 m_freem(m); 266 return IPPROTO_DONE; 267 } 268 m->m_data -= iphlen; 269 m->m_len += iphlen; 270 ip = mtod(m, struct ip *); 271 272 switch (igmp->igmp_type) { 273 274 case IGMP_HOST_MEMBERSHIP_QUERY: 275 igmpstat_inc(igps_rcv_queries); 276 277 if (ifp->if_flags & IFF_LOOPBACK) 278 break; 279 280 if (igmp->igmp_code == 0) { 281 rti = rti_find(ifp); 282 if (rti == NULL) { 283 m_freem(m); 284 return IPPROTO_DONE; 285 } 286 rti->rti_type = IGMP_v1_ROUTER; 287 rti->rti_age = 0; 288 289 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { 290 igmpstat_inc(igps_rcv_badqueries); 291 m_freem(m); 292 return IPPROTO_DONE; 293 } 294 295 /* 296 * Start the timers in all of our membership records 297 * for the interface on which the query arrived, 298 * except those that are already running and those 299 * that belong to a "local" group (224.0.0.X). 300 */ 301 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 302 if (ifma->ifma_addr->sa_family != AF_INET) 303 continue; 304 inm = ifmatoinm(ifma); 305 if (inm->inm_timer == 0 && 306 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 307 inm->inm_state = IGMP_DELAYING_MEMBER; 308 inm->inm_timer = IGMP_RANDOM_DELAY( 309 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 310 igmp_timers_are_running = 1; 311 } 312 } 313 } else { 314 if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 315 igmpstat_inc(igps_rcv_badqueries); 316 m_freem(m); 317 return IPPROTO_DONE; 318 } 319 320 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 321 if (timer == 0) 322 timer = 1; 323 324 /* 325 * Start the timers in all of our membership records 326 * for the interface on which the query arrived, 327 * except those that are already running and those 328 * that belong to a "local" group (224.0.0.X). For 329 * timers already running, check if they need to be 330 * reset. 331 */ 332 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 333 if (ifma->ifma_addr->sa_family != AF_INET) 334 continue; 335 inm = ifmatoinm(ifma); 336 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 337 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 338 ip->ip_dst.s_addr == inm->inm_addr.s_addr)) { 339 switch (inm->inm_state) { 340 case IGMP_DELAYING_MEMBER: 341 if (inm->inm_timer <= timer) 342 break; 343 /* FALLTHROUGH */ 344 case IGMP_IDLE_MEMBER: 345 case IGMP_LAZY_MEMBER: 346 case IGMP_AWAKENING_MEMBER: 347 inm->inm_state = 348 IGMP_DELAYING_MEMBER; 349 inm->inm_timer = 350 IGMP_RANDOM_DELAY(timer); 351 igmp_timers_are_running = 1; 352 break; 353 case IGMP_SLEEPING_MEMBER: 354 inm->inm_state = 355 IGMP_AWAKENING_MEMBER; 356 break; 357 } 358 } 359 } 360 } 361 362 break; 363 364 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 365 igmpstat_inc(igps_rcv_reports); 366 367 if (ifp->if_flags & IFF_LOOPBACK) 368 break; 369 370 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 371 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 372 igmpstat_inc(igps_rcv_badreports); 373 m_freem(m); 374 return IPPROTO_DONE; 375 } 376 377 /* 378 * KLUDGE: if the IP source address of the report has an 379 * unspecified (i.e., zero) subnet number, as is allowed for 380 * a booting host, replace it with the correct subnet number 381 * so that a process-level multicast routing daemon can 382 * determine which subnet it arrived from. This is necessary 383 * to compensate for the lack of any way for a process to 384 * determine the arrival interface of an incoming packet. 385 */ 386 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 387 IFP_TO_IA(ifp, ia); 388 if (ia) 389 ip->ip_src.s_addr = ia->ia_net; 390 } 391 392 /* 393 * If we belong to the group being reported, stop 394 * our timer for that group. 395 */ 396 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 397 if (inm != NULL) { 398 inm->inm_timer = 0; 399 igmpstat_inc(igps_rcv_ourreports); 400 401 switch (inm->inm_state) { 402 case IGMP_IDLE_MEMBER: 403 case IGMP_LAZY_MEMBER: 404 case IGMP_AWAKENING_MEMBER: 405 case IGMP_SLEEPING_MEMBER: 406 inm->inm_state = IGMP_SLEEPING_MEMBER; 407 break; 408 case IGMP_DELAYING_MEMBER: 409 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 410 inm->inm_state = IGMP_LAZY_MEMBER; 411 else 412 inm->inm_state = IGMP_SLEEPING_MEMBER; 413 break; 414 } 415 } 416 417 break; 418 419 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 420 #ifdef MROUTING 421 /* 422 * Make sure we don't hear our own membership report. Fast 423 * leave requires knowing that we are the only member of a 424 * group. 425 */ 426 IFP_TO_IA(ifp, ia); 427 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr) 428 break; 429 #endif 430 431 igmpstat_inc(igps_rcv_reports); 432 433 if (ifp->if_flags & IFF_LOOPBACK) 434 break; 435 436 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 437 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 438 igmpstat_inc(igps_rcv_badreports); 439 m_freem(m); 440 return IPPROTO_DONE; 441 } 442 443 /* 444 * KLUDGE: if the IP source address of the report has an 445 * unspecified (i.e., zero) subnet number, as is allowed for 446 * a booting host, replace it with the correct subnet number 447 * so that a process-level multicast routing daemon can 448 * determine which subnet it arrived from. This is necessary 449 * to compensate for the lack of any way for a process to 450 * determine the arrival interface of an incoming packet. 451 */ 452 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 453 #ifndef MROUTING 454 IFP_TO_IA(ifp, ia); 455 #endif 456 if (ia) 457 ip->ip_src.s_addr = ia->ia_net; 458 } 459 460 /* 461 * If we belong to the group being reported, stop 462 * our timer for that group. 463 */ 464 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 465 if (inm != NULL) { 466 inm->inm_timer = 0; 467 igmpstat_inc(igps_rcv_ourreports); 468 469 switch (inm->inm_state) { 470 case IGMP_DELAYING_MEMBER: 471 case IGMP_IDLE_MEMBER: 472 case IGMP_AWAKENING_MEMBER: 473 inm->inm_state = IGMP_LAZY_MEMBER; 474 break; 475 case IGMP_LAZY_MEMBER: 476 case IGMP_SLEEPING_MEMBER: 477 break; 478 } 479 } 480 481 break; 482 483 } 484 485 /* 486 * Pass all valid IGMP packets up to any process(es) listening 487 * on a raw IGMP socket. 488 */ 489 return rip_input(mp, offp, proto, af); 490 } 491 492 void 493 igmp_joingroup(struct in_multi *inm) 494 { 495 struct ifnet* ifp; 496 int i; 497 498 ifp = if_get(inm->inm_ifidx); 499 500 inm->inm_state = IGMP_IDLE_MEMBER; 501 502 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 503 ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) { 504 if ((i = rti_fill(inm)) == -1) 505 goto out; 506 507 igmp_sendpkt(ifp, inm, i, 0); 508 inm->inm_state = IGMP_DELAYING_MEMBER; 509 inm->inm_timer = IGMP_RANDOM_DELAY( 510 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 511 igmp_timers_are_running = 1; 512 } else 513 inm->inm_timer = 0; 514 515 out: 516 if_put(ifp); 517 } 518 519 void 520 igmp_leavegroup(struct in_multi *inm) 521 { 522 struct ifnet* ifp; 523 524 ifp = if_get(inm->inm_ifidx); 525 526 switch (inm->inm_state) { 527 case IGMP_DELAYING_MEMBER: 528 case IGMP_IDLE_MEMBER: 529 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 530 ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) 531 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 532 igmp_sendpkt(ifp, inm, 533 IGMP_HOST_LEAVE_MESSAGE, 534 INADDR_ALLROUTERS_GROUP); 535 break; 536 case IGMP_LAZY_MEMBER: 537 case IGMP_AWAKENING_MEMBER: 538 case IGMP_SLEEPING_MEMBER: 539 break; 540 } 541 if_put(ifp); 542 } 543 544 void 545 igmp_fasttimo(void) 546 { 547 struct ifnet *ifp; 548 549 NET_LOCK(); 550 551 /* 552 * Quick check to see if any work needs to be done, in order 553 * to minimize the overhead of fasttimo processing. 554 */ 555 if (!igmp_timers_are_running) 556 goto out; 557 558 igmp_timers_are_running = 0; 559 TAILQ_FOREACH(ifp, &ifnet, if_list) 560 igmp_checktimer(ifp); 561 562 out: 563 NET_UNLOCK(); 564 } 565 566 567 void 568 igmp_checktimer(struct ifnet *ifp) 569 { 570 struct in_multi *inm; 571 struct ifmaddr *ifma; 572 573 NET_ASSERT_LOCKED(); 574 575 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { 576 if (ifma->ifma_addr->sa_family != AF_INET) 577 continue; 578 inm = ifmatoinm(ifma); 579 if (inm->inm_timer == 0) { 580 /* do nothing */ 581 } else if (--inm->inm_timer == 0) { 582 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 583 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 584 igmp_sendpkt(ifp, inm, 585 IGMP_v1_HOST_MEMBERSHIP_REPORT, 0); 586 else 587 igmp_sendpkt(ifp, inm, 588 IGMP_v2_HOST_MEMBERSHIP_REPORT, 0); 589 inm->inm_state = IGMP_IDLE_MEMBER; 590 } 591 } else { 592 igmp_timers_are_running = 1; 593 } 594 } 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 if (newp != NULL) 691 return (EPERM); 692 return (igmp_sysctl_igmpstat(oldp, oldlenp, newp)); 693 default: 694 return (EOPNOTSUPP); 695 } 696 /* NOTREACHED */ 697 } 698 699 int 700 igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp) 701 { 702 uint64_t counters[igps_ncounters]; 703 struct igmpstat igmpstat; 704 u_long *words = (u_long *)&igmpstat; 705 int i; 706 707 CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long))); 708 memset(&igmpstat, 0, sizeof igmpstat); 709 counters_read(igmpcounters, counters, nitems(counters)); 710 711 for (i = 0; i < nitems(counters); i++) 712 words[i] = (u_long)counters[i]; 713 714 return (sysctl_rdstruct(oldp, oldlenp, newp, 715 &igmpstat, sizeof(igmpstat))); 716 } 717