igmp.c revision 1.8 1 /* $NetBSD: igmp.c,v 1.8 1994/06/29 06:37:56 cgd Exp $ */
2
3 /*
4 * Copyright (c) 1988 Stephen Deering.
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Stephen Deering of Stanford University.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 * @(#)igmp.c 8.1 (Berkeley) 7/19/93
40 */
41
42 /* Internet Group Management Protocol (IGMP) routines. */
43
44
45 #include <sys/param.h>
46 #include <sys/mbuf.h>
47 #include <sys/socket.h>
48 #include <sys/protosw.h>
49
50 #include <net/if.h>
51 #include <net/route.h>
52
53 #include <netinet/in.h>
54 #include <netinet/in_var.h>
55 #include <netinet/in_systm.h>
56 #include <netinet/ip.h>
57 #include <netinet/ip_var.h>
58 #include <netinet/igmp.h>
59 #include <netinet/igmp_var.h>
60
61 static int igmp_timers_are_running = 0;
62 static u_long igmp_all_hosts_group;
63
64 static void igmp_sendreport __P((struct in_multi *));
65
66 void
67 igmp_init()
68 {
69 /*
70 * To avoid byte-swapping the same value over and over again.
71 */
72 igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
73 }
74
75 void
76 igmp_input(m, iphlen)
77 register struct mbuf *m;
78 register int iphlen;
79 {
80 register struct igmp *igmp;
81 register struct ip *ip;
82 register int igmplen;
83 register struct ifnet *ifp = m->m_pkthdr.rcvif;
84 register int minlen;
85 register struct in_multi *inm;
86 register struct in_ifaddr *ia;
87 struct in_multistep step;
88
89 ++igmpstat.igps_rcv_total;
90
91 ip = mtod(m, struct ip *);
92 igmplen = ip->ip_len;
93
94 /*
95 * Validate lengths
96 */
97 if (igmplen < IGMP_MINLEN) {
98 ++igmpstat.igps_rcv_tooshort;
99 m_freem(m);
100 return;
101 }
102 minlen = iphlen + IGMP_MINLEN;
103 if ((m->m_flags & M_EXT || m->m_len < minlen) &&
104 (m = m_pullup(m, minlen)) == 0) {
105 ++igmpstat.igps_rcv_tooshort;
106 return;
107 }
108
109 /*
110 * Validate checksum
111 */
112 m->m_data += iphlen;
113 m->m_len -= iphlen;
114 igmp = mtod(m, struct igmp *);
115 if (in_cksum(m, igmplen)) {
116 ++igmpstat.igps_rcv_badsum;
117 m_freem(m);
118 return;
119 }
120 m->m_data -= iphlen;
121 m->m_len += iphlen;
122 ip = mtod(m, struct ip *);
123
124 switch (igmp->igmp_type) {
125
126 case IGMP_HOST_MEMBERSHIP_QUERY:
127 ++igmpstat.igps_rcv_queries;
128
129 if (ifp->if_flags & IFF_LOOPBACK)
130 break;
131
132 if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
133 ++igmpstat.igps_rcv_badqueries;
134 m_freem(m);
135 return;
136 }
137
138 /*
139 * Start the timers in all of our membership records for
140 * the interface on which the query arrived, except those
141 * that are already running and those that belong to the
142 * "all-hosts" group.
143 */
144 IN_FIRST_MULTI(step, inm);
145 while (inm != NULL) {
146 if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
147 inm->inm_addr.s_addr != igmp_all_hosts_group) {
148 inm->inm_timer =
149 IGMP_RANDOM_DELAY(inm->inm_addr);
150 igmp_timers_are_running = 1;
151 }
152 IN_NEXT_MULTI(step, inm);
153 }
154
155 break;
156
157 case IGMP_HOST_MEMBERSHIP_REPORT:
158 ++igmpstat.igps_rcv_reports;
159
160 if (ifp->if_flags & IFF_LOOPBACK)
161 break;
162
163 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
164 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
165 ++igmpstat.igps_rcv_badreports;
166 m_freem(m);
167 return;
168 }
169
170 /*
171 * KLUDGE: if the IP source address of the report has an
172 * unspecified (i.e., zero) subnet number, as is allowed for
173 * a booting host, replace it with the correct subnet number
174 * so that a process-level multicast routing demon can
175 * determine which subnet it arrived from. This is necessary
176 * to compensate for the lack of any way for a process to
177 * determine the arrival interface of an incoming packet.
178 */
179 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
180 IFP_TO_IA(ifp, ia);
181 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
182 }
183
184 /*
185 * If we belong to the group being reported, stop
186 * our timer for that group.
187 */
188 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
189 if (inm != NULL) {
190 inm->inm_timer = 0;
191 ++igmpstat.igps_rcv_ourreports;
192 }
193
194 break;
195 }
196
197 /*
198 * Pass all valid IGMP packets up to any process(es) listening
199 * on a raw IGMP socket.
200 */
201 rip_input(m);
202 }
203
204 void
205 igmp_joingroup(inm)
206 struct in_multi *inm;
207 {
208 register int s = splnet();
209
210 if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
211 (inm->inm_ifp->if_flags & IFF_LOOPBACK))
212 inm->inm_timer = 0;
213 else {
214 igmp_sendreport(inm);
215 inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
216 igmp_timers_are_running = 1;
217 }
218 splx(s);
219 }
220
221 void
222 igmp_leavegroup(inm)
223 struct in_multi *inm;
224 {
225 /*
226 * No action required on leaving a group.
227 */
228 }
229
230 void
231 igmp_fasttimo()
232 {
233 register struct in_multi *inm;
234 register int s;
235 struct in_multistep step;
236
237 /*
238 * Quick check to see if any work needs to be done, in order
239 * to minimize the overhead of fasttimo processing.
240 */
241 if (!igmp_timers_are_running)
242 return;
243
244 s = splnet();
245 igmp_timers_are_running = 0;
246 IN_FIRST_MULTI(step, inm);
247 while (inm != NULL) {
248 if (inm->inm_timer == 0) {
249 /* do nothing */
250 } else if (--inm->inm_timer == 0) {
251 igmp_sendreport(inm);
252 } else {
253 igmp_timers_are_running = 1;
254 }
255 IN_NEXT_MULTI(step, inm);
256 }
257 splx(s);
258 }
259
260 static void
261 igmp_sendreport(inm)
262 register struct in_multi *inm;
263 {
264 register struct mbuf *m;
265 register struct igmp *igmp;
266 register struct ip *ip;
267 register struct ip_moptions *imo;
268 struct ip_moptions simo;
269
270 MGETHDR(m, M_DONTWAIT, MT_HEADER);
271 if (m == NULL)
272 return;
273 /*
274 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
275 * is smaller than mbuf size returned by MGETHDR.
276 */
277 m->m_data += max_linkhdr;
278 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
279 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
280
281 ip = mtod(m, struct ip *);
282 ip->ip_tos = 0;
283 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
284 ip->ip_off = 0;
285 ip->ip_p = IPPROTO_IGMP;
286 ip->ip_src.s_addr = INADDR_ANY;
287 ip->ip_dst = inm->inm_addr;
288
289 m->m_data += sizeof(struct ip);
290 m->m_len -= sizeof(struct ip);
291 igmp = mtod(m, struct igmp *);
292 igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
293 igmp->igmp_code = 0;
294 igmp->igmp_group = inm->inm_addr;
295 igmp->igmp_cksum = 0;
296 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
297 m->m_data -= sizeof(struct ip);
298 m->m_len += sizeof(struct ip);
299
300 imo = &simo;
301 bzero((caddr_t)imo, sizeof(*imo));
302 imo->imo_multicast_ifp = inm->inm_ifp;
303 imo->imo_multicast_ttl = 1;
304 /*
305 * Request loopback of the report if we are acting as a multicast
306 * router, so that the process-level routing demon can hear it.
307 */
308 #ifdef MROUTING
309 {
310 extern struct socket *ip_mrouter;
311 imo->imo_multicast_loop = (ip_mrouter != NULL);
312 }
313 #endif
314 ip_output(m, NULL, NULL, 0, imo);
315
316 ++igmpstat.igps_snd_reports;
317 }
318