1/* 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * Copyright (c) 1988 Stephen Deering. 30 * Copyright (c) 1992, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * This code is derived from software contributed to Berkeley by 34 * Stephen Deering of Stanford University. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. All advertising materials mentioning features or use of this software 45 * must display the following acknowledgement: 46 * This product includes software developed by the University of 47 * California, Berkeley and its contributors. 48 * 4. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 * 64 * @(#)igmp.c 8.1 (Berkeley) 7/19/93 65 */ 66/* 67 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 68 * support for mandatory and extensible security protections. This notice 69 * is included in support of clause 2.2 (b) of the Apple Public License, 70 * Version 2.0. 71 */ 72 73/* 74 * Internet Group Management Protocol (IGMP) routines. 75 * 76 * Written by Steve Deering, Stanford, May 1988. 77 * Modified by Rosen Sharma, Stanford, Aug 1994. 78 * Modified by Bill Fenner, Xerox PARC, Feb 1995. 79 * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995. 80 * 81 * MULTICAST Revision: 3.5.1.4 82 */ 83 84#include <sys/param.h> 85#include <sys/systm.h> 86#include <sys/malloc.h> 87#include <sys/mbuf.h> 88#include <sys/socket.h> 89#include <sys/protosw.h> 90#include <sys/kernel.h> 91#include <sys/sysctl.h> 92 93#include <net/if.h> 94#include <net/route.h> 95 96#include <netinet/in.h> 97#include <netinet/in_var.h> 98#include <netinet/in_systm.h> 99#include <netinet/ip.h> 100#include <netinet/ip_var.h> 101#include <netinet/igmp.h> 102#include <netinet/igmp_var.h> 103 104#if CONFIG_MACF_NET 105#include <security/mac_framework.h> 106#endif 107 108#ifndef __APPLE__ 109static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); 110#endif 111 112static struct router_info * 113 find_rti(struct ifnet *ifp, int wait); 114 115static struct igmpstat igmpstat; 116 117SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD, 118 &igmpstat, igmpstat, ""); 119 120static int igmp_timers_are_running; 121static u_long igmp_all_hosts_group; 122static u_long igmp_all_rtrs_group; 123static struct mbuf *router_alert; 124static struct router_info *Head; 125 126static void igmp_sendpkt(struct in_multi *, int, unsigned long); 127 128void 129igmp_init(void) 130{ 131 struct ipoption *ra; 132 133 /* 134 * To avoid byte-swapping the same value over and over again. 135 */ 136 igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 137 igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP); 138 139 igmp_timers_are_running = 0; 140 141 /* 142 * Construct a Router Alert option to use in outgoing packets 143 */ 144 MGET(router_alert, M_DONTWAIT, MT_DATA); 145 ra = mtod(router_alert, struct ipoption *); 146 ra->ipopt_dst.s_addr = 0; 147 ra->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ 148 ra->ipopt_list[1] = 0x04; /* 4 bytes long */ 149 ra->ipopt_list[2] = 0x00; 150 ra->ipopt_list[3] = 0x00; 151 router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; 152 153 Head = (struct router_info *) 0; 154} 155 156static struct router_info * 157find_rti( 158 struct ifnet *ifp, int wait) 159{ 160 struct router_info *rti = Head; 161 162 163#if IGMP_DEBUG 164 printf("[igmp.c, _find_rti] --> entering \n"); 165#endif 166 while (rti) { 167 if (rti->rti_ifp == ifp) { 168#if IGMP_DEBUG 169 printf("[igmp.c, _find_rti] --> found old entry \n"); 170#endif 171 return rti; 172 } 173 rti = rti->rti_next; 174 } 175 176 MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, wait); 177 if (rti != NULL) 178 { 179 rti->rti_ifp = ifp; 180 rti->rti_type = IGMP_V2_ROUTER; 181 rti->rti_time = 0; 182 rti->rti_next = Head; 183 Head = rti; 184 } 185#if IGMP_DEBUG 186 if (rti) printf("[igmp.c, _find_rti] --> created an entry \n"); 187#endif 188 return rti; 189} 190 191void 192igmp_input( 193 struct mbuf *m, 194 int iphlen) 195{ 196 struct igmp *igmp; 197 struct ip *ip; 198 int igmplen; 199 struct ifnet *ifp = m->m_pkthdr.rcvif; 200 int minlen; 201 struct in_multi *inm; 202 struct in_ifaddr *ia; 203 struct in_multistep step; 204 struct router_info *rti; 205 206 int timer; /** timer value in the igmp query header **/ 207 208 ++igmpstat.igps_rcv_total; 209 210 ip = mtod(m, struct ip *); 211 igmplen = ip->ip_len; 212 213 /* 214 * Validate lengths 215 */ 216 if (igmplen < IGMP_MINLEN) { 217 ++igmpstat.igps_rcv_tooshort; 218 m_freem(m); 219 return; 220 } 221 minlen = iphlen + IGMP_MINLEN; 222 if ((m->m_flags & M_EXT || m->m_len < minlen) && 223 (m = m_pullup(m, minlen)) == 0) { 224 ++igmpstat.igps_rcv_tooshort; 225 return; 226 } 227 228 /* 229 * Validate checksum 230 */ 231 m->m_data += iphlen; 232 m->m_len -= iphlen; 233 igmp = mtod(m, struct igmp *); 234 if (in_cksum(m, igmplen)) { 235 ++igmpstat.igps_rcv_badsum; 236 m_freem(m); 237 return; 238 } 239 m->m_data -= iphlen; 240 m->m_len += iphlen; 241 242 ip = mtod(m, struct ip *); 243 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 244 if (timer == 0) 245 timer = 1; 246 rti = find_rti(ifp, M_NOWAIT); 247 if (rti == NULL) { 248 m_freem(m); 249 return; 250 } 251 252 /* 253 * In the IGMPv2 specification, there are 3 states and a flag. 254 * 255 * In Non-Member state, we simply don't have a membership record. 256 * In Delaying Member state, our timer is running (inm->inm_timer) 257 * In Idle Member state, our timer is not running (inm->inm_timer==0) 258 * 259 * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if 260 * we have heard a report from another member, or IGMP_IREPORTEDLAST 261 * if I sent the last report. 262 */ 263 switch (igmp->igmp_type) { 264 265 case IGMP_MEMBERSHIP_QUERY: 266 ++igmpstat.igps_rcv_queries; 267 268 if (ifp->if_flags & IFF_LOOPBACK) 269 break; 270 271 if (igmp->igmp_code == 0) { 272 /* 273 * Old router. Remember that the querier on this 274 * interface is old, and set the timer to the 275 * value in RFC 1112. 276 */ 277 278 rti->rti_type = IGMP_V1_ROUTER; 279 rti->rti_time = 0; 280 281 timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; 282 283 if (ip->ip_dst.s_addr != igmp_all_hosts_group || 284 igmp->igmp_group.s_addr != 0) { 285 ++igmpstat.igps_rcv_badqueries; 286 m_freem(m); 287 return; 288 } 289 } else { 290 /* 291 * New router. Simply do the new validity check. 292 */ 293 294 if (igmp->igmp_group.s_addr != 0 && 295 !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { 296 ++igmpstat.igps_rcv_badqueries; 297 m_freem(m); 298 return; 299 } 300 } 301 302 /* 303 * - Start the timers in all of our membership records 304 * that the query applies to for the interface on 305 * which the query arrived excl. those that belong 306 * to the "all-hosts" group (224.0.0.1). 307 * - Restart any timer that is already running but has 308 * a value longer than the requested timeout. 309 * - Use the value specified in the query message as 310 * the maximum timeout. 311 */ 312 lck_mtx_lock(rt_mtx); 313 IN_FIRST_MULTI(step, inm); 314 while (inm != NULL) { 315 if (inm->inm_ifp == ifp && 316 inm->inm_addr.s_addr != igmp_all_hosts_group && 317 (igmp->igmp_group.s_addr == 0 || 318 igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { 319 if (inm->inm_timer == 0 || 320 inm->inm_timer > timer) { 321 inm->inm_timer = 322 IGMP_RANDOM_DELAY(timer); 323 igmp_timers_are_running = 1; 324 } 325 } 326 IN_NEXT_MULTI(step, inm); 327 } 328 lck_mtx_unlock(rt_mtx); 329 330 break; 331 332 case IGMP_V1_MEMBERSHIP_REPORT: 333 case IGMP_V2_MEMBERSHIP_REPORT: 334 /* 335 * For fast leave to work, we have to know that we are the 336 * last person to send a report for this group. Reports 337 * can potentially get looped back if we are a multicast 338 * router, so discard reports sourced by me. 339 */ 340 IFP_TO_IA(ifp, ia); 341 if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) 342 break; 343 344 ++igmpstat.igps_rcv_reports; 345 346 if (ifp->if_flags & IFF_LOOPBACK) 347 break; 348 349 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { 350 ++igmpstat.igps_rcv_badreports; 351 m_freem(m); 352 return; 353 } 354 355 /* 356 * KLUDGE: if the IP source address of the report has an 357 * unspecified (i.e., zero) subnet number, as is allowed for 358 * a booting host, replace it with the correct subnet number 359 * so that a process-level multicast routing demon can 360 * determine which subnet it arrived from. This is necessary 361 * to compensate for the lack of any way for a process to 362 * determine the arrival interface of an incoming packet. 363 */ 364 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) 365 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 366 367 /* 368 * If we belong to the group being reported, stop 369 * our timer for that group. 370 */ 371 ifnet_lock_shared(ifp); 372 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 373 ifnet_lock_done(ifp); 374 375 if (inm != NULL) { 376 inm->inm_timer = 0; 377 ++igmpstat.igps_rcv_ourreports; 378 379 inm->inm_state = IGMP_OTHERMEMBER; 380 } 381 382 break; 383 } 384 385 /* 386 * Pass all valid IGMP packets up to any process(es) listening 387 * on a raw IGMP socket. 388 */ 389 rip_input(m, iphlen); 390} 391 392int 393igmp_joingroup(struct in_multi *inm) 394{ 395 396 if (inm->inm_addr.s_addr == igmp_all_hosts_group 397 || inm->inm_ifp->if_flags & IFF_LOOPBACK) { 398 inm->inm_timer = 0; 399 inm->inm_state = IGMP_OTHERMEMBER; 400 } else { 401 inm->inm_rti = find_rti(inm->inm_ifp, M_WAITOK); 402 if (inm->inm_rti == NULL) return ENOMEM; 403 igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); 404 inm->inm_timer = IGMP_RANDOM_DELAY( 405 IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); 406 inm->inm_state = IGMP_IREPORTEDLAST; 407 igmp_timers_are_running = 1; 408 } 409 return 0; 410} 411 412void 413igmp_leavegroup(struct in_multi *inm) 414{ 415 if (inm->inm_state == IGMP_IREPORTEDLAST && 416 inm->inm_addr.s_addr != igmp_all_hosts_group && 417 !(inm->inm_ifp->if_flags & IFF_LOOPBACK) && 418 inm->inm_rti->rti_type != IGMP_V1_ROUTER) 419 igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group); 420} 421 422void 423igmp_fasttimo(void) 424{ 425 struct in_multi *inm; 426 struct in_multistep step; 427 428 /* 429 * Quick check to see if any work needs to be done, in order 430 * to minimize the overhead of fasttimo processing. 431 */ 432 433 if (!igmp_timers_are_running) 434 return; 435 436 igmp_timers_are_running = 0; 437 IN_FIRST_MULTI(step, inm); 438 while (inm != NULL) { 439 if (inm->inm_timer == 0) { 440 /* do nothing */ 441 } else if ((--inm->inm_timer == 0) && (inm->inm_rti != NULL)) { 442 igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); 443 inm->inm_state = IGMP_IREPORTEDLAST; 444 } else { 445 igmp_timers_are_running = 1; 446 } 447 IN_NEXT_MULTI(step, inm); 448 } 449} 450 451void 452igmp_slowtimo(void) 453{ 454 struct router_info *rti = Head; 455 456#if IGMP_DEBUG 457 printf("[igmp.c,_slowtimo] -- > entering \n"); 458#endif 459 while (rti) { 460 if (rti->rti_type == IGMP_V1_ROUTER) { 461 rti->rti_time++; 462 if (rti->rti_time >= IGMP_AGE_THRESHOLD) { 463 rti->rti_type = IGMP_V2_ROUTER; 464 } 465 } 466 rti = rti->rti_next; 467 } 468#if IGMP_DEBUG 469 printf("[igmp.c,_slowtimo] -- > exiting \n"); 470#endif 471} 472 473static struct route igmprt; 474 475static void 476igmp_sendpkt(struct in_multi *inm, int type, unsigned long addr) 477{ 478 struct mbuf *m; 479 struct igmp *igmp; 480 struct ip *ip; 481 struct ip_moptions imo; 482 483 MGETHDR(m, M_DONTWAIT, MT_HEADER); /* MAC-OK */ 484 if (m == NULL) 485 return; 486 487 m->m_pkthdr.rcvif = lo_ifp; 488#if CONFIG_MACF_NET 489 mac_mbuf_label_associate_linklayer(inm->inm_ifp, m); 490#endif 491 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 492 MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip)); 493 m->m_data += sizeof(struct ip); 494 m->m_len = IGMP_MINLEN; 495 m->m_pkthdr.csum_flags = 0; 496 m->m_pkthdr.csum_data = 0; 497 igmp = mtod(m, struct igmp *); 498 igmp->igmp_type = type; 499 igmp->igmp_code = 0; 500 igmp->igmp_group = inm->inm_addr; 501 igmp->igmp_cksum = 0; 502 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 503 504 m->m_data -= sizeof(struct ip); 505 m->m_len += sizeof(struct ip); 506 ip = mtod(m, struct ip *); 507 ip->ip_tos = 0; 508 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 509 ip->ip_off = 0; 510 ip->ip_p = IPPROTO_IGMP; 511 ip->ip_src.s_addr = INADDR_ANY; 512 ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr; 513 514 imo.imo_multicast_ifp = inm->inm_ifp; 515 imo.imo_multicast_ttl = 1; 516 imo.imo_multicast_vif = -1; 517#if MROUTING 518 /* 519 * Request loopback of the report if we are acting as a multicast 520 * router, so that the process-level routing demon can hear it. 521 */ 522 imo.imo_multicast_loop = (ip_mrouter != NULL); 523#else 524 imo.imo_multicast_loop = 0; 525#endif 526 527 /* 528 * XXX 529 * Do we have to worry about reentrancy here? Don't think so. 530 */ 531 ip_output(m, router_alert, &igmprt, 0, &imo, NULL); 532 533 ++igmpstat.igps_snd_reports; 534} 535 536