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