xref: /freebsd/sys/netinet/igmp.c (revision a316b26e50bbed7cf655fbba726ab87d8ab7599d)
1 /*
2  * Copyright (c) 1988 Stephen Deering.
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Stephen Deering of Stanford University.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
38  * $Id: igmp.c,v 1.5 1994/09/14 03:10:07 wollman Exp $
39  */
40 
41 /*
42  * Internet Group Management Protocol (IGMP) routines.
43  *
44  * Written by Steve Deering, Stanford, May 1988.
45  * Modified by Rosen Sharma, Stanford, Aug 1994.
46  *
47  * MULTICAST 1.4
48  */
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/mbuf.h>
53 #include <sys/socket.h>
54 #include <sys/protosw.h>
55 
56 #include <net/if.h>
57 #include <net/route.h>
58 
59 #include <netinet/in.h>
60 #include <netinet/in_var.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63 #include <netinet/ip_var.h>
64 #include <netinet/igmp.h>
65 #include <netinet/igmp_var.h>
66 
67 extern struct ifnet loif;
68 
69 struct igmpstat igmpstat;
70 
71 static int igmp_timers_are_running = 0;
72 static u_long igmp_all_hosts_group;
73 static struct router_info *Head = 0;
74 
75 static void igmp_sendpkt(struct in_multi *, int);
76 static void igmp_sendleave(struct in_multi *);
77 
78 void
79 igmp_init()
80 {
81 	/*
82 	 * To avoid byte-swapping the same value over and over again.
83 	 */
84 	igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
85 	Head = (struct router_info *) 0;
86 }
87 
88 int
89 fill_rti(inm)
90 	struct in_multi *inm;
91 {
92 	register struct router_info *rti = Head;
93 
94 #ifdef IGMP_DEBUG
95 	printf("[igmp.c, _fill_rti] --> entering \n");
96 #endif
97 	while (rti) {
98 		if (rti->ifp == inm->inm_ifp){ /* ? is it ok to compare */
99 					       /* pointers */
100 			inm->inm_rti  = rti;
101 #ifdef IGMP_DEBUG
102 			printf("[igmp.c, _fill_rti] --> found old entry \n");
103 #endif
104 			if (rti->type == IGMP_OLD_ROUTER)
105 				return IGMP_HOST_MEMBERSHIP_REPORT;
106 			else
107 				return IGMP_HOST_NEW_MEMBERSHIP_REPORT;
108 		}
109 		rti = rti->next;
110 	}
111 	MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT);
112 	rti->ifp = inm->inm_ifp;
113 	rti->type = IGMP_NEW_ROUTER;
114 	rti->time = IGMP_AGE_THRESHOLD;
115 	rti->next = Head;
116 	Head = rti;
117 	inm->inm_rti = rti;
118 #ifdef IGMP_DEBUG
119 	printf("[igmp.c, _fill_rti] --> created new entry \n");
120 #endif
121 	return IGMP_HOST_NEW_MEMBERSHIP_REPORT;
122 }
123 
124 struct router_info *
125 find_rti(ifp)
126 	struct ifnet *ifp;
127 {
128         register struct router_info *rti = Head;
129 
130 #ifdef IGMP_DEBUG
131 	printf("[igmp.c, _find_rti] --> entering \n");
132 #endif
133         while (rti) {
134                 if (rti->ifp == ifp){ /* ? is it ok to compare pointers */
135 #ifdef IGMP_DEBUG
136 			printf("[igmp.c, _find_rti] --> found old entry \n");
137 #endif
138                         return rti;
139                 }
140                 rti = rti->next;
141         }
142 	MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT);
143         rti->ifp = ifp;
144         rti->type = IGMP_NEW_ROUTER;
145         rti->time = IGMP_AGE_THRESHOLD;
146         rti->next = Head;
147         Head = rti;
148 #ifdef IGMP_DEBUG
149 	printf("[igmp.c, _find_rti] --> created an entry \n");
150 #endif
151         return rti;
152 }
153 
154 void
155 igmp_input(m, iphlen)
156 	register struct mbuf *m;
157 	register int iphlen;
158 {
159 	register struct igmp *igmp;
160 	register struct ip *ip;
161 	register int igmplen;
162 	register struct ifnet *ifp = m->m_pkthdr.rcvif;
163 	register int minlen;
164 	register struct in_multi *inm;
165 	register struct in_ifaddr *ia;
166 	struct in_multistep step;
167 	struct router_info *rti;
168 
169 	static int timer; /** timer value in the igmp query header **/
170 
171 	++igmpstat.igps_rcv_total;
172 
173 	ip = mtod(m, struct ip *);
174 	igmplen = ip->ip_len;
175 
176 	/*
177 	 * Validate lengths
178 	 */
179 	if (igmplen < IGMP_MINLEN) {
180 		++igmpstat.igps_rcv_tooshort;
181 		m_freem(m);
182 		return;
183 	}
184 	minlen = iphlen + IGMP_MINLEN;
185 	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
186 	    (m = m_pullup(m, minlen)) == 0) {
187 		++igmpstat.igps_rcv_tooshort;
188 		return;
189 	}
190 
191 	/*
192 	 * Validate checksum
193 	 */
194 	m->m_data += iphlen;
195 	m->m_len -= iphlen;
196 	igmp = mtod(m, struct igmp *);
197 	if (in_cksum(m, igmplen)) {
198 		++igmpstat.igps_rcv_badsum;
199 		m_freem(m);
200 		return;
201 	}
202 	m->m_data -= iphlen;
203 	m->m_len += iphlen;
204 
205 	ip = mtod(m, struct ip *);
206 	timer = ntohs(igmp->igmp_code);
207 	rti = find_rti(ifp);
208 
209 	switch (igmp->igmp_type) {
210 
211 	case IGMP_HOST_MEMBERSHIP_QUERY:
212 		++igmpstat.igps_rcv_queries;
213 
214 		if (ifp == &loif)
215 			break;
216 
217 		if (igmp->igmp_code == 0) {
218 
219 			rti->type = IGMP_OLD_ROUTER; rti->time = 0;
220 
221 			/*
222 			** Do exactly as RFC 1112 says
223 			*/
224 
225 			if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
226 				++igmpstat.igps_rcv_badqueries;
227 				m_freem(m);
228 				return;
229 			}
230 
231 			/*
232 			 * Start the timers in all of our membership records for
233 			 * the interface on which the query arrived, except those
234 			 * that are already running and those that belong to the
235 			 * "all-hosts" group.
236 			 */
237 			IN_FIRST_MULTI(step, inm);
238 			while (inm != NULL) {
239 				if (inm->inm_ifp == ifp
240 				    && inm->inm_timer == 0
241 				    && inm->inm_addr.s_addr
242 				    != igmp_all_hosts_group) {
243 
244 					inm->inm_state = IGMP_DELAYING_MEMBER;
245 					inm->inm_timer = IGMP_RANDOM_DELAY(
246 				IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ );
247 
248 					igmp_timers_are_running = 1;
249 				}
250 				IN_NEXT_MULTI(step, inm);
251 			}
252 		} else {
253 		    /*
254 		    ** New Router
255 		    */
256 
257 		    if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
258 			if (!(m->m_flags & M_MCAST)) {
259 			    ++igmpstat.igps_rcv_badqueries;
260 			    m_freem(m);
261 			    return;
262 			}
263 		    }
264 		    if (ip->ip_dst.s_addr == igmp_all_hosts_group) {
265 
266 			/*
267 			 * - Start the timers in all of our membership records
268 			 *   for the interface on which the query arrived
269 			 *   excl. those that belong to the "all-hosts" group.
270 			 * - For timers already running check if they need to
271 			 *   be reset.
272 			 * - Use the igmp->igmp_code filed as the maximum
273 			 *   delay possible
274 			 */
275 			IN_FIRST_MULTI(step, inm);
276 			while (inm != NULL){
277 			    switch(inm->inm_state){
278 			      case IGMP_IDLE_MEMBER:
279 			      case IGMP_LAZY_MEMBER:
280 			      case IGMP_AWAKENING_MEMBER:
281 				if (inm->inm_ifp == ifp  &&
282 				    inm->inm_addr.s_addr !=
283 						igmp_all_hosts_group) {
284 				    inm->inm_timer = IGMP_RANDOM_DELAY(timer);
285 				    igmp_timers_are_running = 1;
286 				    inm->inm_state = IGMP_DELAYING_MEMBER;
287 				}
288 				break;
289 			      case IGMP_DELAYING_MEMBER:
290 				if (inm->inm_ifp == ifp &&
291 				    (inm->inm_timer >
292 					timer * PR_FASTHZ / IGMP_TIMER_SCALE)
293 					&&
294 				    inm->inm_addr.s_addr !=
295 						igmp_all_hosts_group) {
296 				    inm->inm_timer = IGMP_RANDOM_DELAY(timer);
297 				    igmp_timers_are_running = 1;
298 				    inm->inm_state = IGMP_DELAYING_MEMBER;
299 				}
300 				break;
301 			      case IGMP_SLEEPING_MEMBER:
302 				inm->inm_state = IGMP_AWAKENING_MEMBER;
303 				break;
304 			    }
305 			    IN_NEXT_MULTI(step, inm);
306 			  }
307 		    } else {
308 		      /*
309 		      ** group specific query
310 		      */
311 
312 		      IN_FIRST_MULTI(step, inm);
313 		      while (inm != NULL) {
314 			if (inm->inm_addr.s_addr == ip->ip_dst.s_addr) {
315 			  switch(inm->inm_state ){
316 			  case IGMP_IDLE_MEMBER:
317 			  case IGMP_LAZY_MEMBER:
318 			  case IGMP_AWAKENING_MEMBER:
319 			    inm->inm_state = IGMP_DELAYING_MEMBER;
320 			    if (inm->inm_ifp == ifp ) {
321 			      inm->inm_timer = IGMP_RANDOM_DELAY(timer);
322 			      igmp_timers_are_running = 1;
323 			      inm->inm_state = IGMP_DELAYING_MEMBER;
324 			    }
325 			    break;
326 			  case IGMP_DELAYING_MEMBER:
327 			    inm->inm_state = IGMP_DELAYING_MEMBER;
328 			    if (inm->inm_ifp == ifp &&
329 				(inm->inm_timer >
330 				 timer * PR_FASTHZ / IGMP_TIMER_SCALE) ) {
331 			      inm->inm_timer = IGMP_RANDOM_DELAY(timer);
332 			      igmp_timers_are_running = 1;
333 			      inm->inm_state = IGMP_DELAYING_MEMBER;
334 			    }
335 			    break;
336 			  case IGMP_SLEEPING_MEMBER:
337 			    inm->inm_state = IGMP_AWAKENING_MEMBER;
338 			    break;
339 			  }
340 			}
341 			IN_NEXT_MULTI(step, inm);
342 		}
343 	      }
344 	    }
345 		break;
346 
347 	case IGMP_HOST_MEMBERSHIP_REPORT:
348 		++igmpstat.igps_rcv_reports;
349 
350 		if (ifp == &loif)
351 			break;
352 
353 		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
354 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
355 			++igmpstat.igps_rcv_badreports;
356 			m_freem(m);
357 			return;
358 		}
359 
360 		/*
361 		 * KLUDGE: if the IP source address of the report has an
362 		 * unspecified (i.e., zero) subnet number, as is allowed for
363 		 * a booting host, replace it with the correct subnet number
364 		 * so that a process-level multicast routing demon can
365 		 * determine which subnet it arrived from.  This is necessary
366 		 * to compensate for the lack of any way for a process to
367 		 * determine the arrival interface of an incoming packet.
368 		 */
369 		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
370 			IFP_TO_IA(ifp, ia);
371 			if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
372 		}
373 
374 		/*
375 		 * If we belong to the group being reported, stop
376 		 * our timer for that group.
377 		 */
378 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
379 		if (inm != NULL) {
380 			inm->inm_timer = 0;
381 			++igmpstat.igps_rcv_ourreports;
382 		}
383 
384 		if (inm != NULL) {
385 		  inm->inm_timer = 0;
386 		  ++igmpstat.igps_rcv_ourreports;
387 
388 		  switch(inm->inm_state){
389 		  case IGMP_IDLE_MEMBER:
390 		  case IGMP_LAZY_MEMBER:
391 		  case IGMP_AWAKENING_MEMBER:
392 		  case IGMP_SLEEPING_MEMBER:
393 		    inm->inm_state = IGMP_SLEEPING_MEMBER;
394 		    break;
395 		  case IGMP_DELAYING_MEMBER:
396 		    /** check this out - this was  if (oldrouter)    **/
397 		    if (inm->inm_rti->type == IGMP_OLD_ROUTER)
398 			inm->inm_state = IGMP_LAZY_MEMBER;
399 		    else inm->inm_state = IGMP_SLEEPING_MEMBER;
400 		    break;
401 		  }
402 		}
403 
404 		break;
405 
406 	      case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
407 		/*
408 		 * an new report
409 		 */
410 		++igmpstat.igps_rcv_reports;
411 
412 		if (ifp == &loif)
413 		  break;
414 
415 		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
416 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
417 		  ++igmpstat.igps_rcv_badreports;
418 		  m_freem(m);
419 		  return;
420 		}
421 
422 		/*
423 		 * KLUDGE: if the IP source address of the report has an
424 		 * unspecified (i.e., zero) subnet number, as is allowed for
425 		 * a booting host, replace it with the correct subnet number
426 		 * so that a process-level multicast routing demon can
427 		 * determine which subnet it arrived from.  This is necessary
428 		 * to compensate for the lack of any way for a process to
429 		 * determine the arrival interface of an incoming packet.
430 		 */
431 		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
432 		  IFP_TO_IA(ifp, ia);
433 		  if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
434 		}
435 
436 		/*
437 		 * If we belong to the group being reported, stop
438 		 * our timer for that group.
439 		 */
440 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
441 		if (inm != NULL) {
442 		  inm->inm_timer = 0;
443 		  ++igmpstat.igps_rcv_ourreports;
444 
445 		  switch(inm->inm_state){
446 		  case IGMP_DELAYING_MEMBER:
447 		  case IGMP_IDLE_MEMBER:
448 		    inm->inm_state = IGMP_LAZY_MEMBER;
449 		    break;
450 		  case IGMP_AWAKENING_MEMBER:
451 		    inm->inm_state = IGMP_LAZY_MEMBER;
452 		    break;
453 		  case IGMP_LAZY_MEMBER:
454 		  case IGMP_SLEEPING_MEMBER:
455 		    break;
456 		  }
457 		}
458 	}
459 
460 	/*
461 	 * Pass all valid IGMP packets up to any process(es) listening
462 	 * on a raw IGMP socket.
463 	 */
464 	rip_input(m);
465 }
466 
467 void
468 igmp_joingroup(inm)
469 	struct in_multi *inm;
470 {
471 	register int s = splnet();
472 
473 	inm->inm_state = IGMP_IDLE_MEMBER;
474 
475 	if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
476 	    inm->inm_ifp == &loif)
477 		inm->inm_timer = 0;
478 	else {
479 		igmp_sendpkt(inm,fill_rti(inm));
480 		inm->inm_timer = IGMP_RANDOM_DELAY(
481 					IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ);
482 		inm->inm_state = IGMP_DELAYING_MEMBER;
483 		igmp_timers_are_running = 1;
484 	}
485 	splx(s);
486 }
487 
488 void
489 igmp_leavegroup(inm)
490 	struct in_multi *inm;
491 {
492 	/*
493 	 * No action required on leaving a group.
494 	 */
495          switch(inm->inm_state){
496 	 case IGMP_DELAYING_MEMBER:
497 	 case IGMP_IDLE_MEMBER:
498 	   if (!(inm->inm_addr.s_addr == igmp_all_hosts_group ||
499 	       inm->inm_ifp == &loif))
500 	       if (inm->inm_rti->type != IGMP_OLD_ROUTER)
501 		   igmp_sendleave(inm);
502 	   break;
503 	 case IGMP_LAZY_MEMBER:
504 	 case IGMP_AWAKENING_MEMBER:
505 	 case IGMP_SLEEPING_MEMBER:
506 	   break;
507 	 }
508 }
509 
510 void
511 igmp_fasttimo()
512 {
513 	register struct in_multi *inm;
514 	register int s;
515 	struct in_multistep step;
516 
517 	/*
518 	 * Quick check to see if any work needs to be done, in order
519 	 * to minimize the overhead of fasttimo processing.
520 	 */
521 	if (!igmp_timers_are_running)
522 		return;
523 
524 	s = splnet();
525 	igmp_timers_are_running = 0;
526 	IN_FIRST_MULTI(step, inm);
527 	while (inm != NULL) {
528 		if (inm->inm_timer == 0) {
529 			/* do nothing */
530 		} else if (--inm->inm_timer == 0) {
531 		  if (inm->inm_state == IGMP_DELAYING_MEMBER) {
532 		    if (inm->inm_rti->type == IGMP_OLD_ROUTER)
533 			igmp_sendpkt(inm, IGMP_HOST_MEMBERSHIP_REPORT);
534 		    else
535 			igmp_sendpkt(inm, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
536 		    inm->inm_state = IGMP_IDLE_MEMBER;
537 		  }
538 		} else {
539 			igmp_timers_are_running = 1;
540 		}
541 		IN_NEXT_MULTI(step, inm);
542 	}
543 	splx(s);
544 }
545 
546 void
547 igmp_slowtimo()
548 {
549 	int s = splnet();
550 	register struct router_info *rti =  Head;
551 
552 #ifdef IGMP_DEBUG
553 	printf("[igmp.c,_slowtimo] -- > entering \n");
554 #endif
555 	while (rti) {
556 		rti->time ++;
557 		if (rti->time >= IGMP_AGE_THRESHOLD){
558 			rti->type = IGMP_NEW_ROUTER;
559 			rti->time = IGMP_AGE_THRESHOLD;
560 		}
561 		rti = rti->next;
562 	}
563 #ifdef IGMP_DEBUG
564 	printf("[igmp.c,_slowtimo] -- > exiting \n");
565 #endif
566 	splx(s);
567 }
568 
569 static void
570 igmp_sendpkt(inm, type)
571 	struct in_multi *inm;
572 	int type;
573 {
574         struct mbuf *m;
575         struct igmp *igmp;
576         struct ip *ip;
577         struct ip_moptions *imo;
578 
579         MGETHDR(m, M_DONTWAIT, MT_HEADER);
580         if (m == NULL)
581                 return;
582 
583 	MALLOC(imo, struct ip_moptions *, sizeof *imo, M_IPMOPTS, M_DONTWAIT);
584 	if (!imo) {
585 		m_free(m);
586 		return;
587 	}
588 
589 	m->m_pkthdr.rcvif = &loif;
590 	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
591 	MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip));
592 	m->m_data += sizeof(struct ip);
593         m->m_len = IGMP_MINLEN;
594         igmp = mtod(m, struct igmp *);
595         igmp->igmp_type   = type;
596         igmp->igmp_code   = 0;
597         igmp->igmp_group  = inm->inm_addr;
598         igmp->igmp_cksum  = 0;
599         igmp->igmp_cksum  = in_cksum(m, IGMP_MINLEN);
600 
601         m->m_data -= sizeof(struct ip);
602         m->m_len += sizeof(struct ip);
603         ip = mtod(m, struct ip *);
604         ip->ip_tos        = 0;
605         ip->ip_len        = sizeof(struct ip) + IGMP_MINLEN;
606         ip->ip_off        = 0;
607         ip->ip_p          = IPPROTO_IGMP;
608         ip->ip_src.s_addr = INADDR_ANY;
609         ip->ip_dst        = igmp->igmp_group;
610 
611         imo->imo_multicast_ifp  = inm->inm_ifp;
612         imo->imo_multicast_ttl  = 1;
613         /*
614          * Request loopback of the report if we are acting as a multicast
615          * router, so that the process-level routing demon can hear it.
616          */
617         imo->imo_multicast_loop = (ip_mrouter != NULL);
618 
619         ip_output(m, (struct mbuf *)0, (struct route *)0, 0, imo);
620 
621 	FREE(imo, M_IPMOPTS);
622         ++igmpstat.igps_snd_reports;
623 
624 }
625 
626 static void
627 igmp_sendleave(inm)
628 	struct in_multi *inm;
629 {
630 	igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
631 }
632