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