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