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