xref: /csrg-svn/sys/netinet/igmp.c (revision 54819)
1*54819Ssklower /*
2*54819Ssklower  * Copyright (c) 1988 Stephen Deering.
3*54819Ssklower  * Copyright (c) 1992 Regents of the University of California.
4*54819Ssklower  * All rights reserved.
5*54819Ssklower  *
6*54819Ssklower  * This code is derived from software contributed to Berkeley by
7*54819Ssklower  * Stephen Deering of Stanford University.
8*54819Ssklower  *
9*54819Ssklower  * %sccs.include.redist.c%
10*54819Ssklower  *
11*54819Ssklower  *	@(#)igmp.c	7.1 (Berkeley) 07/08/92
12*54819Ssklower  */
13*54819Ssklower 
14*54819Ssklower /* Internet Group Management Protocol (IGMP) routines. */
15*54819Ssklower 
16*54819Ssklower #ifdef MULTICAST
17*54819Ssklower 
18*54819Ssklower #include "param.h"
19*54819Ssklower #include "mbuf.h"
20*54819Ssklower #include "socket.h"
21*54819Ssklower #include "protosw.h"
22*54819Ssklower 
23*54819Ssklower #include "../net/if.h"
24*54819Ssklower #include "../net/route.h"
25*54819Ssklower 
26*54819Ssklower #include "in.h"
27*54819Ssklower #include "in_var.h"
28*54819Ssklower #include "in_systm.h"
29*54819Ssklower #include "ip.h"
30*54819Ssklower #include "ip_var.h"
31*54819Ssklower #include "igmp.h"
32*54819Ssklower #include "igmp_var.h"
33*54819Ssklower 
34*54819Ssklower extern struct ifnet loif;
35*54819Ssklower 
36*54819Ssklower static int igmp_timers_are_running = 0;
37*54819Ssklower static u_long igmp_all_hosts_group;
38*54819Ssklower 
39*54819Ssklower static void igmp_sendreport __P((struct in_multi *));
40*54819Ssklower 
41*54819Ssklower void
42*54819Ssklower igmp_init()
43*54819Ssklower {
44*54819Ssklower 	/*
45*54819Ssklower 	 * To avoid byte-swapping the same value over and over again.
46*54819Ssklower 	 */
47*54819Ssklower 	igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
48*54819Ssklower }
49*54819Ssklower 
50*54819Ssklower void
51*54819Ssklower igmp_input(m, ifp)
52*54819Ssklower 	register struct mbuf *m;
53*54819Ssklower 	register struct ifnet *ifp;
54*54819Ssklower {
55*54819Ssklower 	register struct igmp *igmp;
56*54819Ssklower 	register struct ip *ip;
57*54819Ssklower 	register int igmplen;
58*54819Ssklower 	register int iphlen;
59*54819Ssklower 	register int minlen;
60*54819Ssklower 	register struct in_multi *inm;
61*54819Ssklower 	register struct in_ifaddr *ia;
62*54819Ssklower 	struct in_multistep step;
63*54819Ssklower 
64*54819Ssklower 	++igmpstat.igps_rcv_total;
65*54819Ssklower 
66*54819Ssklower 	ip = mtod(m, struct ip *);
67*54819Ssklower 	iphlen = ip->ip_hl << 2;
68*54819Ssklower 	igmplen = ip->ip_len;
69*54819Ssklower 
70*54819Ssklower 	/*
71*54819Ssklower 	 * Validate lengths
72*54819Ssklower 	 */
73*54819Ssklower 	if (igmplen < IGMP_MINLEN) {
74*54819Ssklower 		++igmpstat.igps_rcv_tooshort;
75*54819Ssklower 		m_freem(m);
76*54819Ssklower 		return;
77*54819Ssklower 	}
78*54819Ssklower 	minlen = iphlen + IGMP_MINLEN;
79*54819Ssklower 	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
80*54819Ssklower 	    (m = m_pullup(m, minlen)) == 0) {
81*54819Ssklower 		++igmpstat.igps_rcv_tooshort;
82*54819Ssklower 		return;
83*54819Ssklower 	}
84*54819Ssklower 
85*54819Ssklower 	/*
86*54819Ssklower 	 * Validate checksum
87*54819Ssklower 	 */
88*54819Ssklower 	m->m_data += iphlen;
89*54819Ssklower 	m->m_len -= iphlen;
90*54819Ssklower 	igmp = mtod(m, struct igmp *);
91*54819Ssklower 	if (in_cksum(m, igmplen)) {
92*54819Ssklower 		++igmpstat.igps_rcv_badsum;
93*54819Ssklower 		m_freem(m);
94*54819Ssklower 		return;
95*54819Ssklower 	}
96*54819Ssklower 	m->m_data -= iphlen;
97*54819Ssklower 	m->m_len += iphlen;
98*54819Ssklower 	ip = mtod(m, struct ip *);
99*54819Ssklower 
100*54819Ssklower 	switch (igmp->igmp_type) {
101*54819Ssklower 
102*54819Ssklower 	case IGMP_HOST_MEMBERSHIP_QUERY:
103*54819Ssklower 		++igmpstat.igps_rcv_queries;
104*54819Ssklower 
105*54819Ssklower 		if (ifp == &loif)
106*54819Ssklower 			break;
107*54819Ssklower 
108*54819Ssklower 		if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
109*54819Ssklower 			++igmpstat.igps_rcv_badqueries;
110*54819Ssklower 			m_freem(m);
111*54819Ssklower 			return;
112*54819Ssklower 		}
113*54819Ssklower 
114*54819Ssklower 		/*
115*54819Ssklower 		 * Start the timers in all of our membership records for
116*54819Ssklower 		 * the interface on which the query arrived, except those
117*54819Ssklower 		 * that are already running and those that belong to the
118*54819Ssklower 		 * "all-hosts" group.
119*54819Ssklower 		 */
120*54819Ssklower 		IN_FIRST_MULTI(step, inm);
121*54819Ssklower 		while (inm != NULL) {
122*54819Ssklower 			if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
123*54819Ssklower 			    inm->inm_addr.s_addr != igmp_all_hosts_group) {
124*54819Ssklower 				inm->inm_timer =
125*54819Ssklower 				    IGMP_RANDOM_DELAY(inm->inm_addr);
126*54819Ssklower 				igmp_timers_are_running = 1;
127*54819Ssklower 			}
128*54819Ssklower 			IN_NEXT_MULTI(step, inm);
129*54819Ssklower 		}
130*54819Ssklower 
131*54819Ssklower 		break;
132*54819Ssklower 
133*54819Ssklower 	case IGMP_HOST_MEMBERSHIP_REPORT:
134*54819Ssklower 		++igmpstat.igps_rcv_reports;
135*54819Ssklower 
136*54819Ssklower 		if (ifp == &loif)
137*54819Ssklower 			break;
138*54819Ssklower 
139*54819Ssklower 		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
140*54819Ssklower 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
141*54819Ssklower 			++igmpstat.igps_rcv_badreports;
142*54819Ssklower 			m_freem(m);
143*54819Ssklower 			return;
144*54819Ssklower 		}
145*54819Ssklower 
146*54819Ssklower 		/*
147*54819Ssklower 		 * KLUDGE: if the IP source address of the report has an
148*54819Ssklower 		 * unspecified (i.e., zero) subnet number, as is allowed for
149*54819Ssklower 		 * a booting host, replace it with the correct subnet number
150*54819Ssklower 		 * so that a process-level multicast routing demon can
151*54819Ssklower 		 * determine which subnet it arrived from.  This is necessary
152*54819Ssklower 		 * to compensate for the lack of any way for a process to
153*54819Ssklower 		 * determine the arrival interface of an incoming packet.
154*54819Ssklower 		 */
155*54819Ssklower 		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
156*54819Ssklower 			IFP_TO_IA(ifp, ia);
157*54819Ssklower 			if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
158*54819Ssklower 		}
159*54819Ssklower 
160*54819Ssklower 		/*
161*54819Ssklower 		 * If we belong to the group being reported, stop
162*54819Ssklower 		 * our timer for that group.
163*54819Ssklower 		 */
164*54819Ssklower 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
165*54819Ssklower 		if (inm != NULL) {
166*54819Ssklower 			inm->inm_timer = 0;
167*54819Ssklower 			++igmpstat.igps_rcv_ourreports;
168*54819Ssklower 		}
169*54819Ssklower 
170*54819Ssklower 		break;
171*54819Ssklower 	}
172*54819Ssklower 
173*54819Ssklower 	/*
174*54819Ssklower 	 * Pass all valid IGMP packets up to any process(es) listening
175*54819Ssklower 	 * on a raw IGMP socket.
176*54819Ssklower 	 */
177*54819Ssklower 	rip_input(m);
178*54819Ssklower }
179*54819Ssklower 
180*54819Ssklower void
181*54819Ssklower igmp_joingroup(inm)
182*54819Ssklower 	struct in_multi *inm;
183*54819Ssklower {
184*54819Ssklower 	register int s = splnet();
185*54819Ssklower 
186*54819Ssklower 	if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
187*54819Ssklower 	    inm->inm_ifp == &loif)
188*54819Ssklower 		inm->inm_timer = 0;
189*54819Ssklower 	else {
190*54819Ssklower 		igmp_sendreport(inm);
191*54819Ssklower 		inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
192*54819Ssklower 		igmp_timers_are_running = 1;
193*54819Ssklower 	}
194*54819Ssklower 	splx(s);
195*54819Ssklower }
196*54819Ssklower 
197*54819Ssklower void
198*54819Ssklower igmp_leavegroup(inm)
199*54819Ssklower 	struct in_multi *inm;
200*54819Ssklower {
201*54819Ssklower 	/*
202*54819Ssklower 	 * No action required on leaving a group.
203*54819Ssklower 	 */
204*54819Ssklower }
205*54819Ssklower 
206*54819Ssklower void
207*54819Ssklower igmp_fasttimo()
208*54819Ssklower {
209*54819Ssklower 	register struct in_multi *inm;
210*54819Ssklower 	register int s;
211*54819Ssklower 	struct in_multistep step;
212*54819Ssklower 
213*54819Ssklower 	/*
214*54819Ssklower 	 * Quick check to see if any work needs to be done, in order
215*54819Ssklower 	 * to minimize the overhead of fasttimo processing.
216*54819Ssklower 	 */
217*54819Ssklower 	if (!igmp_timers_are_running)
218*54819Ssklower 		return;
219*54819Ssklower 
220*54819Ssklower 	s = splnet();
221*54819Ssklower 	igmp_timers_are_running = 0;
222*54819Ssklower 	IN_FIRST_MULTI(step, inm);
223*54819Ssklower 	while (inm != NULL) {
224*54819Ssklower 		if (inm->inm_timer == 0) {
225*54819Ssklower 			/* do nothing */
226*54819Ssklower 		} else if (--inm->inm_timer == 0) {
227*54819Ssklower 			igmp_sendreport(inm);
228*54819Ssklower 		} else {
229*54819Ssklower 			igmp_timers_are_running = 1;
230*54819Ssklower 		}
231*54819Ssklower 		IN_NEXT_MULTI(step, inm);
232*54819Ssklower 	}
233*54819Ssklower 	splx(s);
234*54819Ssklower }
235*54819Ssklower 
236*54819Ssklower static void
237*54819Ssklower igmp_sendreport(inm)
238*54819Ssklower 	register struct in_multi *inm;
239*54819Ssklower {
240*54819Ssklower 	register struct mbuf *m;
241*54819Ssklower 	register struct igmp *igmp;
242*54819Ssklower 	register struct ip *ip;
243*54819Ssklower 	register struct ip_moptions *imo;
244*54819Ssklower 	struct ip_moptions simo;
245*54819Ssklower 	extern struct socket *ip_mrouter;
246*54819Ssklower 
247*54819Ssklower 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
248*54819Ssklower 	if (m == NULL)
249*54819Ssklower 		return;
250*54819Ssklower 	m->m_data += max_linkhdr;
251*54819Ssklower 	m->m_len = sizeof(struct ip) + IGMP_MINLEN;
252*54819Ssklower 
253*54819Ssklower 	ip = mtod(m, struct ip *);
254*54819Ssklower 	ip->ip_tos = 0;
255*54819Ssklower 	ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
256*54819Ssklower 	ip->ip_off = 0;
257*54819Ssklower 	ip->ip_p = IPPROTO_IGMP;
258*54819Ssklower 	ip->ip_src.s_addr = INADDR_ANY;
259*54819Ssklower 	ip->ip_dst = inm->inm_addr;
260*54819Ssklower 
261*54819Ssklower 	igmp = (struct igmp *)(ip + 1);
262*54819Ssklower 	igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
263*54819Ssklower 	igmp->igmp_code = 0;
264*54819Ssklower 	igmp->igmp_group = inm->inm_addr;
265*54819Ssklower 	igmp->igmp_cksum = 0;
266*54819Ssklower 	igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
267*54819Ssklower 
268*54819Ssklower 	imo = &simo;
269*54819Ssklower 	bzero((caddr_t)imo, sizeof(*imo));
270*54819Ssklower 	imo->imo_multicast_ifp = inm->inm_ifp;
271*54819Ssklower 	imo->imo_multicast_ttl = 1;
272*54819Ssklower 	/*
273*54819Ssklower 	 * Request loopback of the report if we are acting as a multicast
274*54819Ssklower 	 * router, so that the process-level routing demon can hear it.
275*54819Ssklower 	 */
276*54819Ssklower 	imo->imo_multicast_loop = (ip_mrouter != NULL);
277*54819Ssklower 
278*54819Ssklower 	ip_output(m, NULL, NULL, 0, imo);
279*54819Ssklower 
280*54819Ssklower 	++igmpstat.igps_snd_reports;
281*54819Ssklower }
282*54819Ssklower #endif
283