1 /* 2 * Copyright (c) 1988 Stephen Deering. 3 * Copyright (c) 1992, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Stephen Deering of Stanford University. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * @(#)igmp.c 8.1 (Berkeley) 7/19/93 38 * $Id: igmp.c,v 1.5 1994/09/14 03:10:07 wollman Exp $ 39 */ 40 41 /* 42 * Internet Group Management Protocol (IGMP) routines. 43 * 44 * Written by Steve Deering, Stanford, May 1988. 45 * Modified by Rosen Sharma, Stanford, Aug 1994. 46 * 47 * MULTICAST 1.4 48 */ 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/mbuf.h> 53 #include <sys/socket.h> 54 #include <sys/protosw.h> 55 56 #include <net/if.h> 57 #include <net/route.h> 58 59 #include <netinet/in.h> 60 #include <netinet/in_var.h> 61 #include <netinet/in_systm.h> 62 #include <netinet/ip.h> 63 #include <netinet/ip_var.h> 64 #include <netinet/igmp.h> 65 #include <netinet/igmp_var.h> 66 67 extern struct ifnet loif; 68 69 struct igmpstat igmpstat; 70 71 static int igmp_timers_are_running = 0; 72 static u_long igmp_all_hosts_group; 73 static struct router_info *Head = 0; 74 75 static void igmp_sendpkt(struct in_multi *, int); 76 static void igmp_sendleave(struct in_multi *); 77 78 void 79 igmp_init() 80 { 81 /* 82 * To avoid byte-swapping the same value over and over again. 83 */ 84 igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 85 Head = (struct router_info *) 0; 86 } 87 88 int 89 fill_rti(inm) 90 struct in_multi *inm; 91 { 92 register struct router_info *rti = Head; 93 94 #ifdef IGMP_DEBUG 95 printf("[igmp.c, _fill_rti] --> entering \n"); 96 #endif 97 while (rti) { 98 if (rti->ifp == inm->inm_ifp){ /* ? is it ok to compare */ 99 /* pointers */ 100 inm->inm_rti = rti; 101 #ifdef IGMP_DEBUG 102 printf("[igmp.c, _fill_rti] --> found old entry \n"); 103 #endif 104 if (rti->type == IGMP_OLD_ROUTER) 105 return IGMP_HOST_MEMBERSHIP_REPORT; 106 else 107 return IGMP_HOST_NEW_MEMBERSHIP_REPORT; 108 } 109 rti = rti->next; 110 } 111 MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT); 112 rti->ifp = inm->inm_ifp; 113 rti->type = IGMP_NEW_ROUTER; 114 rti->time = IGMP_AGE_THRESHOLD; 115 rti->next = Head; 116 Head = rti; 117 inm->inm_rti = rti; 118 #ifdef IGMP_DEBUG 119 printf("[igmp.c, _fill_rti] --> created new entry \n"); 120 #endif 121 return IGMP_HOST_NEW_MEMBERSHIP_REPORT; 122 } 123 124 struct router_info * 125 find_rti(ifp) 126 struct ifnet *ifp; 127 { 128 register struct router_info *rti = Head; 129 130 #ifdef IGMP_DEBUG 131 printf("[igmp.c, _find_rti] --> entering \n"); 132 #endif 133 while (rti) { 134 if (rti->ifp == ifp){ /* ? is it ok to compare pointers */ 135 #ifdef IGMP_DEBUG 136 printf("[igmp.c, _find_rti] --> found old entry \n"); 137 #endif 138 return rti; 139 } 140 rti = rti->next; 141 } 142 MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT); 143 rti->ifp = ifp; 144 rti->type = IGMP_NEW_ROUTER; 145 rti->time = IGMP_AGE_THRESHOLD; 146 rti->next = Head; 147 Head = rti; 148 #ifdef IGMP_DEBUG 149 printf("[igmp.c, _find_rti] --> created an entry \n"); 150 #endif 151 return rti; 152 } 153 154 void 155 igmp_input(m, iphlen) 156 register struct mbuf *m; 157 register int iphlen; 158 { 159 register struct igmp *igmp; 160 register struct ip *ip; 161 register int igmplen; 162 register struct ifnet *ifp = m->m_pkthdr.rcvif; 163 register int minlen; 164 register struct in_multi *inm; 165 register struct in_ifaddr *ia; 166 struct in_multistep step; 167 struct router_info *rti; 168 169 static int timer; /** timer value in the igmp query header **/ 170 171 ++igmpstat.igps_rcv_total; 172 173 ip = mtod(m, struct ip *); 174 igmplen = ip->ip_len; 175 176 /* 177 * Validate lengths 178 */ 179 if (igmplen < IGMP_MINLEN) { 180 ++igmpstat.igps_rcv_tooshort; 181 m_freem(m); 182 return; 183 } 184 minlen = iphlen + IGMP_MINLEN; 185 if ((m->m_flags & M_EXT || m->m_len < minlen) && 186 (m = m_pullup(m, minlen)) == 0) { 187 ++igmpstat.igps_rcv_tooshort; 188 return; 189 } 190 191 /* 192 * Validate checksum 193 */ 194 m->m_data += iphlen; 195 m->m_len -= iphlen; 196 igmp = mtod(m, struct igmp *); 197 if (in_cksum(m, igmplen)) { 198 ++igmpstat.igps_rcv_badsum; 199 m_freem(m); 200 return; 201 } 202 m->m_data -= iphlen; 203 m->m_len += iphlen; 204 205 ip = mtod(m, struct ip *); 206 timer = ntohs(igmp->igmp_code); 207 rti = find_rti(ifp); 208 209 switch (igmp->igmp_type) { 210 211 case IGMP_HOST_MEMBERSHIP_QUERY: 212 ++igmpstat.igps_rcv_queries; 213 214 if (ifp == &loif) 215 break; 216 217 if (igmp->igmp_code == 0) { 218 219 rti->type = IGMP_OLD_ROUTER; rti->time = 0; 220 221 /* 222 ** Do exactly as RFC 1112 says 223 */ 224 225 if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 226 ++igmpstat.igps_rcv_badqueries; 227 m_freem(m); 228 return; 229 } 230 231 /* 232 * Start the timers in all of our membership records for 233 * the interface on which the query arrived, except those 234 * that are already running and those that belong to the 235 * "all-hosts" group. 236 */ 237 IN_FIRST_MULTI(step, inm); 238 while (inm != NULL) { 239 if (inm->inm_ifp == ifp 240 && inm->inm_timer == 0 241 && inm->inm_addr.s_addr 242 != igmp_all_hosts_group) { 243 244 inm->inm_state = IGMP_DELAYING_MEMBER; 245 inm->inm_timer = IGMP_RANDOM_DELAY( 246 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ ); 247 248 igmp_timers_are_running = 1; 249 } 250 IN_NEXT_MULTI(step, inm); 251 } 252 } else { 253 /* 254 ** New Router 255 */ 256 257 if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 258 if (!(m->m_flags & M_MCAST)) { 259 ++igmpstat.igps_rcv_badqueries; 260 m_freem(m); 261 return; 262 } 263 } 264 if (ip->ip_dst.s_addr == igmp_all_hosts_group) { 265 266 /* 267 * - Start the timers in all of our membership records 268 * for the interface on which the query arrived 269 * excl. those that belong to the "all-hosts" group. 270 * - For timers already running check if they need to 271 * be reset. 272 * - Use the igmp->igmp_code filed as the maximum 273 * delay possible 274 */ 275 IN_FIRST_MULTI(step, inm); 276 while (inm != NULL){ 277 switch(inm->inm_state){ 278 case IGMP_IDLE_MEMBER: 279 case IGMP_LAZY_MEMBER: 280 case IGMP_AWAKENING_MEMBER: 281 if (inm->inm_ifp == ifp && 282 inm->inm_addr.s_addr != 283 igmp_all_hosts_group) { 284 inm->inm_timer = IGMP_RANDOM_DELAY(timer); 285 igmp_timers_are_running = 1; 286 inm->inm_state = IGMP_DELAYING_MEMBER; 287 } 288 break; 289 case IGMP_DELAYING_MEMBER: 290 if (inm->inm_ifp == ifp && 291 (inm->inm_timer > 292 timer * PR_FASTHZ / IGMP_TIMER_SCALE) 293 && 294 inm->inm_addr.s_addr != 295 igmp_all_hosts_group) { 296 inm->inm_timer = IGMP_RANDOM_DELAY(timer); 297 igmp_timers_are_running = 1; 298 inm->inm_state = IGMP_DELAYING_MEMBER; 299 } 300 break; 301 case IGMP_SLEEPING_MEMBER: 302 inm->inm_state = IGMP_AWAKENING_MEMBER; 303 break; 304 } 305 IN_NEXT_MULTI(step, inm); 306 } 307 } else { 308 /* 309 ** group specific query 310 */ 311 312 IN_FIRST_MULTI(step, inm); 313 while (inm != NULL) { 314 if (inm->inm_addr.s_addr == ip->ip_dst.s_addr) { 315 switch(inm->inm_state ){ 316 case IGMP_IDLE_MEMBER: 317 case IGMP_LAZY_MEMBER: 318 case IGMP_AWAKENING_MEMBER: 319 inm->inm_state = IGMP_DELAYING_MEMBER; 320 if (inm->inm_ifp == ifp ) { 321 inm->inm_timer = IGMP_RANDOM_DELAY(timer); 322 igmp_timers_are_running = 1; 323 inm->inm_state = IGMP_DELAYING_MEMBER; 324 } 325 break; 326 case IGMP_DELAYING_MEMBER: 327 inm->inm_state = IGMP_DELAYING_MEMBER; 328 if (inm->inm_ifp == ifp && 329 (inm->inm_timer > 330 timer * PR_FASTHZ / IGMP_TIMER_SCALE) ) { 331 inm->inm_timer = IGMP_RANDOM_DELAY(timer); 332 igmp_timers_are_running = 1; 333 inm->inm_state = IGMP_DELAYING_MEMBER; 334 } 335 break; 336 case IGMP_SLEEPING_MEMBER: 337 inm->inm_state = IGMP_AWAKENING_MEMBER; 338 break; 339 } 340 } 341 IN_NEXT_MULTI(step, inm); 342 } 343 } 344 } 345 break; 346 347 case IGMP_HOST_MEMBERSHIP_REPORT: 348 ++igmpstat.igps_rcv_reports; 349 350 if (ifp == &loif) 351 break; 352 353 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 354 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 355 ++igmpstat.igps_rcv_badreports; 356 m_freem(m); 357 return; 358 } 359 360 /* 361 * KLUDGE: if the IP source address of the report has an 362 * unspecified (i.e., zero) subnet number, as is allowed for 363 * a booting host, replace it with the correct subnet number 364 * so that a process-level multicast routing demon can 365 * determine which subnet it arrived from. This is necessary 366 * to compensate for the lack of any way for a process to 367 * determine the arrival interface of an incoming packet. 368 */ 369 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 370 IFP_TO_IA(ifp, ia); 371 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 372 } 373 374 /* 375 * If we belong to the group being reported, stop 376 * our timer for that group. 377 */ 378 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 379 if (inm != NULL) { 380 inm->inm_timer = 0; 381 ++igmpstat.igps_rcv_ourreports; 382 } 383 384 if (inm != NULL) { 385 inm->inm_timer = 0; 386 ++igmpstat.igps_rcv_ourreports; 387 388 switch(inm->inm_state){ 389 case IGMP_IDLE_MEMBER: 390 case IGMP_LAZY_MEMBER: 391 case IGMP_AWAKENING_MEMBER: 392 case IGMP_SLEEPING_MEMBER: 393 inm->inm_state = IGMP_SLEEPING_MEMBER; 394 break; 395 case IGMP_DELAYING_MEMBER: 396 /** check this out - this was if (oldrouter) **/ 397 if (inm->inm_rti->type == IGMP_OLD_ROUTER) 398 inm->inm_state = IGMP_LAZY_MEMBER; 399 else inm->inm_state = IGMP_SLEEPING_MEMBER; 400 break; 401 } 402 } 403 404 break; 405 406 case IGMP_HOST_NEW_MEMBERSHIP_REPORT: 407 /* 408 * an new report 409 */ 410 ++igmpstat.igps_rcv_reports; 411 412 if (ifp == &loif) 413 break; 414 415 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 416 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 417 ++igmpstat.igps_rcv_badreports; 418 m_freem(m); 419 return; 420 } 421 422 /* 423 * KLUDGE: if the IP source address of the report has an 424 * unspecified (i.e., zero) subnet number, as is allowed for 425 * a booting host, replace it with the correct subnet number 426 * so that a process-level multicast routing demon can 427 * determine which subnet it arrived from. This is necessary 428 * to compensate for the lack of any way for a process to 429 * determine the arrival interface of an incoming packet. 430 */ 431 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 432 IFP_TO_IA(ifp, ia); 433 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 434 } 435 436 /* 437 * If we belong to the group being reported, stop 438 * our timer for that group. 439 */ 440 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 441 if (inm != NULL) { 442 inm->inm_timer = 0; 443 ++igmpstat.igps_rcv_ourreports; 444 445 switch(inm->inm_state){ 446 case IGMP_DELAYING_MEMBER: 447 case IGMP_IDLE_MEMBER: 448 inm->inm_state = IGMP_LAZY_MEMBER; 449 break; 450 case IGMP_AWAKENING_MEMBER: 451 inm->inm_state = IGMP_LAZY_MEMBER; 452 break; 453 case IGMP_LAZY_MEMBER: 454 case IGMP_SLEEPING_MEMBER: 455 break; 456 } 457 } 458 } 459 460 /* 461 * Pass all valid IGMP packets up to any process(es) listening 462 * on a raw IGMP socket. 463 */ 464 rip_input(m); 465 } 466 467 void 468 igmp_joingroup(inm) 469 struct in_multi *inm; 470 { 471 register int s = splnet(); 472 473 inm->inm_state = IGMP_IDLE_MEMBER; 474 475 if (inm->inm_addr.s_addr == igmp_all_hosts_group || 476 inm->inm_ifp == &loif) 477 inm->inm_timer = 0; 478 else { 479 igmp_sendpkt(inm,fill_rti(inm)); 480 inm->inm_timer = IGMP_RANDOM_DELAY( 481 IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); 482 inm->inm_state = IGMP_DELAYING_MEMBER; 483 igmp_timers_are_running = 1; 484 } 485 splx(s); 486 } 487 488 void 489 igmp_leavegroup(inm) 490 struct in_multi *inm; 491 { 492 /* 493 * No action required on leaving a group. 494 */ 495 switch(inm->inm_state){ 496 case IGMP_DELAYING_MEMBER: 497 case IGMP_IDLE_MEMBER: 498 if (!(inm->inm_addr.s_addr == igmp_all_hosts_group || 499 inm->inm_ifp == &loif)) 500 if (inm->inm_rti->type != IGMP_OLD_ROUTER) 501 igmp_sendleave(inm); 502 break; 503 case IGMP_LAZY_MEMBER: 504 case IGMP_AWAKENING_MEMBER: 505 case IGMP_SLEEPING_MEMBER: 506 break; 507 } 508 } 509 510 void 511 igmp_fasttimo() 512 { 513 register struct in_multi *inm; 514 register int s; 515 struct in_multistep step; 516 517 /* 518 * Quick check to see if any work needs to be done, in order 519 * to minimize the overhead of fasttimo processing. 520 */ 521 if (!igmp_timers_are_running) 522 return; 523 524 s = splnet(); 525 igmp_timers_are_running = 0; 526 IN_FIRST_MULTI(step, inm); 527 while (inm != NULL) { 528 if (inm->inm_timer == 0) { 529 /* do nothing */ 530 } else if (--inm->inm_timer == 0) { 531 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 532 if (inm->inm_rti->type == IGMP_OLD_ROUTER) 533 igmp_sendpkt(inm, IGMP_HOST_MEMBERSHIP_REPORT); 534 else 535 igmp_sendpkt(inm, IGMP_HOST_NEW_MEMBERSHIP_REPORT); 536 inm->inm_state = IGMP_IDLE_MEMBER; 537 } 538 } else { 539 igmp_timers_are_running = 1; 540 } 541 IN_NEXT_MULTI(step, inm); 542 } 543 splx(s); 544 } 545 546 void 547 igmp_slowtimo() 548 { 549 int s = splnet(); 550 register struct router_info *rti = Head; 551 552 #ifdef IGMP_DEBUG 553 printf("[igmp.c,_slowtimo] -- > entering \n"); 554 #endif 555 while (rti) { 556 rti->time ++; 557 if (rti->time >= IGMP_AGE_THRESHOLD){ 558 rti->type = IGMP_NEW_ROUTER; 559 rti->time = IGMP_AGE_THRESHOLD; 560 } 561 rti = rti->next; 562 } 563 #ifdef IGMP_DEBUG 564 printf("[igmp.c,_slowtimo] -- > exiting \n"); 565 #endif 566 splx(s); 567 } 568 569 static void 570 igmp_sendpkt(inm, type) 571 struct in_multi *inm; 572 int type; 573 { 574 struct mbuf *m; 575 struct igmp *igmp; 576 struct ip *ip; 577 struct ip_moptions *imo; 578 579 MGETHDR(m, M_DONTWAIT, MT_HEADER); 580 if (m == NULL) 581 return; 582 583 MALLOC(imo, struct ip_moptions *, sizeof *imo, M_IPMOPTS, M_DONTWAIT); 584 if (!imo) { 585 m_free(m); 586 return; 587 } 588 589 m->m_pkthdr.rcvif = &loif; 590 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 591 MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip)); 592 m->m_data += sizeof(struct ip); 593 m->m_len = IGMP_MINLEN; 594 igmp = mtod(m, struct igmp *); 595 igmp->igmp_type = type; 596 igmp->igmp_code = 0; 597 igmp->igmp_group = inm->inm_addr; 598 igmp->igmp_cksum = 0; 599 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 600 601 m->m_data -= sizeof(struct ip); 602 m->m_len += sizeof(struct ip); 603 ip = mtod(m, struct ip *); 604 ip->ip_tos = 0; 605 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 606 ip->ip_off = 0; 607 ip->ip_p = IPPROTO_IGMP; 608 ip->ip_src.s_addr = INADDR_ANY; 609 ip->ip_dst = igmp->igmp_group; 610 611 imo->imo_multicast_ifp = inm->inm_ifp; 612 imo->imo_multicast_ttl = 1; 613 /* 614 * Request loopback of the report if we are acting as a multicast 615 * router, so that the process-level routing demon can hear it. 616 */ 617 imo->imo_multicast_loop = (ip_mrouter != NULL); 618 619 ip_output(m, (struct mbuf *)0, (struct route *)0, 0, imo); 620 621 FREE(imo, M_IPMOPTS); 622 ++igmpstat.igps_snd_reports; 623 624 } 625 626 static void 627 igmp_sendleave(inm) 628 struct in_multi *inm; 629 { 630 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 631 } 632