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