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