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