igmp.c revision 6472
1279430Srstone/*
2279430Srstone * Copyright (c) 1988 Stephen Deering.
3279430Srstone * Copyright (c) 1992, 1993
4279430Srstone *	The Regents of the University of California.  All rights reserved.
5279430Srstone *
6279430Srstone * This code is derived from software contributed to Berkeley by
7279430Srstone * Stephen Deering of Stanford University.
8279430Srstone *
9279430Srstone * Redistribution and use in source and binary forms, with or without
10279430Srstone * modification, are permitted provided that the following conditions
11279430Srstone * are met:
12279430Srstone * 1. Redistributions of source code must retain the above copyright
13279430Srstone *    notice, this list of conditions and the following disclaimer.
14279430Srstone * 2. Redistributions in binary form must reproduce the above copyright
15279430Srstone *    notice, this list of conditions and the following disclaimer in the
16279430Srstone *    documentation and/or other materials provided with the distribution.
17279430Srstone * 3. All advertising materials mentioning features or use of this software
18279430Srstone *    must display the following acknowledgement:
19279430Srstone *	This product includes software developed by the University of
20279430Srstone *	California, Berkeley and its contributors.
21279430Srstone * 4. Neither the name of the University nor the names of its contributors
22279430Srstone *    may be used to endorse or promote products derived from this software
23279430Srstone *    without specific prior written permission.
24279430Srstone *
25279430Srstone * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26279430Srstone * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27279430Srstone * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28279430Srstone * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29279430Srstone * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30279430Srstone * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31279430Srstone * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32279430Srstone * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33279430Srstone * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34279430Srstone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35279430Srstone * SUCH DAMAGE.
36279430Srstone *
37279430Srstone *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
38279430Srstone * $Id: igmp.c,v 1.6 1994/10/31 06:36:47 pst Exp $
39279430Srstone */
40279430Srstone
41279430Srstone/*
42279430Srstone * Internet Group Management Protocol (IGMP) routines.
43279430Srstone *
44279430Srstone * Written by Steve Deering, Stanford, May 1988.
45279430Srstone * Modified by Rosen Sharma, Stanford, Aug 1994.
46279430Srstone *
47279430Srstone * MULTICAST 1.4
48292637Sngie */
49279430Srstone
50279430Srstone#include <sys/param.h>
51279430Srstone#include <sys/systm.h>
52279430Srstone#include <sys/mbuf.h>
53279430Srstone#include <sys/socket.h>
54279430Srstone#include <sys/protosw.h>
55279430Srstone#include <sys/proc.h>		/* XXX needed for sysctl.h */
56279430Srstone#include <vm/vm.h>		/* XXX needed for sysctl.h */
57279430Srstone#include <sys/sysctl.h>
58279430Srstone
59279430Srstone#include <net/if.h>
60279430Srstone#include <net/route.h>
61279430Srstone
62279430Srstone#include <netinet/in.h>
63292637Sngie#include <netinet/in_var.h>
64279430Srstone#include <netinet/in_systm.h>
65279430Srstone#include <netinet/ip.h>
66279430Srstone#include <netinet/ip_var.h>
67279430Srstone#include <netinet/igmp.h>
68292637Sngie#include <netinet/igmp_var.h>
69279430Srstone
70279430Srstoneextern struct ifnet loif;
71279430Srstone
72279430Srstonestruct igmpstat igmpstat;
73279430Srstone
74279430Srstonestatic int igmp_timers_are_running = 0;
75279430Srstonestatic u_long igmp_all_hosts_group;
76279430Srstonestatic struct router_info *Head = 0;
77279430Srstone
78279430Srstonestatic void igmp_sendpkt(struct in_multi *, int);
79279430Srstonestatic void igmp_sendleave(struct in_multi *);
80279430Srstone
81279430Srstonevoid
82279430Srstoneigmp_init()
83279430Srstone{
84279430Srstone	/*
85279430Srstone	 * To avoid byte-swapping the same value over and over again.
86279430Srstone	 */
87292637Sngie	igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
88279430Srstone	Head = (struct router_info *) 0;
89279430Srstone}
90279430Srstone
91279430Srstoneint
92279430Srstonefill_rti(inm)
93279430Srstone	struct in_multi *inm;
94279430Srstone{
95279430Srstone	register struct router_info *rti = Head;
96279430Srstone
97279430Srstone#ifdef IGMP_DEBUG
98279430Srstone	printf("[igmp.c, _fill_rti] --> entering \n");
99279430Srstone#endif
100279430Srstone	while (rti) {
101279430Srstone		if (rti->ifp == inm->inm_ifp){ /* ? is it ok to compare */
102292637Sngie					       /* pointers */
103279430Srstone			inm->inm_rti  = rti;
104279430Srstone#ifdef IGMP_DEBUG
105279430Srstone			printf("[igmp.c, _fill_rti] --> found old entry \n");
106292637Sngie#endif
107279430Srstone			if (rti->type == IGMP_OLD_ROUTER)
108279430Srstone				return IGMP_HOST_MEMBERSHIP_REPORT;
109279430Srstone			else
110279430Srstone				return IGMP_HOST_NEW_MEMBERSHIP_REPORT;
111279430Srstone		}
112279430Srstone		rti = rti->next;
113279430Srstone	}
114279430Srstone	MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT);
115279430Srstone	rti->ifp = inm->inm_ifp;
116279430Srstone	rti->type = IGMP_NEW_ROUTER;
117279430Srstone	rti->time = IGMP_AGE_THRESHOLD;
118279430Srstone	rti->next = Head;
119279430Srstone	Head = rti;
120279430Srstone	inm->inm_rti = rti;
121279430Srstone#ifdef IGMP_DEBUG
122279430Srstone	printf("[igmp.c, _fill_rti] --> created new entry \n");
123279430Srstone#endif
124279430Srstone	return IGMP_HOST_NEW_MEMBERSHIP_REPORT;
125279430Srstone}
126292637Sngie
127279430Srstonestruct router_info *
128279430Srstonefind_rti(ifp)
129279430Srstone	struct ifnet *ifp;
130279430Srstone{
131279430Srstone        register struct router_info *rti = Head;
132279430Srstone
133279430Srstone#ifdef IGMP_DEBUG
134279430Srstone	printf("[igmp.c, _find_rti] --> entering \n");
135279430Srstone#endif
136279430Srstone        while (rti) {
137279430Srstone                if (rti->ifp == ifp){ /* ? is it ok to compare pointers */
138279430Srstone#ifdef IGMP_DEBUG
139279430Srstone			printf("[igmp.c, _find_rti] --> found old entry \n");
140279430Srstone#endif
141279430Srstone                        return rti;
142279430Srstone                }
143279430Srstone                rti = rti->next;
144292637Sngie        }
145279430Srstone	MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT);
146279430Srstone        rti->ifp = ifp;
147279430Srstone        rti->type = IGMP_NEW_ROUTER;
148279430Srstone        rti->time = IGMP_AGE_THRESHOLD;
149279430Srstone        rti->next = Head;
150292637Sngie        Head = rti;
151319370Sngie#ifdef IGMP_DEBUG
152279430Srstone	printf("[igmp.c, _find_rti] --> created an entry \n");
153279430Srstone#endif
154279430Srstone        return rti;
155279430Srstone}
156279430Srstone
157279430Srstonevoid
158279430Srstoneigmp_input(m, iphlen)
159279430Srstone	register struct mbuf *m;
160279430Srstone	register int iphlen;
161279430Srstone{
162279430Srstone	register struct igmp *igmp;
163279430Srstone	register struct ip *ip;
164279430Srstone	register int igmplen;
165279430Srstone	register struct ifnet *ifp = m->m_pkthdr.rcvif;
166279430Srstone	register int minlen;
167279430Srstone	register struct in_multi *inm;
168279430Srstone	register struct in_ifaddr *ia;
169279430Srstone	struct in_multistep step;
170279430Srstone	struct router_info *rti;
171279430Srstone
172279430Srstone	static int timer; /** timer value in the igmp query header **/
173279430Srstone
174279430Srstone	++igmpstat.igps_rcv_total;
175279430Srstone
176279430Srstone	ip = mtod(m, struct ip *);
177279430Srstone	igmplen = ip->ip_len;
178279430Srstone
179279430Srstone	/*
180279430Srstone	 * Validate lengths
181279430Srstone	 */
182279430Srstone	if (igmplen < IGMP_MINLEN) {
183279430Srstone		++igmpstat.igps_rcv_tooshort;
184279430Srstone		m_freem(m);
185279430Srstone		return;
186279430Srstone	}
187279430Srstone	minlen = iphlen + IGMP_MINLEN;
188279430Srstone	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
189279430Srstone	    (m = m_pullup(m, minlen)) == 0) {
190279430Srstone		++igmpstat.igps_rcv_tooshort;
191279430Srstone		return;
192292637Sngie	}
193279430Srstone
194279430Srstone	/*
195279430Srstone	 * Validate checksum
196279430Srstone	 */
197279430Srstone	m->m_data += iphlen;
198279430Srstone	m->m_len -= iphlen;
199279430Srstone	igmp = mtod(m, struct igmp *);
200279430Srstone	if (in_cksum(m, igmplen)) {
201279430Srstone		++igmpstat.igps_rcv_badsum;
202279430Srstone		m_freem(m);
203279430Srstone		return;
204279430Srstone	}
205279430Srstone	m->m_data -= iphlen;
206279430Srstone	m->m_len += iphlen;
207279430Srstone
208279430Srstone	ip = mtod(m, struct ip *);
209279430Srstone	timer = ntohs(igmp->igmp_code);
210279430Srstone	rti = find_rti(ifp);
211279430Srstone
212279430Srstone	switch (igmp->igmp_type) {
213279430Srstone
214279430Srstone	case IGMP_HOST_MEMBERSHIP_QUERY:
215279430Srstone		++igmpstat.igps_rcv_queries;
216279430Srstone
217279430Srstone		if (ifp == &loif)
218279430Srstone			break;
219279430Srstone
220279430Srstone		if (igmp->igmp_code == 0) {
221279430Srstone
222279430Srstone			rti->type = IGMP_OLD_ROUTER; rti->time = 0;
223279430Srstone
224279430Srstone			/*
225279430Srstone			** Do exactly as RFC 1112 says
226279430Srstone			*/
227279430Srstone
228279430Srstone			if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
229279430Srstone				++igmpstat.igps_rcv_badqueries;
230279430Srstone				m_freem(m);
231279430Srstone				return;
232279430Srstone			}
233279430Srstone
234279430Srstone			/*
235279430Srstone			 * Start the timers in all of our membership records for
236279430Srstone			 * the interface on which the query arrived, except those
237279430Srstone			 * that are already running and those that belong to the
238279430Srstone			 * "all-hosts" group.
239279430Srstone			 */
240279430Srstone			IN_FIRST_MULTI(step, inm);
241279430Srstone			while (inm != NULL) {
242279430Srstone				if (inm->inm_ifp == ifp
243279430Srstone				    && inm->inm_timer == 0
244292637Sngie				    && inm->inm_addr.s_addr
245292637Sngie				    != igmp_all_hosts_group) {
246279430Srstone
247279430Srstone					inm->inm_state = IGMP_DELAYING_MEMBER;
248279430Srstone					inm->inm_timer = IGMP_RANDOM_DELAY(
249279430Srstone				IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ );
250279430Srstone
251279430Srstone					igmp_timers_are_running = 1;
252279430Srstone				}
253279430Srstone				IN_NEXT_MULTI(step, inm);
254279430Srstone			}
255279430Srstone		} else {
256279430Srstone		    /*
257279430Srstone		    ** New Router
258279430Srstone		    */
259292637Sngie
260292637Sngie		    if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
261279430Srstone			if (!(m->m_flags & M_MCAST)) {
262279430Srstone			    ++igmpstat.igps_rcv_badqueries;
263279430Srstone			    m_freem(m);
264279430Srstone			    return;
265279430Srstone			}
266279430Srstone		    }
267292634Sngie		    if (ip->ip_dst.s_addr == igmp_all_hosts_group) {
268292634Sngie
269292634Sngie			/*
270292634Sngie			 * - Start the timers in all of our membership records
271292634Sngie			 *   for the interface on which the query arrived
272292634Sngie			 *   excl. those that belong to the "all-hosts" group.
273292634Sngie			 * - For timers already running check if they need to
274292634Sngie			 *   be reset.
275292634Sngie			 * - Use the igmp->igmp_code filed as the maximum
276292634Sngie			 *   delay possible
277292634Sngie			 */
278292634Sngie			IN_FIRST_MULTI(step, inm);
279292634Sngie			while (inm != NULL){
280292634Sngie			    switch(inm->inm_state){
281292634Sngie			      case IGMP_IDLE_MEMBER:
282292634Sngie			      case IGMP_LAZY_MEMBER:
283292634Sngie			      case IGMP_AWAKENING_MEMBER:
284292634Sngie				if (inm->inm_ifp == ifp  &&
285292634Sngie				    inm->inm_addr.s_addr !=
286292634Sngie						igmp_all_hosts_group) {
287292634Sngie				    inm->inm_timer = IGMP_RANDOM_DELAY(timer);
288292634Sngie				    igmp_timers_are_running = 1;
289292634Sngie				    inm->inm_state = IGMP_DELAYING_MEMBER;
290292634Sngie				}
291292634Sngie				break;
292292634Sngie			      case IGMP_DELAYING_MEMBER:
293292634Sngie				if (inm->inm_ifp == ifp &&
294292634Sngie				    (inm->inm_timer >
295292634Sngie					timer * PR_FASTHZ / IGMP_TIMER_SCALE)
296292634Sngie					&&
297292634Sngie				    inm->inm_addr.s_addr !=
298292634Sngie						igmp_all_hosts_group) {
299292634Sngie				    inm->inm_timer = IGMP_RANDOM_DELAY(timer);
300292634Sngie				    igmp_timers_are_running = 1;
301292634Sngie				    inm->inm_state = IGMP_DELAYING_MEMBER;
302292634Sngie				}
303292634Sngie				break;
304292634Sngie			      case IGMP_SLEEPING_MEMBER:
305292634Sngie				inm->inm_state = IGMP_AWAKENING_MEMBER;
306292634Sngie				break;
307292634Sngie			    }
308292634Sngie			    IN_NEXT_MULTI(step, inm);
309292634Sngie			  }
310292634Sngie		    } else {
311292634Sngie		      /*
312292634Sngie		      ** group specific query
313292634Sngie		      */
314292634Sngie
315292634Sngie		      IN_FIRST_MULTI(step, inm);
316292634Sngie		      while (inm != NULL) {
317292634Sngie			if (inm->inm_addr.s_addr == ip->ip_dst.s_addr) {
318292634Sngie			  switch(inm->inm_state ){
319292634Sngie			  case IGMP_IDLE_MEMBER:
320292634Sngie			  case IGMP_LAZY_MEMBER:
321292634Sngie			  case IGMP_AWAKENING_MEMBER:
322292634Sngie			    inm->inm_state = IGMP_DELAYING_MEMBER;
323292634Sngie			    if (inm->inm_ifp == ifp ) {
324292634Sngie			      inm->inm_timer = IGMP_RANDOM_DELAY(timer);
325292634Sngie			      igmp_timers_are_running = 1;
326292634Sngie			      inm->inm_state = IGMP_DELAYING_MEMBER;
327292634Sngie			    }
328292634Sngie			    break;
329292634Sngie			  case IGMP_DELAYING_MEMBER:
330292634Sngie			    inm->inm_state = IGMP_DELAYING_MEMBER;
331292634Sngie			    if (inm->inm_ifp == ifp &&
332292634Sngie				(inm->inm_timer >
333292634Sngie				 timer * PR_FASTHZ / IGMP_TIMER_SCALE) ) {
334292634Sngie			      inm->inm_timer = IGMP_RANDOM_DELAY(timer);
335292634Sngie			      igmp_timers_are_running = 1;
336292634Sngie			      inm->inm_state = IGMP_DELAYING_MEMBER;
337292634Sngie			    }
338292634Sngie			    break;
339292634Sngie			  case IGMP_SLEEPING_MEMBER:
340292634Sngie			    inm->inm_state = IGMP_AWAKENING_MEMBER;
341292634Sngie			    break;
342292634Sngie			  }
343292634Sngie			}
344292634Sngie			IN_NEXT_MULTI(step, inm);
345292634Sngie		}
346292634Sngie	      }
347292634Sngie	    }
348292634Sngie		break;
349292634Sngie
350292634Sngie	case IGMP_HOST_MEMBERSHIP_REPORT:
351292634Sngie		++igmpstat.igps_rcv_reports;
352292634Sngie
353292634Sngie		if (ifp == &loif)
354292634Sngie			break;
355292634Sngie
356292634Sngie		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
357292634Sngie		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
358292634Sngie			++igmpstat.igps_rcv_badreports;
359292634Sngie			m_freem(m);
360292634Sngie			return;
361292634Sngie		}
362292634Sngie
363292634Sngie		/*
364292634Sngie		 * KLUDGE: if the IP source address of the report has an
365292634Sngie		 * unspecified (i.e., zero) subnet number, as is allowed for
366292634Sngie		 * a booting host, replace it with the correct subnet number
367292634Sngie		 * so that a process-level multicast routing demon can
368292634Sngie		 * determine which subnet it arrived from.  This is necessary
369292634Sngie		 * to compensate for the lack of any way for a process to
370292634Sngie		 * determine the arrival interface of an incoming packet.
371292634Sngie		 */
372292634Sngie		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
373292634Sngie			IFP_TO_IA(ifp, ia);
374292634Sngie			if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
375292634Sngie		}
376292634Sngie
377292634Sngie		/*
378292634Sngie		 * If we belong to the group being reported, stop
379292634Sngie		 * our timer for that group.
380292634Sngie		 */
381292634Sngie		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
382292634Sngie		if (inm != NULL) {
383292634Sngie			inm->inm_timer = 0;
384292634Sngie			++igmpstat.igps_rcv_ourreports;
385292634Sngie		}
386292634Sngie
387292634Sngie		if (inm != NULL) {
388292634Sngie		  inm->inm_timer = 0;
389292634Sngie		  ++igmpstat.igps_rcv_ourreports;
390292634Sngie
391292634Sngie		  switch(inm->inm_state){
392292634Sngie		  case IGMP_IDLE_MEMBER:
393292634Sngie		  case IGMP_LAZY_MEMBER:
394292634Sngie		  case IGMP_AWAKENING_MEMBER:
395292634Sngie		  case IGMP_SLEEPING_MEMBER:
396292634Sngie		    inm->inm_state = IGMP_SLEEPING_MEMBER;
397292634Sngie		    break;
398292634Sngie		  case IGMP_DELAYING_MEMBER:
399292634Sngie		    /** check this out - this was  if (oldrouter)    **/
400292634Sngie		    if (inm->inm_rti->type == IGMP_OLD_ROUTER)
401292634Sngie			inm->inm_state = IGMP_LAZY_MEMBER;
402292634Sngie		    else inm->inm_state = IGMP_SLEEPING_MEMBER;
403292634Sngie		    break;
404292634Sngie		  }
405292634Sngie		}
406292634Sngie
407292634Sngie		break;
408292634Sngie
409292634Sngie	      case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
410292634Sngie		/*
411292634Sngie		 * an new report
412292634Sngie		 */
413292634Sngie		++igmpstat.igps_rcv_reports;
414292634Sngie
415292634Sngie		if (ifp == &loif)
416292634Sngie		  break;
417292634Sngie
418292634Sngie		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
419292634Sngie		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
420292634Sngie		  ++igmpstat.igps_rcv_badreports;
421292634Sngie		  m_freem(m);
422292634Sngie		  return;
423292634Sngie		}
424292634Sngie
425292634Sngie		/*
426292634Sngie		 * KLUDGE: if the IP source address of the report has an
427292634Sngie		 * unspecified (i.e., zero) subnet number, as is allowed for
428292634Sngie		 * a booting host, replace it with the correct subnet number
429292634Sngie		 * so that a process-level multicast routing demon can
430292634Sngie		 * determine which subnet it arrived from.  This is necessary
431292634Sngie		 * to compensate for the lack of any way for a process to
432292634Sngie		 * determine the arrival interface of an incoming packet.
433292634Sngie		 */
434292634Sngie		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
435292634Sngie		  IFP_TO_IA(ifp, ia);
436292634Sngie		  if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
437292634Sngie		}
438292634Sngie
439292634Sngie		/*
440292634Sngie		 * If we belong to the group being reported, stop
441292634Sngie		 * our timer for that group.
442292634Sngie		 */
443292637Sngie		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
444292634Sngie		if (inm != NULL) {
445292634Sngie		  inm->inm_timer = 0;
446292634Sngie		  ++igmpstat.igps_rcv_ourreports;
447292634Sngie
448292634Sngie		  switch(inm->inm_state){
449292634Sngie		  case IGMP_DELAYING_MEMBER:
450292634Sngie		  case IGMP_IDLE_MEMBER:
451292634Sngie		    inm->inm_state = IGMP_LAZY_MEMBER;
452292634Sngie		    break;
453292634Sngie		  case IGMP_AWAKENING_MEMBER:
454292634Sngie		    inm->inm_state = IGMP_LAZY_MEMBER;
455292634Sngie		    break;
456292634Sngie		  case IGMP_LAZY_MEMBER:
457292634Sngie		  case IGMP_SLEEPING_MEMBER:
458292634Sngie		    break;
459292634Sngie		  }
460292634Sngie		}
461292634Sngie	}
462292634Sngie
463292634Sngie	/*
464292634Sngie	 * Pass all valid IGMP packets up to any process(es) listening
465292634Sngie	 * on a raw IGMP socket.
466292634Sngie	 */
467292634Sngie	rip_input(m);
468292634Sngie}
469292634Sngie
470292634Sngievoid
471292634Sngieigmp_joingroup(inm)
472292634Sngie	struct in_multi *inm;
473292634Sngie{
474292634Sngie	register int s = splnet();
475292634Sngie
476292634Sngie	inm->inm_state = IGMP_IDLE_MEMBER;
477292634Sngie
478292634Sngie	if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
479292634Sngie	    inm->inm_ifp == &loif)
480292634Sngie		inm->inm_timer = 0;
481292634Sngie	else {
482292634Sngie		igmp_sendpkt(inm,fill_rti(inm));
483292634Sngie		inm->inm_timer = IGMP_RANDOM_DELAY(
484292634Sngie					IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ);
485292634Sngie		inm->inm_state = IGMP_DELAYING_MEMBER;
486292634Sngie		igmp_timers_are_running = 1;
487292634Sngie	}
488292634Sngie	splx(s);
489292634Sngie}
490292634Sngie
491292634Sngievoid
492292634Sngieigmp_leavegroup(inm)
493292634Sngie	struct in_multi *inm;
494292634Sngie{
495292634Sngie	/*
496292634Sngie	 * No action required on leaving a group.
497292634Sngie	 */
498292634Sngie         switch(inm->inm_state){
499292634Sngie	 case IGMP_DELAYING_MEMBER:
500292634Sngie	 case IGMP_IDLE_MEMBER:
501292634Sngie	   if (!(inm->inm_addr.s_addr == igmp_all_hosts_group ||
502292634Sngie	       inm->inm_ifp == &loif))
503292634Sngie	       if (inm->inm_rti->type != IGMP_OLD_ROUTER)
504292634Sngie		   igmp_sendleave(inm);
505292634Sngie	   break;
506292634Sngie	 case IGMP_LAZY_MEMBER:
507292634Sngie	 case IGMP_AWAKENING_MEMBER:
508292634Sngie	 case IGMP_SLEEPING_MEMBER:
509292634Sngie	   break;
510292634Sngie	 }
511292634Sngie}
512292634Sngie
513292634Sngievoid
514292634Sngieigmp_fasttimo()
515292634Sngie{
516292634Sngie	register struct in_multi *inm;
517292634Sngie	register int s;
518292634Sngie	struct in_multistep step;
519292634Sngie
520292634Sngie	/*
521292634Sngie	 * Quick check to see if any work needs to be done, in order
522292634Sngie	 * to minimize the overhead of fasttimo processing.
523292634Sngie	 */
524292634Sngie	if (!igmp_timers_are_running)
525292634Sngie		return;
526292634Sngie
527292634Sngie	s = splnet();
528292634Sngie	igmp_timers_are_running = 0;
529292634Sngie	IN_FIRST_MULTI(step, inm);
530292634Sngie	while (inm != NULL) {
531292634Sngie		if (inm->inm_timer == 0) {
532292634Sngie			/* do nothing */
533292634Sngie		} else if (--inm->inm_timer == 0) {
534292634Sngie		  if (inm->inm_state == IGMP_DELAYING_MEMBER) {
535292634Sngie		    if (inm->inm_rti->type == IGMP_OLD_ROUTER)
536292634Sngie			igmp_sendpkt(inm, IGMP_HOST_MEMBERSHIP_REPORT);
537292634Sngie		    else
538292634Sngie			igmp_sendpkt(inm, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
539279430Srstone		    inm->inm_state = IGMP_IDLE_MEMBER;
540279430Srstone		  }
541279430Srstone		} else {
542279430Srstone			igmp_timers_are_running = 1;
543279430Srstone		}
544279430Srstone		IN_NEXT_MULTI(step, inm);
545279430Srstone	}
546279430Srstone	splx(s);
547279430Srstone}
548279430Srstone
549279430Srstonevoid
550279430Srstoneigmp_slowtimo()
551292634Sngie{
552292634Sngie	int s = splnet();
553292634Sngie	register struct router_info *rti =  Head;
554292634Sngie
555292634Sngie#ifdef IGMP_DEBUG
556292634Sngie	printf("[igmp.c,_slowtimo] -- > entering \n");
557292634Sngie#endif
558292634Sngie	while (rti) {
559292634Sngie		rti->time ++;
560292634Sngie		if (rti->time >= IGMP_AGE_THRESHOLD){
561292634Sngie			rti->type = IGMP_NEW_ROUTER;
562292634Sngie			rti->time = IGMP_AGE_THRESHOLD;
563292634Sngie		}
564292634Sngie		rti = rti->next;
565292634Sngie	}
566292634Sngie#ifdef IGMP_DEBUG
567279430Srstone	printf("[igmp.c,_slowtimo] -- > exiting \n");
568#endif
569	splx(s);
570}
571
572static void
573igmp_sendpkt(inm, type)
574	struct in_multi *inm;
575	int type;
576{
577        struct mbuf *m;
578        struct igmp *igmp;
579        struct ip *ip;
580        struct ip_moptions *imo;
581
582        MGETHDR(m, M_DONTWAIT, MT_HEADER);
583        if (m == NULL)
584                return;
585
586	MALLOC(imo, struct ip_moptions *, sizeof *imo, M_IPMOPTS, M_DONTWAIT);
587	if (!imo) {
588		m_free(m);
589		return;
590	}
591
592	m->m_pkthdr.rcvif = &loif;
593	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
594	MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip));
595	m->m_data += sizeof(struct ip);
596        m->m_len = IGMP_MINLEN;
597        igmp = mtod(m, struct igmp *);
598        igmp->igmp_type   = type;
599        igmp->igmp_code   = 0;
600        igmp->igmp_group  = inm->inm_addr;
601        igmp->igmp_cksum  = 0;
602        igmp->igmp_cksum  = in_cksum(m, IGMP_MINLEN);
603
604        m->m_data -= sizeof(struct ip);
605        m->m_len += sizeof(struct ip);
606        ip = mtod(m, struct ip *);
607        ip->ip_tos        = 0;
608        ip->ip_len        = sizeof(struct ip) + IGMP_MINLEN;
609        ip->ip_off        = 0;
610        ip->ip_p          = IPPROTO_IGMP;
611        ip->ip_src.s_addr = INADDR_ANY;
612        ip->ip_dst        = igmp->igmp_group;
613
614        imo->imo_multicast_ifp  = inm->inm_ifp;
615        imo->imo_multicast_ttl  = 1;
616        /*
617         * Request loopback of the report if we are acting as a multicast
618         * router, so that the process-level routing demon can hear it.
619         */
620        imo->imo_multicast_loop = (ip_mrouter != NULL);
621
622        ip_output(m, (struct mbuf *)0, (struct route *)0, 0, imo);
623
624	FREE(imo, M_IPMOPTS);
625        ++igmpstat.igps_snd_reports;
626
627}
628
629static void
630igmp_sendleave(inm)
631	struct in_multi *inm;
632{
633	igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
634}
635
636int
637igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
638	    void *newp, size_t newlen)
639{
640	/* All sysctl names at this level are terminal. */
641	if (namelen != 1)
642		return ENOTDIR;	/* XXX overloaded */
643
644	switch(name[0]) {
645	case IGMPCTL_STATS:
646		return sysctl_rdstruct(oldp, oldlenp, newp, &igmpstat,
647				       sizeof igmpstat);
648	default:
649		return ENOPROTOOPT;
650	}
651}
652