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