igmp.c revision 1.36 1 /* $NetBSD: igmp.c,v 1.36 2003/08/22 21:53:02 itojun 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.36 2003/08/22 21:53:02 itojun 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 static void rti_delete(struct ifnet *);
77
78 void
79 igmp_init()
80 {
81 igmp_timers_are_running = 0;
82 pool_init(&igmp_rti_pool, sizeof(struct router_info), 0, 0, 0, "igmppl",
83 NULL);
84 }
85
86 static int
87 rti_fill(inm)
88 struct in_multi *inm;
89 {
90 struct router_info *rti;
91
92 LIST_FOREACH(rti, &rti_head, rti_link) {
93 if (rti->rti_ifp == inm->inm_ifp) {
94 inm->inm_rti = rti;
95 if (rti->rti_type == IGMP_v1_ROUTER)
96 return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
97 else
98 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
99 }
100 }
101
102 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
103 if (rti == NULL)
104 return 0;
105 rti->rti_ifp = inm->inm_ifp;
106 rti->rti_type = IGMP_v2_ROUTER;
107 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
108 inm->inm_rti = rti;
109 return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
110 }
111
112 static struct router_info *
113 rti_find(ifp)
114 struct ifnet *ifp;
115 {
116 struct router_info *rti;
117
118 LIST_FOREACH(rti, &rti_head, rti_link) {
119 if (rti->rti_ifp == ifp)
120 return (rti);
121 }
122
123 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
124 if (rti == NULL)
125 return NULL;
126 rti->rti_ifp = ifp;
127 rti->rti_type = IGMP_v2_ROUTER;
128 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
129 return (rti);
130 }
131
132 static void
133 rti_delete(ifp)
134 struct ifnet *ifp;
135 {
136 struct router_info *rti;
137
138 LIST_FOREACH(rti, &rti_head, rti_link) {
139 if (rti->rti_ifp == ifp) {
140 LIST_REMOVE(rti, rti_link);
141 pool_put(&igmp_rti_pool, rti);
142 return;
143 }
144 }
145 }
146
147 void
148 #if __STDC__
149 igmp_input(struct mbuf *m, ...)
150 #else
151 igmp_input(m, va_alist)
152 struct mbuf *m;
153 va_dcl
154 #endif
155 {
156 int proto;
157 int iphlen;
158 struct ifnet *ifp = m->m_pkthdr.rcvif;
159 struct ip *ip = mtod(m, struct ip *);
160 struct igmp *igmp;
161 u_int minlen;
162 struct in_multi *inm;
163 struct in_multistep step;
164 struct router_info *rti;
165 struct in_ifaddr *ia;
166 u_int timer;
167 va_list ap;
168 u_int16_t ip_len;
169
170 va_start(ap, m);
171 iphlen = va_arg(ap, int);
172 proto = va_arg(ap, int);
173 va_end(ap);
174
175 ++igmpstat.igps_rcv_total;
176
177 /*
178 * Validate lengths
179 */
180 minlen = iphlen + IGMP_MINLEN;
181 ip_len = ntohs(ip->ip_len);
182 if (ip_len < minlen) {
183 ++igmpstat.igps_rcv_tooshort;
184 m_freem(m);
185 return;
186 }
187 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
188 || m->m_len < minlen) {
189 if ((m = m_pullup(m, minlen)) == 0) {
190 ++igmpstat.igps_rcv_tooshort;
191 return;
192 }
193 ip = mtod(m, struct ip *);
194 }
195
196 /*
197 * Validate checksum
198 */
199 m->m_data += iphlen;
200 m->m_len -= iphlen;
201 igmp = mtod(m, struct igmp *);
202 /* No need to assert alignment here. */
203 if (in_cksum(m, ip_len - iphlen)) {
204 ++igmpstat.igps_rcv_badsum;
205 m_freem(m);
206 return;
207 }
208 m->m_data -= iphlen;
209 m->m_len += iphlen;
210
211 switch (igmp->igmp_type) {
212
213 case IGMP_HOST_MEMBERSHIP_QUERY:
214 ++igmpstat.igps_rcv_queries;
215
216 if (ifp->if_flags & IFF_LOOPBACK)
217 break;
218
219 if (igmp->igmp_code == 0) {
220 rti = rti_find(ifp);
221 if (rti == NULL)
222 break;
223 rti->rti_type = IGMP_v1_ROUTER;
224 rti->rti_age = 0;
225
226 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
227 ++igmpstat.igps_rcv_badqueries;
228 m_freem(m);
229 return;
230 }
231
232 /*
233 * Start the timers in all of our membership records
234 * for the interface on which the query arrived,
235 * except those that are already running and those
236 * that belong to a "local" group (224.0.0.X).
237 */
238 IN_FIRST_MULTI(step, inm);
239 while (inm != NULL) {
240 if (inm->inm_ifp == ifp &&
241 inm->inm_timer == 0 &&
242 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
243 inm->inm_state = IGMP_DELAYING_MEMBER;
244 inm->inm_timer = IGMP_RANDOM_DELAY(
245 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
246 igmp_timers_are_running = 1;
247 }
248 IN_NEXT_MULTI(step, inm);
249 }
250 } else {
251 if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
252 ++igmpstat.igps_rcv_badqueries;
253 m_freem(m);
254 return;
255 }
256
257 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
258 if (timer == 0)
259 timer =1;
260
261 /*
262 * Start the timers in all of our membership records
263 * for the interface on which the query arrived,
264 * except those that are already running and those
265 * that belong to a "local" group (224.0.0.X). For
266 * timers already running, check if they need to be
267 * reset.
268 */
269 IN_FIRST_MULTI(step, inm);
270 while (inm != NULL) {
271 if (inm->inm_ifp == ifp &&
272 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
273 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
274 in_hosteq(ip->ip_dst, inm->inm_addr))) {
275 switch (inm->inm_state) {
276 case IGMP_DELAYING_MEMBER:
277 if (inm->inm_timer <= timer)
278 break;
279 /* FALLTHROUGH */
280 case IGMP_IDLE_MEMBER:
281 case IGMP_LAZY_MEMBER:
282 case IGMP_AWAKENING_MEMBER:
283 inm->inm_state =
284 IGMP_DELAYING_MEMBER;
285 inm->inm_timer =
286 IGMP_RANDOM_DELAY(timer);
287 igmp_timers_are_running = 1;
288 break;
289 case IGMP_SLEEPING_MEMBER:
290 inm->inm_state =
291 IGMP_AWAKENING_MEMBER;
292 break;
293 }
294 }
295 IN_NEXT_MULTI(step, inm);
296 }
297 }
298
299 break;
300
301 case IGMP_v1_HOST_MEMBERSHIP_REPORT:
302 ++igmpstat.igps_rcv_reports;
303
304 if (ifp->if_flags & IFF_LOOPBACK)
305 break;
306
307 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
308 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
309 ++igmpstat.igps_rcv_badreports;
310 m_freem(m);
311 return;
312 }
313
314 /*
315 * KLUDGE: if the IP source address of the report has an
316 * unspecified (i.e., zero) subnet number, as is allowed for
317 * a booting host, replace it with the correct subnet number
318 * so that a process-level multicast routing daemon can
319 * determine which subnet it arrived from. This is necessary
320 * to compensate for the lack of any way for a process to
321 * determine the arrival interface of an incoming packet.
322 */
323 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
324 IFP_TO_IA(ifp, ia); /* XXX */
325 if (ia)
326 ip->ip_src.s_addr = ia->ia_subnet;
327 }
328
329 /*
330 * If we belong to the group being reported, stop
331 * our timer for that group.
332 */
333 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
334 if (inm != NULL) {
335 inm->inm_timer = 0;
336 ++igmpstat.igps_rcv_ourreports;
337
338 switch (inm->inm_state) {
339 case IGMP_IDLE_MEMBER:
340 case IGMP_LAZY_MEMBER:
341 case IGMP_AWAKENING_MEMBER:
342 case IGMP_SLEEPING_MEMBER:
343 inm->inm_state = IGMP_SLEEPING_MEMBER;
344 break;
345 case IGMP_DELAYING_MEMBER:
346 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
347 inm->inm_state = IGMP_LAZY_MEMBER;
348 else
349 inm->inm_state = IGMP_SLEEPING_MEMBER;
350 break;
351 }
352 }
353
354 break;
355
356 case IGMP_v2_HOST_MEMBERSHIP_REPORT:
357 #ifdef MROUTING
358 /*
359 * Make sure we don't hear our own membership report. Fast
360 * leave requires knowing that we are the only member of a
361 * group.
362 */
363 IFP_TO_IA(ifp, ia); /* XXX */
364 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr))
365 break;
366 #endif
367
368 ++igmpstat.igps_rcv_reports;
369
370 if (ifp->if_flags & IFF_LOOPBACK)
371 break;
372
373 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
374 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
375 ++igmpstat.igps_rcv_badreports;
376 m_freem(m);
377 return;
378 }
379
380 /*
381 * KLUDGE: if the IP source address of the report has an
382 * unspecified (i.e., zero) subnet number, as is allowed for
383 * a booting host, replace it with the correct subnet number
384 * so that a process-level multicast routing daemon can
385 * determine which subnet it arrived from. This is necessary
386 * to compensate for the lack of any way for a process to
387 * determine the arrival interface of an incoming packet.
388 */
389 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
390 #ifndef MROUTING
391 IFP_TO_IA(ifp, ia); /* XXX */
392 #endif
393 if (ia)
394 ip->ip_src.s_addr = ia->ia_subnet;
395 }
396
397 /*
398 * If we belong to the group being reported, stop
399 * our timer for that group.
400 */
401 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
402 if (inm != NULL) {
403 inm->inm_timer = 0;
404 ++igmpstat.igps_rcv_ourreports;
405
406 switch (inm->inm_state) {
407 case IGMP_DELAYING_MEMBER:
408 case IGMP_IDLE_MEMBER:
409 case IGMP_AWAKENING_MEMBER:
410 inm->inm_state = IGMP_LAZY_MEMBER;
411 break;
412 case IGMP_LAZY_MEMBER:
413 case IGMP_SLEEPING_MEMBER:
414 break;
415 }
416 }
417
418 break;
419
420 }
421
422 /*
423 * Pass all valid IGMP packets up to any process(es) listening
424 * on a raw IGMP socket.
425 */
426 rip_input(m, iphlen, proto);
427 return;
428 }
429
430 int
431 igmp_joingroup(inm)
432 struct in_multi *inm;
433 {
434 int report_type;
435 int s = splsoftnet();
436
437 inm->inm_state = IGMP_IDLE_MEMBER;
438
439 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
440 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
441 report_type = rti_fill(inm);
442 if (report_type == 0)
443 return ENOMEM;
444 igmp_sendpkt(inm, report_type);
445 inm->inm_state = IGMP_DELAYING_MEMBER;
446 inm->inm_timer = IGMP_RANDOM_DELAY(
447 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
448 igmp_timers_are_running = 1;
449 } else
450 inm->inm_timer = 0;
451 splx(s);
452 return 0;
453 }
454
455 void
456 igmp_leavegroup(inm)
457 struct in_multi *inm;
458 {
459
460 switch (inm->inm_state) {
461 case IGMP_DELAYING_MEMBER:
462 case IGMP_IDLE_MEMBER:
463 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
464 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
465 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
466 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
467 break;
468 case IGMP_LAZY_MEMBER:
469 case IGMP_AWAKENING_MEMBER:
470 case IGMP_SLEEPING_MEMBER:
471 break;
472 }
473 }
474
475 void
476 igmp_fasttimo()
477 {
478 struct in_multi *inm;
479 struct in_multistep step;
480 int s;
481
482 /*
483 * Quick check to see if any work needs to be done, in order
484 * to minimize the overhead of fasttimo processing.
485 */
486 if (!igmp_timers_are_running)
487 return;
488
489 s = splsoftnet();
490 igmp_timers_are_running = 0;
491 IN_FIRST_MULTI(step, inm);
492 while (inm != NULL) {
493 if (inm->inm_timer == 0) {
494 /* do nothing */
495 } else if (--inm->inm_timer == 0) {
496 if (inm->inm_state == IGMP_DELAYING_MEMBER) {
497 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
498 igmp_sendpkt(inm,
499 IGMP_v1_HOST_MEMBERSHIP_REPORT);
500 else
501 igmp_sendpkt(inm,
502 IGMP_v2_HOST_MEMBERSHIP_REPORT);
503 inm->inm_state = IGMP_IDLE_MEMBER;
504 }
505 } else {
506 igmp_timers_are_running = 1;
507 }
508 IN_NEXT_MULTI(step, inm);
509 }
510 splx(s);
511 }
512
513 void
514 igmp_slowtimo()
515 {
516 struct router_info *rti;
517 int s;
518
519 s = splsoftnet();
520 LIST_FOREACH(rti, &rti_head, rti_link) {
521 if (rti->rti_type == IGMP_v1_ROUTER &&
522 ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
523 rti->rti_type = IGMP_v2_ROUTER;
524 }
525 }
526 splx(s);
527 }
528
529 void
530 igmp_sendpkt(inm, type)
531 struct in_multi *inm;
532 int type;
533 {
534 struct mbuf *m;
535 struct igmp *igmp;
536 struct ip *ip;
537 struct ip_moptions imo;
538 #ifdef MROUTING
539 extern struct socket *ip_mrouter;
540 #endif /* MROUTING */
541
542 MGETHDR(m, M_DONTWAIT, MT_HEADER);
543 if (m == NULL)
544 return;
545 /*
546 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
547 * is smaller than mbuf size returned by MGETHDR.
548 */
549 m->m_data += max_linkhdr;
550 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
551 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
552
553 ip = mtod(m, struct ip *);
554 ip->ip_tos = 0;
555 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
556 ip->ip_off = htons(0);
557 ip->ip_p = IPPROTO_IGMP;
558 ip->ip_src = zeroin_addr;
559 ip->ip_dst = inm->inm_addr;
560
561 m->m_data += sizeof(struct ip);
562 m->m_len -= sizeof(struct ip);
563 igmp = mtod(m, struct igmp *);
564 igmp->igmp_type = type;
565 igmp->igmp_code = 0;
566 igmp->igmp_group = inm->inm_addr;
567 igmp->igmp_cksum = 0;
568 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
569 m->m_data -= sizeof(struct ip);
570 m->m_len += sizeof(struct ip);
571
572 imo.imo_multicast_ifp = inm->inm_ifp;
573 imo.imo_multicast_ttl = 1;
574 #ifdef RSVP_ISI
575 imo.imo_multicast_vif = -1;
576 #endif
577 /*
578 * Request loopback of the report if we are acting as a multicast
579 * router, so that the process-level routing demon can hear it.
580 */
581 #ifdef MROUTING
582 imo.imo_multicast_loop = (ip_mrouter != NULL);
583 #else
584 imo.imo_multicast_loop = 0;
585 #endif /* MROUTING */
586
587 ip_output(m, (struct mbuf *)NULL, (struct route *)NULL,
588 IP_MULTICASTOPTS, &imo, (struct socket *)NULL);
589
590 ++igmpstat.igps_snd_reports;
591 }
592
593 void
594 igmp_purgeif(ifp)
595 struct ifnet *ifp;
596 {
597
598 rti_delete(ifp);
599 }
600