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