xref: /openbsd-src/sys/netinet/igmp.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: igmp.c,v 1.18 2003/07/09 22:03:16 itojun 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 <machine/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
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
56 rti_fill(inm)
57 	struct in_multi *inm;
58 {
59 	register 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 *
84 rti_find(ifp)
85 	struct ifnet *ifp;
86 {
87 	register 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
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
122 igmp_input(struct mbuf *m, ...)
123 {
124 	register int iphlen;
125 	register struct ifnet *ifp = m->m_pkthdr.rcvif;
126 	register struct ip *ip = mtod(m, struct ip *);
127 	register struct igmp *igmp;
128 	register int igmplen;
129 	register int minlen;
130 	struct in_multi *inm;
131 	struct in_multistep step;
132 	struct router_info *rti;
133 	register 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
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
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
439 igmp_fasttimo()
440 {
441 	register 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
477 igmp_slowtimo()
478 {
479 	register 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
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