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