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