igmp.c revision 1.33 1 /* $NetBSD: igmp.c,v 1.33 2003/06/15 02:49:32 matt Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Internet Group Management Protocol (IGMP) routines.
34 *
35 * Written by Steve Deering, Stanford, May 1988.
36 * Modified by Rosen Sharma, Stanford, Aug 1994.
37 * Modified by Bill Fenner, Xerox PARC, Feb 1995.
38 *
39 * MULTICAST Revision: 1.3
40 */
41
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.33 2003/06/15 02:49:32 matt Exp $");
44
45 #include "opt_mrouting.h"
46
47 #include <sys/param.h>
48 #include <sys/mbuf.h>
49 #include <sys/socket.h>
50 #include <sys/protosw.h>
51 #include <sys/systm.h>
52
53 #include <net/if.h>
54 #include <net/route.h>
55
56 #include <netinet/in.h>
57 #include <netinet/in_var.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/ip.h>
60 #include <netinet/ip_var.h>
61 #include <netinet/igmp.h>
62 #include <netinet/igmp_var.h>
63
64 #include <machine/stdarg.h>
65
66 #define IP_MULTICASTOPTS 0
67
68 struct pool igmp_rti_pool;
69 struct igmpstat igmpstat;
70 int igmp_timers_are_running;
71 static LIST_HEAD(, router_info) rti_head = LIST_HEAD_INITIALIZER(rti_head);
72
73 void igmp_sendpkt __P((struct in_multi *, int));
74 static int rti_fill __P((struct in_multi *));
75 static struct router_info * rti_find __P((struct ifnet *));
76
77 void
78 igmp_init()
79 {
80 igmp_timers_are_running = 0;
81 pool_init(&igmp_rti_pool, sizeof(struct router_info), 0, 0, 0, "igmppl",
82 NULL);
83 }
84
85 static int
86 rti_fill(inm)
87 struct in_multi *inm;
88 {
89 struct router_info *rti;
90
91 LIST_FOREACH(rti, &rti_head, rti_link) {
92 if (rti->rti_ifp == inm->inm_ifp) {
93 inm->inm_rti = rti;
94 if (rti->rti_type == IGMP_v1_ROUTER)
95 return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
96 else
97 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
98 }
99 }
100
101 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
102 if (rti == NULL)
103 return 0;
104 rti->rti_ifp = inm->inm_ifp;
105 rti->rti_type = IGMP_v2_ROUTER;
106 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
107 inm->inm_rti = rti;
108 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
109 }
110
111 static struct router_info *
112 rti_find(ifp)
113 struct ifnet *ifp;
114 {
115 struct router_info *rti;
116
117 LIST_FOREACH(rti, &rti_head, rti_link) {
118 if (rti->rti_ifp == ifp)
119 return (rti);
120 }
121
122 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
123 if (rti == NULL)
124 return NULL;
125 rti->rti_ifp = ifp;
126 rti->rti_type = IGMP_v2_ROUTER;
127 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
128 return (rti);
129 }
130
131 void
132 #if __STDC__
133 igmp_input(struct mbuf *m, ...)
134 #else
135 igmp_input(m, va_alist)
136 struct mbuf *m;
137 va_dcl
138 #endif
139 {
140 int proto;
141 int iphlen;
142 struct ifnet *ifp = m->m_pkthdr.rcvif;
143 struct ip *ip = mtod(m, struct ip *);
144 struct igmp *igmp;
145 u_int minlen;
146 struct in_multi *inm;
147 struct in_multistep step;
148 struct router_info *rti;
149 struct in_ifaddr *ia;
150 u_int timer;
151 va_list ap;
152 u_int16_t ip_len;
153
154 va_start(ap, m);
155 iphlen = va_arg(ap, int);
156 proto = va_arg(ap, int);
157 va_end(ap);
158
159 ++igmpstat.igps_rcv_total;
160
161 /*
162 * Validate lengths
163 */
164 minlen = iphlen + IGMP_MINLEN;
165 ip_len = ntohs(ip->ip_len);
166 if (ip_len < minlen) {
167 ++igmpstat.igps_rcv_tooshort;
168 m_freem(m);
169 return;
170 }
171 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
172 || m->m_len < minlen) {
173 if ((m = m_pullup(m, minlen)) == 0) {
174 ++igmpstat.igps_rcv_tooshort;
175 return;
176 }
177 ip = mtod(m, struct ip *);
178 }
179
180 /*
181 * Validate checksum
182 */
183 m->m_data += iphlen;
184 m->m_len -= iphlen;
185 igmp = mtod(m, struct igmp *);
186 /* No need to assert alignment here. */
187 if (in_cksum(m, ip_len - iphlen)) {
188 ++igmpstat.igps_rcv_badsum;
189 m_freem(m);
190 return;
191 }
192 m->m_data -= iphlen;
193 m->m_len += iphlen;
194
195 switch (igmp->igmp_type) {
196
197 case IGMP_HOST_MEMBERSHIP_QUERY:
198 ++igmpstat.igps_rcv_queries;
199
200 if (ifp->if_flags & IFF_LOOPBACK)
201 break;
202
203 if (igmp->igmp_code == 0) {
204 rti = rti_find(ifp);
205 if (rti == NULL)
206 break;
207 rti->rti_type = IGMP_v1_ROUTER;
208 rti->rti_age = 0;
209
210 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
211 ++igmpstat.igps_rcv_badqueries;
212 m_freem(m);
213 return;
214 }
215
216 /*
217 * Start the timers in all of our membership records
218 * for the interface on which the query arrived,
219 * except those that are already running and those
220 * that belong to a "local" group (224.0.0.X).
221 */
222 IN_FIRST_MULTI(step, inm);
223 while (inm != NULL) {
224 if (inm->inm_ifp == ifp &&
225 inm->inm_timer == 0 &&
226 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
227 inm->inm_state = IGMP_DELAYING_MEMBER;
228 inm->inm_timer = IGMP_RANDOM_DELAY(
229 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
230 igmp_timers_are_running = 1;
231 }
232 IN_NEXT_MULTI(step, inm);
233 }
234 } else {
235 if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
236 ++igmpstat.igps_rcv_badqueries;
237 m_freem(m);
238 return;
239 }
240
241 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
242 if (timer == 0)
243 timer =1;
244
245 /*
246 * Start the timers in all of our membership records
247 * for the interface on which the query arrived,
248 * except those that are already running and those
249 * that belong to a "local" group (224.0.0.X). For
250 * timers already running, check if they need to be
251 * reset.
252 */
253 IN_FIRST_MULTI(step, inm);
254 while (inm != NULL) {
255 if (inm->inm_ifp == ifp &&
256 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
257 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
258 in_hosteq(ip->ip_dst, inm->inm_addr))) {
259 switch (inm->inm_state) {
260 case IGMP_DELAYING_MEMBER:
261 if (inm->inm_timer <= timer)
262 break;
263 /* FALLTHROUGH */
264 case IGMP_IDLE_MEMBER:
265 case IGMP_LAZY_MEMBER:
266 case IGMP_AWAKENING_MEMBER:
267 inm->inm_state =
268 IGMP_DELAYING_MEMBER;
269 inm->inm_timer =
270 IGMP_RANDOM_DELAY(timer);
271 igmp_timers_are_running = 1;
272 break;
273 case IGMP_SLEEPING_MEMBER:
274 inm->inm_state =
275 IGMP_AWAKENING_MEMBER;
276 break;
277 }
278 }
279 IN_NEXT_MULTI(step, inm);
280 }
281 }
282
283 break;
284
285 case IGMP_v1_HOST_MEMBERSHIP_REPORT:
286 ++igmpstat.igps_rcv_reports;
287
288 if (ifp->if_flags & IFF_LOOPBACK)
289 break;
290
291 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
292 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
293 ++igmpstat.igps_rcv_badreports;
294 m_freem(m);
295 return;
296 }
297
298 /*
299 * KLUDGE: if the IP source address of the report has an
300 * unspecified (i.e., zero) subnet number, as is allowed for
301 * a booting host, replace it with the correct subnet number
302 * so that a process-level multicast routing daemon can
303 * determine which subnet it arrived from. This is necessary
304 * to compensate for the lack of any way for a process to
305 * determine the arrival interface of an incoming packet.
306 */
307 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
308 IFP_TO_IA(ifp, ia); /* XXX */
309 if (ia)
310 ip->ip_src.s_addr = ia->ia_subnet;
311 }
312
313 /*
314 * If we belong to the group being reported, stop
315 * our timer for that group.
316 */
317 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
318 if (inm != NULL) {
319 inm->inm_timer = 0;
320 ++igmpstat.igps_rcv_ourreports;
321
322 switch (inm->inm_state) {
323 case IGMP_IDLE_MEMBER:
324 case IGMP_LAZY_MEMBER:
325 case IGMP_AWAKENING_MEMBER:
326 case IGMP_SLEEPING_MEMBER:
327 inm->inm_state = IGMP_SLEEPING_MEMBER;
328 break;
329 case IGMP_DELAYING_MEMBER:
330 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
331 inm->inm_state = IGMP_LAZY_MEMBER;
332 else
333 inm->inm_state = IGMP_SLEEPING_MEMBER;
334 break;
335 }
336 }
337
338 break;
339
340 case IGMP_v2_HOST_MEMBERSHIP_REPORT:
341 #ifdef MROUTING
342 /*
343 * Make sure we don't hear our own membership report. Fast
344 * leave requires knowing that we are the only member of a
345 * group.
346 */
347 IFP_TO_IA(ifp, ia); /* XXX */
348 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr))
349 break;
350 #endif
351
352 ++igmpstat.igps_rcv_reports;
353
354 if (ifp->if_flags & IFF_LOOPBACK)
355 break;
356
357 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
358 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
359 ++igmpstat.igps_rcv_badreports;
360 m_freem(m);
361 return;
362 }
363
364 /*
365 * KLUDGE: if the IP source address of the report has an
366 * unspecified (i.e., zero) subnet number, as is allowed for
367 * a booting host, replace it with the correct subnet number
368 * so that a process-level multicast routing daemon can
369 * determine which subnet it arrived from. This is necessary
370 * to compensate for the lack of any way for a process to
371 * determine the arrival interface of an incoming packet.
372 */
373 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
374 #ifndef MROUTING
375 IFP_TO_IA(ifp, ia); /* XXX */
376 #endif
377 if (ia)
378 ip->ip_src.s_addr = ia->ia_subnet;
379 }
380
381 /*
382 * If we belong to the group being reported, stop
383 * our timer for that group.
384 */
385 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
386 if (inm != NULL) {
387 inm->inm_timer = 0;
388 ++igmpstat.igps_rcv_ourreports;
389
390 switch (inm->inm_state) {
391 case IGMP_DELAYING_MEMBER:
392 case IGMP_IDLE_MEMBER:
393 case IGMP_AWAKENING_MEMBER:
394 inm->inm_state = IGMP_LAZY_MEMBER;
395 break;
396 case IGMP_LAZY_MEMBER:
397 case IGMP_SLEEPING_MEMBER:
398 break;
399 }
400 }
401
402 break;
403
404 }
405
406 /*
407 * Pass all valid IGMP packets up to any process(es) listening
408 * on a raw IGMP socket.
409 */
410 rip_input(m, iphlen, proto);
411 return;
412 }
413
414 int
415 igmp_joingroup(inm)
416 struct in_multi *inm;
417 {
418 int report_type;
419 int s = splsoftnet();
420
421 inm->inm_state = IGMP_IDLE_MEMBER;
422
423 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
424 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
425 report_type = rti_fill(inm);
426 if (report_type == 0)
427 return ENOMEM;
428 igmp_sendpkt(inm, report_type);
429 inm->inm_state = IGMP_DELAYING_MEMBER;
430 inm->inm_timer = IGMP_RANDOM_DELAY(
431 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
432 igmp_timers_are_running = 1;
433 } else
434 inm->inm_timer = 0;
435 splx(s);
436 return 0;
437 }
438
439 void
440 igmp_leavegroup(inm)
441 struct in_multi *inm;
442 {
443
444 switch (inm->inm_state) {
445 case IGMP_DELAYING_MEMBER:
446 case IGMP_IDLE_MEMBER:
447 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
448 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
449 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
450 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
451 break;
452 case IGMP_LAZY_MEMBER:
453 case IGMP_AWAKENING_MEMBER:
454 case IGMP_SLEEPING_MEMBER:
455 break;
456 }
457 }
458
459 void
460 igmp_fasttimo()
461 {
462 struct in_multi *inm;
463 struct in_multistep step;
464 int s;
465
466 /*
467 * Quick check to see if any work needs to be done, in order
468 * to minimize the overhead of fasttimo processing.
469 */
470 if (!igmp_timers_are_running)
471 return;
472
473 s = splsoftnet();
474 igmp_timers_are_running = 0;
475 IN_FIRST_MULTI(step, inm);
476 while (inm != NULL) {
477 if (inm->inm_timer == 0) {
478 /* do nothing */
479 } else if (--inm->inm_timer == 0) {
480 if (inm->inm_state == IGMP_DELAYING_MEMBER) {
481 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
482 igmp_sendpkt(inm,
483 IGMP_v1_HOST_MEMBERSHIP_REPORT);
484 else
485 igmp_sendpkt(inm,
486 IGMP_v2_HOST_MEMBERSHIP_REPORT);
487 inm->inm_state = IGMP_IDLE_MEMBER;
488 }
489 } else {
490 igmp_timers_are_running = 1;
491 }
492 IN_NEXT_MULTI(step, inm);
493 }
494 splx(s);
495 }
496
497 void
498 igmp_slowtimo()
499 {
500 struct router_info *rti;
501 int s;
502
503 s = splsoftnet();
504 LIST_FOREACH(rti, &rti_head, rti_link) {
505 if (rti->rti_type == IGMP_v1_ROUTER &&
506 ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
507 rti->rti_type = IGMP_v2_ROUTER;
508 }
509 }
510 splx(s);
511 }
512
513 void
514 igmp_sendpkt(inm, type)
515 struct in_multi *inm;
516 int type;
517 {
518 struct mbuf *m;
519 struct igmp *igmp;
520 struct ip *ip;
521 struct ip_moptions imo;
522 #ifdef MROUTING
523 extern struct socket *ip_mrouter;
524 #endif /* MROUTING */
525
526 MGETHDR(m, M_DONTWAIT, MT_HEADER);
527 if (m == NULL)
528 return;
529 /*
530 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
531 * is smaller than mbuf size returned by MGETHDR.
532 */
533 m->m_data += max_linkhdr;
534 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
535 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
536
537 ip = mtod(m, struct ip *);
538 ip->ip_tos = 0;
539 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
540 ip->ip_off = htons(0);
541 ip->ip_p = IPPROTO_IGMP;
542 ip->ip_src = zeroin_addr;
543 ip->ip_dst = inm->inm_addr;
544
545 m->m_data += sizeof(struct ip);
546 m->m_len -= sizeof(struct ip);
547 igmp = mtod(m, struct igmp *);
548 igmp->igmp_type = type;
549 igmp->igmp_code = 0;
550 igmp->igmp_group = inm->inm_addr;
551 igmp->igmp_cksum = 0;
552 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
553 m->m_data -= sizeof(struct ip);
554 m->m_len += sizeof(struct ip);
555
556 imo.imo_multicast_ifp = inm->inm_ifp;
557 imo.imo_multicast_ttl = 1;
558 #ifdef RSVP_ISI
559 imo.imo_multicast_vif = -1;
560 #endif
561 /*
562 * Request loopback of the report if we are acting as a multicast
563 * router, so that the process-level routing demon can hear it.
564 */
565 #ifdef MROUTING
566 imo.imo_multicast_loop = (ip_mrouter != NULL);
567 #else
568 imo.imo_multicast_loop = 0;
569 #endif /* MROUTING */
570
571 ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
572 &imo);
573
574 ++igmpstat.igps_snd_reports;
575 }
576