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