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