1 /* $OpenBSD: igmp.c,v 1.26 2007/12/14 18:33:40 deraadt 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/socket.h> 81 #include <sys/protosw.h> 82 #include <sys/sysctl.h> 83 84 #include <net/if.h> 85 #include <net/route.h> 86 87 #include <netinet/in.h> 88 #include <netinet/in_var.h> 89 #include <netinet/in_systm.h> 90 #include <netinet/ip.h> 91 #include <netinet/ip_var.h> 92 #include <netinet/igmp.h> 93 #include <netinet/igmp_var.h> 94 #include <dev/rndvar.h> 95 96 #include <sys/stdarg.h> 97 98 #define IP_MULTICASTOPTS 0 99 100 int *igmpctl_vars[IGMPCTL_MAXID] = IGMPCTL_VARS; 101 102 int igmp_timers_are_running; 103 static struct router_info *rti_head; 104 struct igmpstat igmpstat; 105 106 void igmp_sendpkt(struct in_multi *, int, in_addr_t); 107 int rti_fill(struct in_multi *); 108 struct router_info * rti_find(struct ifnet *); 109 110 void 111 igmp_init() 112 { 113 114 /* 115 * To avoid byte-swapping the same value over and over again. 116 */ 117 igmp_timers_are_running = 0; 118 rti_head = 0; 119 } 120 121 /* Return -1 for error. */ 122 int 123 rti_fill(inm) 124 struct in_multi *inm; 125 { 126 struct router_info *rti; 127 128 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 129 if (rti->rti_ifp == inm->inm_ia->ia_ifp) { 130 inm->inm_rti = rti; 131 if (rti->rti_type == IGMP_v1_ROUTER) 132 return (IGMP_v1_HOST_MEMBERSHIP_REPORT); 133 else 134 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 135 } 136 } 137 138 rti = (struct router_info *)malloc(sizeof(struct router_info), 139 M_MRTABLE, M_NOWAIT); 140 if (rti == NULL) 141 return (-1); 142 rti->rti_ifp = inm->inm_ia->ia_ifp; 143 rti->rti_type = IGMP_v2_ROUTER; 144 rti->rti_next = rti_head; 145 rti_head = rti; 146 inm->inm_rti = rti; 147 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 148 } 149 150 struct router_info * 151 rti_find(ifp) 152 struct ifnet *ifp; 153 { 154 struct router_info *rti; 155 156 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 157 if (rti->rti_ifp == ifp) 158 return (rti); 159 } 160 161 rti = (struct router_info *)malloc(sizeof(struct router_info), 162 M_MRTABLE, M_NOWAIT); 163 if (rti == NULL) 164 return (NULL); 165 rti->rti_ifp = ifp; 166 rti->rti_type = IGMP_v2_ROUTER; 167 rti->rti_next = rti_head; 168 rti_head = rti; 169 return (rti); 170 } 171 172 void 173 rti_delete(ifp) 174 struct ifnet *ifp; 175 { 176 struct router_info *rti, **prti = &rti_head; 177 178 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 179 if (rti->rti_ifp == ifp) { 180 *prti = rti->rti_next; 181 free(rti, M_MRTABLE); 182 break; 183 } 184 prti = &rti->rti_next; 185 } 186 } 187 188 void 189 igmp_input(struct mbuf *m, ...) 190 { 191 int iphlen; 192 struct ifnet *ifp = m->m_pkthdr.rcvif; 193 struct ip *ip = mtod(m, struct ip *); 194 struct igmp *igmp; 195 int igmplen; 196 int minlen; 197 struct in_multi *inm; 198 struct in_multistep step; 199 struct router_info *rti; 200 struct in_ifaddr *ia; 201 int timer; 202 va_list ap; 203 204 va_start(ap, m); 205 iphlen = va_arg(ap, int); 206 va_end(ap); 207 208 ++igmpstat.igps_rcv_total; 209 210 igmplen = ntohs(ip->ip_len) - iphlen; 211 212 /* 213 * Validate lengths 214 */ 215 if (igmplen < IGMP_MINLEN) { 216 ++igmpstat.igps_rcv_tooshort; 217 m_freem(m); 218 return; 219 } 220 minlen = iphlen + IGMP_MINLEN; 221 if ((m->m_flags & M_EXT || m->m_len < minlen) && 222 (m = m_pullup(m, minlen)) == NULL) { 223 ++igmpstat.igps_rcv_tooshort; 224 return; 225 } 226 227 /* 228 * Validate checksum 229 */ 230 m->m_data += iphlen; 231 m->m_len -= iphlen; 232 igmp = mtod(m, struct igmp *); 233 if (in_cksum(m, igmplen)) { 234 ++igmpstat.igps_rcv_badsum; 235 m_freem(m); 236 return; 237 } 238 m->m_data -= iphlen; 239 m->m_len += iphlen; 240 ip = mtod(m, struct ip *); 241 242 switch (igmp->igmp_type) { 243 244 case IGMP_HOST_MEMBERSHIP_QUERY: 245 ++igmpstat.igps_rcv_queries; 246 247 if (ifp->if_flags & IFF_LOOPBACK) 248 break; 249 250 if (igmp->igmp_code == 0) { 251 rti = rti_find(ifp); 252 if (rti == NULL) { 253 m_freem(m); 254 return; 255 } 256 rti->rti_type = IGMP_v1_ROUTER; 257 rti->rti_age = 0; 258 259 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) { 260 ++igmpstat.igps_rcv_badqueries; 261 m_freem(m); 262 return; 263 } 264 265 /* 266 * Start the timers in all of our membership records 267 * for the interface on which the query arrived, 268 * except those that are already running and those 269 * that belong to a "local" group (224.0.0.X). 270 */ 271 IN_FIRST_MULTI(step, inm); 272 while (inm != NULL) { 273 if (inm->inm_ia->ia_ifp == ifp && 274 inm->inm_timer == 0 && 275 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 276 inm->inm_state = IGMP_DELAYING_MEMBER; 277 inm->inm_timer = IGMP_RANDOM_DELAY( 278 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 279 igmp_timers_are_running = 1; 280 } 281 IN_NEXT_MULTI(step, inm); 282 } 283 } else { 284 if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 285 ++igmpstat.igps_rcv_badqueries; 286 m_freem(m); 287 return; 288 } 289 290 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 291 if (timer == 0) 292 timer = 1; 293 294 /* 295 * Start the timers in all of our membership records 296 * for the interface on which the query arrived, 297 * except those that are already running and those 298 * that belong to a "local" group (224.0.0.X). For 299 * timers already running, check if they need to be 300 * reset. 301 */ 302 IN_FIRST_MULTI(step, inm); 303 while (inm != NULL) { 304 if (inm->inm_ia->ia_ifp == ifp && 305 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 306 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 307 ip->ip_dst.s_addr == inm->inm_addr.s_addr)) { 308 switch (inm->inm_state) { 309 case IGMP_DELAYING_MEMBER: 310 if (inm->inm_timer <= timer) 311 break; 312 /* FALLTHROUGH */ 313 case IGMP_IDLE_MEMBER: 314 case IGMP_LAZY_MEMBER: 315 case IGMP_AWAKENING_MEMBER: 316 inm->inm_state = 317 IGMP_DELAYING_MEMBER; 318 inm->inm_timer = 319 IGMP_RANDOM_DELAY(timer); 320 igmp_timers_are_running = 1; 321 break; 322 case IGMP_SLEEPING_MEMBER: 323 inm->inm_state = 324 IGMP_AWAKENING_MEMBER; 325 break; 326 } 327 } 328 IN_NEXT_MULTI(step, inm); 329 } 330 } 331 332 break; 333 334 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 335 ++igmpstat.igps_rcv_reports; 336 337 if (ifp->if_flags & IFF_LOOPBACK) 338 break; 339 340 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 341 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 342 ++igmpstat.igps_rcv_badreports; 343 m_freem(m); 344 return; 345 } 346 347 /* 348 * KLUDGE: if the IP source address of the report has an 349 * unspecified (i.e., zero) subnet number, as is allowed for 350 * a booting host, replace it with the correct subnet number 351 * so that a process-level multicast routing daemon can 352 * determine which subnet it arrived from. This is necessary 353 * to compensate for the lack of any way for a process to 354 * determine the arrival interface of an incoming packet. 355 */ 356 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 357 IFP_TO_IA(ifp, ia); 358 if (ia) 359 ip->ip_src.s_addr = ia->ia_subnet; 360 } 361 362 /* 363 * If we belong to the group being reported, stop 364 * our timer for that group. 365 */ 366 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 367 if (inm != NULL) { 368 inm->inm_timer = 0; 369 ++igmpstat.igps_rcv_ourreports; 370 371 switch (inm->inm_state) { 372 case IGMP_IDLE_MEMBER: 373 case IGMP_LAZY_MEMBER: 374 case IGMP_AWAKENING_MEMBER: 375 case IGMP_SLEEPING_MEMBER: 376 inm->inm_state = IGMP_SLEEPING_MEMBER; 377 break; 378 case IGMP_DELAYING_MEMBER: 379 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 380 inm->inm_state = IGMP_LAZY_MEMBER; 381 else 382 inm->inm_state = IGMP_SLEEPING_MEMBER; 383 break; 384 } 385 } 386 387 break; 388 389 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 390 #ifdef MROUTING 391 /* 392 * Make sure we don't hear our own membership report. Fast 393 * leave requires knowing that we are the only member of a 394 * group. 395 */ 396 IFP_TO_IA(ifp, ia); 397 if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr) 398 break; 399 #endif 400 401 ++igmpstat.igps_rcv_reports; 402 403 if (ifp->if_flags & IFF_LOOPBACK) 404 break; 405 406 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 407 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 408 ++igmpstat.igps_rcv_badreports; 409 m_freem(m); 410 return; 411 } 412 413 /* 414 * KLUDGE: if the IP source address of the report has an 415 * unspecified (i.e., zero) subnet number, as is allowed for 416 * a booting host, replace it with the correct subnet number 417 * so that a process-level multicast routing daemon can 418 * determine which subnet it arrived from. This is necessary 419 * to compensate for the lack of any way for a process to 420 * determine the arrival interface of an incoming packet. 421 */ 422 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 423 #ifndef MROUTING 424 IFP_TO_IA(ifp, ia); 425 #endif 426 if (ia) 427 ip->ip_src.s_addr = ia->ia_subnet; 428 } 429 430 /* 431 * If we belong to the group being reported, stop 432 * our timer for that group. 433 */ 434 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 435 if (inm != NULL) { 436 inm->inm_timer = 0; 437 ++igmpstat.igps_rcv_ourreports; 438 439 switch (inm->inm_state) { 440 case IGMP_DELAYING_MEMBER: 441 case IGMP_IDLE_MEMBER: 442 case IGMP_AWAKENING_MEMBER: 443 inm->inm_state = IGMP_LAZY_MEMBER; 444 break; 445 case IGMP_LAZY_MEMBER: 446 case IGMP_SLEEPING_MEMBER: 447 break; 448 } 449 } 450 451 break; 452 453 } 454 455 /* 456 * Pass all valid IGMP packets up to any process(es) listening 457 * on a raw IGMP socket. 458 */ 459 rip_input(m); 460 } 461 462 void 463 igmp_joingroup(inm) 464 struct in_multi *inm; 465 { 466 int i, s = splsoftnet(); 467 468 inm->inm_state = IGMP_IDLE_MEMBER; 469 470 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 471 (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0) { 472 if ((i = rti_fill(inm)) == -1) { 473 splx(s); 474 return; 475 } 476 igmp_sendpkt(inm, i, 0); 477 inm->inm_state = IGMP_DELAYING_MEMBER; 478 inm->inm_timer = IGMP_RANDOM_DELAY( 479 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 480 igmp_timers_are_running = 1; 481 } else 482 inm->inm_timer = 0; 483 splx(s); 484 } 485 486 void 487 igmp_leavegroup(inm) 488 struct in_multi *inm; 489 { 490 491 switch (inm->inm_state) { 492 case IGMP_DELAYING_MEMBER: 493 case IGMP_IDLE_MEMBER: 494 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 495 (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0) 496 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 497 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE, 498 INADDR_ALLROUTERS_GROUP); 499 break; 500 case IGMP_LAZY_MEMBER: 501 case IGMP_AWAKENING_MEMBER: 502 case IGMP_SLEEPING_MEMBER: 503 break; 504 } 505 } 506 507 void 508 igmp_fasttimo() 509 { 510 struct in_multi *inm; 511 struct in_multistep step; 512 int s; 513 514 /* 515 * Quick check to see if any work needs to be done, in order 516 * to minimize the overhead of fasttimo processing. 517 */ 518 if (!igmp_timers_are_running) 519 return; 520 521 s = splsoftnet(); 522 igmp_timers_are_running = 0; 523 IN_FIRST_MULTI(step, inm); 524 while (inm != NULL) { 525 if (inm->inm_timer == 0) { 526 /* do nothing */ 527 } else if (--inm->inm_timer == 0) { 528 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 529 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 530 igmp_sendpkt(inm, 531 IGMP_v1_HOST_MEMBERSHIP_REPORT, 0); 532 else 533 igmp_sendpkt(inm, 534 IGMP_v2_HOST_MEMBERSHIP_REPORT, 0); 535 inm->inm_state = IGMP_IDLE_MEMBER; 536 } 537 } else { 538 igmp_timers_are_running = 1; 539 } 540 IN_NEXT_MULTI(step, inm); 541 } 542 splx(s); 543 } 544 545 void 546 igmp_slowtimo() 547 { 548 struct router_info *rti; 549 int s; 550 551 s = splsoftnet(); 552 for (rti = rti_head; rti != 0; rti = rti->rti_next) { 553 if (rti->rti_type == IGMP_v1_ROUTER && 554 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 555 rti->rti_type = IGMP_v2_ROUTER; 556 } 557 } 558 splx(s); 559 } 560 561 void 562 igmp_sendpkt(inm, type, addr) 563 struct in_multi *inm; 564 int type; 565 in_addr_t addr; 566 { 567 struct mbuf *m; 568 struct igmp *igmp; 569 struct ip *ip; 570 struct ip_moptions imo; 571 #ifdef MROUTING 572 extern struct socket *ip_mrouter; 573 #endif /* MROUTING */ 574 575 MGETHDR(m, M_DONTWAIT, MT_HEADER); 576 if (m == NULL) 577 return; 578 /* 579 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 580 * is smaller than mbuf size returned by MGETHDR. 581 */ 582 m->m_data += max_linkhdr; 583 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 584 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 585 586 ip = mtod(m, struct ip *); 587 ip->ip_tos = 0; 588 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 589 ip->ip_off = 0; 590 ip->ip_p = IPPROTO_IGMP; 591 ip->ip_src.s_addr = INADDR_ANY; 592 if (addr) { 593 ip->ip_dst.s_addr = addr; 594 } else { 595 ip->ip_dst = inm->inm_addr; 596 } 597 598 m->m_data += sizeof(struct ip); 599 m->m_len -= sizeof(struct ip); 600 igmp = mtod(m, struct igmp *); 601 igmp->igmp_type = type; 602 igmp->igmp_code = 0; 603 igmp->igmp_group = inm->inm_addr; 604 igmp->igmp_cksum = 0; 605 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 606 m->m_data -= sizeof(struct ip); 607 m->m_len += sizeof(struct ip); 608 609 imo.imo_multicast_ifp = inm->inm_ia->ia_ifp; 610 imo.imo_multicast_ttl = 1; 611 #ifdef RSVP_ISI 612 imo.imo_multicast_vif = -1; 613 #endif 614 /* 615 * Request loopback of the report if we are acting as a multicast 616 * router, so that the process-level routing daemon can hear it. 617 */ 618 #ifdef MROUTING 619 imo.imo_multicast_loop = (ip_mrouter != NULL); 620 #else 621 imo.imo_multicast_loop = 0; 622 #endif /* MROUTING */ 623 624 ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS, 625 &imo, (void *)NULL); 626 627 ++igmpstat.igps_snd_reports; 628 } 629 630 /* 631 * Sysctl for igmp variables. 632 */ 633 int 634 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 635 void *newp, size_t newlen) 636 { 637 /* All sysctl names at this level are terminal. */ 638 if (namelen != 1) 639 return (ENOTDIR); 640 641 switch (name[0]) { 642 case IGMPCTL_STATS: 643 if (newp != NULL) 644 return (EPERM); 645 return (sysctl_struct(oldp, oldlenp, newp, newlen, 646 &igmpstat, sizeof(igmpstat))); 647 default: 648 if (name[0] < IGMPCTL_MAXID) 649 return (sysctl_int_arr(igmpctl_vars, name, namelen, 650 oldp, oldlenp, newp, newlen)); 651 return (ENOPROTOOPT); 652 } 653 /* NOTREACHED */ 654 } 655