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