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