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