xref: /openbsd-src/sys/netinet/igmp.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: igmp.c,v 1.12 2001/11/06 21:26:33 jakob 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, in_addr_t));
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, 0);
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  				    INADDR_ALLROUTERS_GROUP);
435 		break;
436 	case IGMP_LAZY_MEMBER:
437 	case IGMP_AWAKENING_MEMBER:
438 	case IGMP_SLEEPING_MEMBER:
439 		break;
440 	}
441 }
442 
443 void
444 igmp_fasttimo()
445 {
446 	register struct in_multi *inm;
447 	struct in_multistep step;
448 	int s;
449 
450 	/*
451 	 * Quick check to see if any work needs to be done, in order
452 	 * to minimize the overhead of fasttimo processing.
453 	 */
454 	if (!igmp_timers_are_running)
455 		return;
456 
457 	s = splsoftnet();
458 	igmp_timers_are_running = 0;
459 	IN_FIRST_MULTI(step, inm);
460 	while (inm != NULL) {
461 		if (inm->inm_timer == 0) {
462 			/* do nothing */
463 		} else if (--inm->inm_timer == 0) {
464 			if (inm->inm_state == IGMP_DELAYING_MEMBER) {
465 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
466 					igmp_sendpkt(inm,
467 					    IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
468 				else
469 					igmp_sendpkt(inm,
470 					    IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
471 				inm->inm_state = IGMP_IDLE_MEMBER;
472 			}
473 		} else {
474 			igmp_timers_are_running = 1;
475 		}
476 		IN_NEXT_MULTI(step, inm);
477 	}
478 	splx(s);
479 }
480 
481 void
482 igmp_slowtimo()
483 {
484 	register struct router_info *rti;
485 	int s;
486 
487 	s = splsoftnet();
488 	for (rti = rti_head; rti != 0; rti = rti->rti_next) {
489 		if (rti->rti_type == IGMP_v1_ROUTER &&
490 		    ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
491 			rti->rti_type = IGMP_v2_ROUTER;
492 		}
493 	}
494 	splx(s);
495 }
496 
497 void
498 igmp_sendpkt(inm, type, addr)
499 	struct in_multi *inm;
500 	int type;
501  	in_addr_t addr;
502 {
503 	struct mbuf *m;
504 	struct igmp *igmp;
505 	struct ip *ip;
506 	struct ip_moptions imo;
507 #ifdef MROUTING
508 	extern struct socket *ip_mrouter;
509 #endif /* MROUTING */
510 
511 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
512 	if (m == NULL)
513 		return;
514 	/*
515 	 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
516 	 * is smaller than mbuf size returned by MGETHDR.
517 	 */
518 	m->m_data += max_linkhdr;
519 	m->m_len = sizeof(struct ip) + IGMP_MINLEN;
520 	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
521 
522 	ip = mtod(m, struct ip *);
523 	ip->ip_tos = 0;
524 	ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
525 	ip->ip_off = 0;
526 	ip->ip_p = IPPROTO_IGMP;
527 	ip->ip_src.s_addr = INADDR_ANY;
528  	if (addr) {
529  		ip->ip_dst.s_addr = addr;
530  	} else {
531  		ip->ip_dst = inm->inm_addr;
532  	}
533 
534 	m->m_data += sizeof(struct ip);
535 	m->m_len -= sizeof(struct ip);
536 	igmp = mtod(m, struct igmp *);
537 	igmp->igmp_type = type;
538 	igmp->igmp_code = 0;
539 	igmp->igmp_group = inm->inm_addr;
540 	igmp->igmp_cksum = 0;
541 	igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
542 	m->m_data -= sizeof(struct ip);
543 	m->m_len += sizeof(struct ip);
544 
545 	imo.imo_multicast_ifp = inm->inm_ifp;
546 	imo.imo_multicast_ttl = 1;
547 #ifdef RSVP_ISI
548 	imo.imo_multicast_vif = -1;
549 #endif
550 	/*
551 	 * Request loopback of the report if we are acting as a multicast
552 	 * router, so that the process-level routing demon can hear it.
553 	 */
554 #ifdef MROUTING
555 	imo.imo_multicast_loop = (ip_mrouter != NULL);
556 #else
557 	imo.imo_multicast_loop = 0;
558 #endif /* MROUTING */
559 
560 	ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
561 	    &imo, NULL);
562 
563 	++igmpstat.igps_snd_reports;
564 }
565