1 /* $NetBSD: igmp.c,v 1.36 2003/08/22 21:53:02 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Internet Group Management Protocol (IGMP) routines. 34 * 35 * Written by Steve Deering, Stanford, May 1988. 36 * Modified by Rosen Sharma, Stanford, Aug 1994. 37 * Modified by Bill Fenner, Xerox PARC, Feb 1995. 38 * 39 * MULTICAST Revision: 1.3 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.36 2003/08/22 21:53:02 itojun Exp $"); 44 45 #include "opt_mrouting.h" 46 47 #include <sys/param.h> 48 #include <sys/mbuf.h> 49 #include <sys/socket.h> 50 #include <sys/protosw.h> 51 #include <sys/systm.h> 52 53 #include <net/if.h> 54 #include <net/route.h> 55 56 #include <netinet/in.h> 57 #include <netinet/in_var.h> 58 #include <netinet/in_systm.h> 59 #include <netinet/ip.h> 60 #include <netinet/ip_var.h> 61 #include <netinet/igmp.h> 62 #include <netinet/igmp_var.h> 63 64 #include <machine/stdarg.h> 65 66 #define IP_MULTICASTOPTS 0 67 68 struct pool igmp_rti_pool; 69 struct igmpstat igmpstat; 70 int igmp_timers_are_running; 71 static LIST_HEAD(, router_info) rti_head = LIST_HEAD_INITIALIZER(rti_head); 72 73 void igmp_sendpkt __P((struct in_multi *, int)); 74 static int rti_fill __P((struct in_multi *)); 75 static struct router_info *rti_find __P((struct ifnet *)); 76 static void rti_delete(struct ifnet *); 77 78 void 79 igmp_init() 80 { 81 igmp_timers_are_running = 0; 82 pool_init(&igmp_rti_pool, sizeof(struct router_info), 0, 0, 0, "igmppl", 83 NULL); 84 } 85 86 static int 87 rti_fill(inm) 88 struct in_multi *inm; 89 { 90 struct router_info *rti; 91 92 LIST_FOREACH(rti, &rti_head, rti_link) { 93 if (rti->rti_ifp == inm->inm_ifp) { 94 inm->inm_rti = rti; 95 if (rti->rti_type == IGMP_v1_ROUTER) 96 return (IGMP_v1_HOST_MEMBERSHIP_REPORT); 97 else 98 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 99 } 100 } 101 102 rti = pool_get(&igmp_rti_pool, PR_NOWAIT); 103 if (rti == NULL) 104 return 0; 105 rti->rti_ifp = inm->inm_ifp; 106 rti->rti_type = IGMP_v2_ROUTER; 107 LIST_INSERT_HEAD(&rti_head, rti, rti_link); 108 inm->inm_rti = rti; 109 return (IGMP_v2_HOST_MEMBERSHIP_REPORT); 110 } 111 112 static struct router_info * 113 rti_find(ifp) 114 struct ifnet *ifp; 115 { 116 struct router_info *rti; 117 118 LIST_FOREACH(rti, &rti_head, rti_link) { 119 if (rti->rti_ifp == ifp) 120 return (rti); 121 } 122 123 rti = pool_get(&igmp_rti_pool, PR_NOWAIT); 124 if (rti == NULL) 125 return NULL; 126 rti->rti_ifp = ifp; 127 rti->rti_type = IGMP_v2_ROUTER; 128 LIST_INSERT_HEAD(&rti_head, rti, rti_link); 129 return (rti); 130 } 131 132 static void 133 rti_delete(ifp) 134 struct ifnet *ifp; 135 { 136 struct router_info *rti; 137 138 LIST_FOREACH(rti, &rti_head, rti_link) { 139 if (rti->rti_ifp == ifp) { 140 LIST_REMOVE(rti, rti_link); 141 pool_put(&igmp_rti_pool, rti); 142 return; 143 } 144 } 145 } 146 147 void 148 #if __STDC__ 149 igmp_input(struct mbuf *m, ...) 150 #else 151 igmp_input(m, va_alist) 152 struct mbuf *m; 153 va_dcl 154 #endif 155 { 156 int proto; 157 int iphlen; 158 struct ifnet *ifp = m->m_pkthdr.rcvif; 159 struct ip *ip = mtod(m, struct ip *); 160 struct igmp *igmp; 161 u_int minlen; 162 struct in_multi *inm; 163 struct in_multistep step; 164 struct router_info *rti; 165 struct in_ifaddr *ia; 166 u_int timer; 167 va_list ap; 168 u_int16_t ip_len; 169 170 va_start(ap, m); 171 iphlen = va_arg(ap, int); 172 proto = va_arg(ap, int); 173 va_end(ap); 174 175 ++igmpstat.igps_rcv_total; 176 177 /* 178 * Validate lengths 179 */ 180 minlen = iphlen + IGMP_MINLEN; 181 ip_len = ntohs(ip->ip_len); 182 if (ip_len < minlen) { 183 ++igmpstat.igps_rcv_tooshort; 184 m_freem(m); 185 return; 186 } 187 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0) 188 || m->m_len < minlen) { 189 if ((m = m_pullup(m, minlen)) == 0) { 190 ++igmpstat.igps_rcv_tooshort; 191 return; 192 } 193 ip = mtod(m, struct ip *); 194 } 195 196 /* 197 * Validate checksum 198 */ 199 m->m_data += iphlen; 200 m->m_len -= iphlen; 201 igmp = mtod(m, struct igmp *); 202 /* No need to assert alignment here. */ 203 if (in_cksum(m, ip_len - iphlen)) { 204 ++igmpstat.igps_rcv_badsum; 205 m_freem(m); 206 return; 207 } 208 m->m_data -= iphlen; 209 m->m_len += iphlen; 210 211 switch (igmp->igmp_type) { 212 213 case IGMP_HOST_MEMBERSHIP_QUERY: 214 ++igmpstat.igps_rcv_queries; 215 216 if (ifp->if_flags & IFF_LOOPBACK) 217 break; 218 219 if (igmp->igmp_code == 0) { 220 rti = rti_find(ifp); 221 if (rti == NULL) 222 break; 223 rti->rti_type = IGMP_v1_ROUTER; 224 rti->rti_age = 0; 225 226 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_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 234 * for the interface on which the query arrived, 235 * except those that are already running and those 236 * that belong to a "local" group (224.0.0.X). 237 */ 238 IN_FIRST_MULTI(step, inm); 239 while (inm != NULL) { 240 if (inm->inm_ifp == ifp && 241 inm->inm_timer == 0 && 242 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) { 243 inm->inm_state = IGMP_DELAYING_MEMBER; 244 inm->inm_timer = IGMP_RANDOM_DELAY( 245 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 246 igmp_timers_are_running = 1; 247 } 248 IN_NEXT_MULTI(step, inm); 249 } 250 } else { 251 if (!IN_MULTICAST(ip->ip_dst.s_addr)) { 252 ++igmpstat.igps_rcv_badqueries; 253 m_freem(m); 254 return; 255 } 256 257 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 258 if (timer == 0) 259 timer =1; 260 261 /* 262 * Start the timers in all of our membership records 263 * for the interface on which the query arrived, 264 * except those that are already running and those 265 * that belong to a "local" group (224.0.0.X). For 266 * timers already running, check if they need to be 267 * reset. 268 */ 269 IN_FIRST_MULTI(step, inm); 270 while (inm != NULL) { 271 if (inm->inm_ifp == ifp && 272 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 273 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP || 274 in_hosteq(ip->ip_dst, inm->inm_addr))) { 275 switch (inm->inm_state) { 276 case IGMP_DELAYING_MEMBER: 277 if (inm->inm_timer <= timer) 278 break; 279 /* FALLTHROUGH */ 280 case IGMP_IDLE_MEMBER: 281 case IGMP_LAZY_MEMBER: 282 case IGMP_AWAKENING_MEMBER: 283 inm->inm_state = 284 IGMP_DELAYING_MEMBER; 285 inm->inm_timer = 286 IGMP_RANDOM_DELAY(timer); 287 igmp_timers_are_running = 1; 288 break; 289 case IGMP_SLEEPING_MEMBER: 290 inm->inm_state = 291 IGMP_AWAKENING_MEMBER; 292 break; 293 } 294 } 295 IN_NEXT_MULTI(step, inm); 296 } 297 } 298 299 break; 300 301 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 302 ++igmpstat.igps_rcv_reports; 303 304 if (ifp->if_flags & IFF_LOOPBACK) 305 break; 306 307 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 308 !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 309 ++igmpstat.igps_rcv_badreports; 310 m_freem(m); 311 return; 312 } 313 314 /* 315 * KLUDGE: if the IP source address of the report has an 316 * unspecified (i.e., zero) subnet number, as is allowed for 317 * a booting host, replace it with the correct subnet number 318 * so that a process-level multicast routing daemon can 319 * determine which subnet it arrived from. This is necessary 320 * to compensate for the lack of any way for a process to 321 * determine the arrival interface of an incoming packet. 322 */ 323 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 324 IFP_TO_IA(ifp, ia); /* XXX */ 325 if (ia) 326 ip->ip_src.s_addr = ia->ia_subnet; 327 } 328 329 /* 330 * If we belong to the group being reported, stop 331 * our timer for that group. 332 */ 333 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 334 if (inm != NULL) { 335 inm->inm_timer = 0; 336 ++igmpstat.igps_rcv_ourreports; 337 338 switch (inm->inm_state) { 339 case IGMP_IDLE_MEMBER: 340 case IGMP_LAZY_MEMBER: 341 case IGMP_AWAKENING_MEMBER: 342 case IGMP_SLEEPING_MEMBER: 343 inm->inm_state = IGMP_SLEEPING_MEMBER; 344 break; 345 case IGMP_DELAYING_MEMBER: 346 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 347 inm->inm_state = IGMP_LAZY_MEMBER; 348 else 349 inm->inm_state = IGMP_SLEEPING_MEMBER; 350 break; 351 } 352 } 353 354 break; 355 356 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 357 #ifdef MROUTING 358 /* 359 * Make sure we don't hear our own membership report. Fast 360 * leave requires knowing that we are the only member of a 361 * group. 362 */ 363 IFP_TO_IA(ifp, ia); /* XXX */ 364 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) 365 break; 366 #endif 367 368 ++igmpstat.igps_rcv_reports; 369 370 if (ifp->if_flags & IFF_LOOPBACK) 371 break; 372 373 if (!IN_MULTICAST(igmp->igmp_group.s_addr) || 374 !in_hosteq(igmp->igmp_group, ip->ip_dst)) { 375 ++igmpstat.igps_rcv_badreports; 376 m_freem(m); 377 return; 378 } 379 380 /* 381 * KLUDGE: if the IP source address of the report has an 382 * unspecified (i.e., zero) subnet number, as is allowed for 383 * a booting host, replace it with the correct subnet number 384 * so that a process-level multicast routing daemon can 385 * determine which subnet it arrived from. This is necessary 386 * to compensate for the lack of any way for a process to 387 * determine the arrival interface of an incoming packet. 388 */ 389 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { 390 #ifndef MROUTING 391 IFP_TO_IA(ifp, ia); /* XXX */ 392 #endif 393 if (ia) 394 ip->ip_src.s_addr = ia->ia_subnet; 395 } 396 397 /* 398 * If we belong to the group being reported, stop 399 * our timer for that group. 400 */ 401 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 402 if (inm != NULL) { 403 inm->inm_timer = 0; 404 ++igmpstat.igps_rcv_ourreports; 405 406 switch (inm->inm_state) { 407 case IGMP_DELAYING_MEMBER: 408 case IGMP_IDLE_MEMBER: 409 case IGMP_AWAKENING_MEMBER: 410 inm->inm_state = IGMP_LAZY_MEMBER; 411 break; 412 case IGMP_LAZY_MEMBER: 413 case IGMP_SLEEPING_MEMBER: 414 break; 415 } 416 } 417 418 break; 419 420 } 421 422 /* 423 * Pass all valid IGMP packets up to any process(es) listening 424 * on a raw IGMP socket. 425 */ 426 rip_input(m, iphlen, proto); 427 return; 428 } 429 430 int 431 igmp_joingroup(inm) 432 struct in_multi *inm; 433 { 434 int report_type; 435 int s = splsoftnet(); 436 437 inm->inm_state = IGMP_IDLE_MEMBER; 438 439 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 440 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) { 441 report_type = rti_fill(inm); 442 if (report_type == 0) 443 return ENOMEM; 444 igmp_sendpkt(inm, report_type); 445 inm->inm_state = IGMP_DELAYING_MEMBER; 446 inm->inm_timer = IGMP_RANDOM_DELAY( 447 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 448 igmp_timers_are_running = 1; 449 } else 450 inm->inm_timer = 0; 451 splx(s); 452 return 0; 453 } 454 455 void 456 igmp_leavegroup(inm) 457 struct in_multi *inm; 458 { 459 460 switch (inm->inm_state) { 461 case IGMP_DELAYING_MEMBER: 462 case IGMP_IDLE_MEMBER: 463 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && 464 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) 465 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) 466 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 467 break; 468 case IGMP_LAZY_MEMBER: 469 case IGMP_AWAKENING_MEMBER: 470 case IGMP_SLEEPING_MEMBER: 471 break; 472 } 473 } 474 475 void 476 igmp_fasttimo() 477 { 478 struct in_multi *inm; 479 struct in_multistep step; 480 int s; 481 482 /* 483 * Quick check to see if any work needs to be done, in order 484 * to minimize the overhead of fasttimo processing. 485 */ 486 if (!igmp_timers_are_running) 487 return; 488 489 s = splsoftnet(); 490 igmp_timers_are_running = 0; 491 IN_FIRST_MULTI(step, inm); 492 while (inm != NULL) { 493 if (inm->inm_timer == 0) { 494 /* do nothing */ 495 } else if (--inm->inm_timer == 0) { 496 if (inm->inm_state == IGMP_DELAYING_MEMBER) { 497 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) 498 igmp_sendpkt(inm, 499 IGMP_v1_HOST_MEMBERSHIP_REPORT); 500 else 501 igmp_sendpkt(inm, 502 IGMP_v2_HOST_MEMBERSHIP_REPORT); 503 inm->inm_state = IGMP_IDLE_MEMBER; 504 } 505 } else { 506 igmp_timers_are_running = 1; 507 } 508 IN_NEXT_MULTI(step, inm); 509 } 510 splx(s); 511 } 512 513 void 514 igmp_slowtimo() 515 { 516 struct router_info *rti; 517 int s; 518 519 s = splsoftnet(); 520 LIST_FOREACH(rti, &rti_head, rti_link) { 521 if (rti->rti_type == IGMP_v1_ROUTER && 522 ++rti->rti_age >= IGMP_AGE_THRESHOLD) { 523 rti->rti_type = IGMP_v2_ROUTER; 524 } 525 } 526 splx(s); 527 } 528 529 void 530 igmp_sendpkt(inm, type) 531 struct in_multi *inm; 532 int type; 533 { 534 struct mbuf *m; 535 struct igmp *igmp; 536 struct ip *ip; 537 struct ip_moptions imo; 538 #ifdef MROUTING 539 extern struct socket *ip_mrouter; 540 #endif /* MROUTING */ 541 542 MGETHDR(m, M_DONTWAIT, MT_HEADER); 543 if (m == NULL) 544 return; 545 /* 546 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN 547 * is smaller than mbuf size returned by MGETHDR. 548 */ 549 m->m_data += max_linkhdr; 550 m->m_len = sizeof(struct ip) + IGMP_MINLEN; 551 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 552 553 ip = mtod(m, struct ip *); 554 ip->ip_tos = 0; 555 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN); 556 ip->ip_off = htons(0); 557 ip->ip_p = IPPROTO_IGMP; 558 ip->ip_src = zeroin_addr; 559 ip->ip_dst = inm->inm_addr; 560 561 m->m_data += sizeof(struct ip); 562 m->m_len -= sizeof(struct ip); 563 igmp = mtod(m, struct igmp *); 564 igmp->igmp_type = type; 565 igmp->igmp_code = 0; 566 igmp->igmp_group = inm->inm_addr; 567 igmp->igmp_cksum = 0; 568 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 569 m->m_data -= sizeof(struct ip); 570 m->m_len += sizeof(struct ip); 571 572 imo.imo_multicast_ifp = inm->inm_ifp; 573 imo.imo_multicast_ttl = 1; 574 #ifdef RSVP_ISI 575 imo.imo_multicast_vif = -1; 576 #endif 577 /* 578 * Request loopback of the report if we are acting as a multicast 579 * router, so that the process-level routing demon can hear it. 580 */ 581 #ifdef MROUTING 582 imo.imo_multicast_loop = (ip_mrouter != NULL); 583 #else 584 imo.imo_multicast_loop = 0; 585 #endif /* MROUTING */ 586 587 ip_output(m, (struct mbuf *)NULL, (struct route *)NULL, 588 IP_MULTICASTOPTS, &imo, (struct socket *)NULL); 589 590 ++igmpstat.igps_snd_reports; 591 } 592 593 void 594 igmp_purgeif(ifp) 595 struct ifnet *ifp; 596 { 597 598 rti_delete(ifp); 599 } 600