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