igmp.c revision 14622
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1988 Stephen Deering.
31541Srgrimes * Copyright (c) 1992, 1993
41541Srgrimes *	The Regents of the University of California.  All rights reserved.
51541Srgrimes *
61541Srgrimes * This code is derived from software contributed to Berkeley by
71541Srgrimes * Stephen Deering of Stanford University.
81541Srgrimes *
91541Srgrimes * Redistribution and use in source and binary forms, with or without
101541Srgrimes * modification, are permitted provided that the following conditions
111541Srgrimes * are met:
121541Srgrimes * 1. Redistributions of source code must retain the above copyright
131541Srgrimes *    notice, this list of conditions and the following disclaimer.
141541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151541Srgrimes *    notice, this list of conditions and the following disclaimer in the
161541Srgrimes *    documentation and/or other materials provided with the distribution.
171541Srgrimes * 3. All advertising materials mentioning features or use of this software
181541Srgrimes *    must display the following acknowledgement:
191541Srgrimes *	This product includes software developed by the University of
201541Srgrimes *	California, Berkeley and its contributors.
211541Srgrimes * 4. Neither the name of the University nor the names of its contributors
221541Srgrimes *    may be used to endorse or promote products derived from this software
231541Srgrimes *    without specific prior written permission.
241541Srgrimes *
251541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
261541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
271541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
281541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
291541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
301541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
311541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
321541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
331541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
341541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
351541Srgrimes * SUCH DAMAGE.
361541Srgrimes *
371541Srgrimes *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
3814622Sfenner * $Id: igmp.c,v 1.15 1995/12/09 20:43:51 phk Exp $
391541Srgrimes */
401541Srgrimes
412531Swollman/*
422531Swollman * Internet Group Management Protocol (IGMP) routines.
432531Swollman *
442531Swollman * Written by Steve Deering, Stanford, May 1988.
452531Swollman * Modified by Rosen Sharma, Stanford, Aug 1994.
469209Swollman * Modified by Bill Fenner, Xerox PARC, Feb 1995.
4714622Sfenner * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995.
482531Swollman *
4914622Sfenner * MULTICAST Revision: 3.5.1.4
502531Swollman */
511541Srgrimes
521541Srgrimes#include <sys/param.h>
531549Srgrimes#include <sys/systm.h>
541541Srgrimes#include <sys/mbuf.h>
551541Srgrimes#include <sys/socket.h>
561541Srgrimes#include <sys/protosw.h>
5712296Sphk#include <sys/kernel.h>
586472Swollman#include <sys/sysctl.h>
591541Srgrimes
601541Srgrimes#include <net/if.h>
611541Srgrimes#include <net/route.h>
621541Srgrimes
631541Srgrimes#include <netinet/in.h>
641541Srgrimes#include <netinet/in_var.h>
651541Srgrimes#include <netinet/in_systm.h>
661541Srgrimes#include <netinet/ip.h>
671541Srgrimes#include <netinet/ip_var.h>
681541Srgrimes#include <netinet/igmp.h>
691541Srgrimes#include <netinet/igmp_var.h>
701541Srgrimes
7112704Sphkstatic int	fill_rti __P((struct in_multi *inm));
7212704Sphkstatic struct router_info *
7312579Sbde		find_rti __P((struct ifnet *ifp));
7412579Sbde
7512704Sphkstatic struct igmpstat igmpstat;
762531Swollman
7712296SphkSYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD,
7812296Sphk	&igmpstat, igmpstat, "");
7912296Sphk
809209Swollmanstatic int igmp_timers_are_running;
811541Srgrimesstatic u_long igmp_all_hosts_group;
8214622Sfennerstatic u_long igmp_all_rtrs_group;
8314622Sfennerstatic struct mbuf *router_alert;
849209Swollmanstatic struct router_info *Head;
851541Srgrimes
8614622Sfennerstatic void igmp_sendpkt(struct in_multi *, int, unsigned long);
871541Srgrimes
881541Srgrimesvoid
891541Srgrimesigmp_init()
901541Srgrimes{
9114622Sfenner	struct ipoption *ra;
9214622Sfenner
931541Srgrimes	/*
941541Srgrimes	 * To avoid byte-swapping the same value over and over again.
951541Srgrimes	 */
961541Srgrimes	igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
9714622Sfenner	igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP);
989209Swollman
999209Swollman	igmp_timers_are_running = 0;
1009209Swollman
10114622Sfenner	/*
10214622Sfenner	 * Construct a Router Alert option to use in outgoing packets
10314622Sfenner	 */
10414622Sfenner	MGET(router_alert, M_DONTWAIT, MT_DATA);
10514622Sfenner	ra = mtod(router_alert, struct ipoption *);
10614622Sfenner	ra->ipopt_dst.s_addr = 0;
10714622Sfenner	ra->ipopt_list[0] = IPOPT_RA;	/* Router Alert Option */
10814622Sfenner	ra->ipopt_list[1] = 0x04;	/* 4 bytes long */
10914622Sfenner	ra->ipopt_list[2] = 0x00;
11014622Sfenner	ra->ipopt_list[3] = 0x00;
11114622Sfenner	router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
11214622Sfenner
1132531Swollman	Head = (struct router_info *) 0;
1141541Srgrimes}
1151541Srgrimes
11612704Sphkstatic struct router_info *
1172531Swollmanfind_rti(ifp)
1182531Swollman	struct ifnet *ifp;
1192531Swollman{
1202531Swollman        register struct router_info *rti = Head;
1212531Swollman
1222531Swollman#ifdef IGMP_DEBUG
1232531Swollman	printf("[igmp.c, _find_rti] --> entering \n");
1242531Swollman#endif
1252531Swollman        while (rti) {
12614622Sfenner                if (rti->rti_ifp == ifp) {
1272531Swollman#ifdef IGMP_DEBUG
1282531Swollman			printf("[igmp.c, _find_rti] --> found old entry \n");
1292531Swollman#endif
1302531Swollman                        return rti;
1312531Swollman                }
13214622Sfenner                rti = rti->rti_next;
1332531Swollman        }
1342531Swollman	MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT);
13514622Sfenner        rti->rti_ifp = ifp;
13614622Sfenner        rti->rti_type = IGMP_V2_ROUTER;
13714622Sfenner        rti->rti_time = 0;
13814622Sfenner        rti->rti_next = Head;
1392531Swollman        Head = rti;
1402531Swollman#ifdef IGMP_DEBUG
1412531Swollman	printf("[igmp.c, _find_rti] --> created an entry \n");
1422531Swollman#endif
1432531Swollman        return rti;
1442531Swollman}
1452531Swollman
1461541Srgrimesvoid
1471541Srgrimesigmp_input(m, iphlen)
1481541Srgrimes	register struct mbuf *m;
1491541Srgrimes	register int iphlen;
1501541Srgrimes{
1511541Srgrimes	register struct igmp *igmp;
1521541Srgrimes	register struct ip *ip;
1531541Srgrimes	register int igmplen;
1541541Srgrimes	register struct ifnet *ifp = m->m_pkthdr.rcvif;
1551541Srgrimes	register int minlen;
1561541Srgrimes	register struct in_multi *inm;
1571541Srgrimes	register struct in_ifaddr *ia;
1581541Srgrimes	struct in_multistep step;
1592531Swollman	struct router_info *rti;
1609209Swollman
1618546Sdg	int timer; /** timer value in the igmp query header **/
1621541Srgrimes
1631541Srgrimes	++igmpstat.igps_rcv_total;
1641541Srgrimes
1651541Srgrimes	ip = mtod(m, struct ip *);
1661541Srgrimes	igmplen = ip->ip_len;
1671541Srgrimes
1681541Srgrimes	/*
1691541Srgrimes	 * Validate lengths
1701541Srgrimes	 */
1711541Srgrimes	if (igmplen < IGMP_MINLEN) {
1721541Srgrimes		++igmpstat.igps_rcv_tooshort;
1731541Srgrimes		m_freem(m);
1741541Srgrimes		return;
1751541Srgrimes	}
1761541Srgrimes	minlen = iphlen + IGMP_MINLEN;
1771541Srgrimes	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
1781541Srgrimes	    (m = m_pullup(m, minlen)) == 0) {
1791541Srgrimes		++igmpstat.igps_rcv_tooshort;
1801541Srgrimes		return;
1811541Srgrimes	}
1821541Srgrimes
1831541Srgrimes	/*
1841541Srgrimes	 * Validate checksum
1851541Srgrimes	 */
1861541Srgrimes	m->m_data += iphlen;
1871541Srgrimes	m->m_len -= iphlen;
1881541Srgrimes	igmp = mtod(m, struct igmp *);
1891541Srgrimes	if (in_cksum(m, igmplen)) {
1901541Srgrimes		++igmpstat.igps_rcv_badsum;
1911541Srgrimes		m_freem(m);
1921541Srgrimes		return;
1931541Srgrimes	}
1941541Srgrimes	m->m_data -= iphlen;
1951541Srgrimes	m->m_len += iphlen;
1962531Swollman
1971541Srgrimes	ip = mtod(m, struct ip *);
1988546Sdg	timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
1992531Swollman	rti = find_rti(ifp);
2001541Srgrimes
20114622Sfenner	/*
20214622Sfenner	 * In the IGMPv2 specification, there are 3 states and a flag.
20314622Sfenner	 *
20414622Sfenner	 * In Non-Member state, we simply don't have a membership record.
20514622Sfenner	 * In Delaying Member state, our timer is running (inm->inm_timer)
20614622Sfenner	 * In Idle Member state, our timer is not running (inm->inm_timer==0)
20714622Sfenner	 *
20814622Sfenner	 * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if
20914622Sfenner	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
21014622Sfenner	 * if I sent the last report.
21114622Sfenner	 */
2121541Srgrimes	switch (igmp->igmp_type) {
2131541Srgrimes
21414622Sfenner	case IGMP_MEMBERSHIP_QUERY:
2151541Srgrimes		++igmpstat.igps_rcv_queries;
2161541Srgrimes
2178090Spst		if (ifp->if_flags & IFF_LOOPBACK)
2181541Srgrimes			break;
2191541Srgrimes
2202531Swollman		if (igmp->igmp_code == 0) {
22114622Sfenner			/*
22214622Sfenner			 * Old router.  Remember that the querier on this
22314622Sfenner			 * interface is old, and set the timer to the
22414622Sfenner			 * value in RFC 1112.
22514622Sfenner			 */
2264028Spst
22714622Sfenner			rti->rti_type = IGMP_V1_ROUTER;
22814622Sfenner			rti->rti_time = 0;
2294028Spst
23014622Sfenner			timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ;
2314028Spst
23214622Sfenner			if (ip->ip_dst.s_addr != igmp_all_hosts_group ||
23314622Sfenner			    igmp->igmp_group.s_addr != 0) {
2342531Swollman				++igmpstat.igps_rcv_badqueries;
2352531Swollman				m_freem(m);
2362531Swollman				return;
2372531Swollman			}
23814622Sfenner		} else {
2392531Swollman			/*
24014622Sfenner			 * New router.  Simply do the new validity check.
2412531Swollman			 */
24214622Sfenner
24314622Sfenner			if (igmp->igmp_group.s_addr != 0 &&
24414622Sfenner			    !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
24514622Sfenner				++igmpstat.igps_rcv_badqueries;
24614622Sfenner				m_freem(m);
24714622Sfenner				return;
24814622Sfenner			}
24914622Sfenner		}
2502531Swollman
25114622Sfenner		/*
25214622Sfenner		 * - Start the timers in all of our membership records
25314622Sfenner		 *   that the query applies to for the interface on
25414622Sfenner		 *   which the query arrived excl. those that belong
25514622Sfenner		 *   to the "all-hosts" group (224.0.0.1).
25614622Sfenner		 * - Restart any timer that is already running but has
25714622Sfenner		 *   a value longer than the requested timeout.
25814622Sfenner		 * - Use the value specified in the query message as
25914622Sfenner		 *   the maximum timeout.
26014622Sfenner		 */
26114622Sfenner		IN_FIRST_MULTI(step, inm);
26214622Sfenner		while (inm != NULL) {
26314622Sfenner			if (inm->inm_ifp == ifp &&
26414622Sfenner			    inm->inm_addr.s_addr != igmp_all_hosts_group &&
26514622Sfenner			    (igmp->igmp_group.s_addr == 0 ||
26614622Sfenner			     igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) {
26714622Sfenner				if (inm->inm_timer == 0 ||
26814622Sfenner				    inm->inm_timer > timer) {
26914622Sfenner					inm->inm_timer =
27014622Sfenner						IGMP_RANDOM_DELAY(timer);
2712531Swollman					igmp_timers_are_running = 1;
2722531Swollman				}
2731541Srgrimes			}
2741541Srgrimes			IN_NEXT_MULTI(step, inm);
2751541Srgrimes		}
2769209Swollman
2771541Srgrimes		break;
2781541Srgrimes
27914622Sfenner	case IGMP_V1_MEMBERSHIP_REPORT:
28014622Sfenner	case IGMP_V2_MEMBERSHIP_REPORT:
2819209Swollman		/*
28214622Sfenner		 * For fast leave to work, we have to know that we are the
28314622Sfenner		 * last person to send a report for this group.  Reports
28414622Sfenner		 * can potentially get looped back if we are a multicast
28514622Sfenner		 * router, so discard reports sourced by me.
2869209Swollman		 */
28714622Sfenner		IFP_TO_IA(ifp, ia);
28814622Sfenner		if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr)
28914622Sfenner			break;
29014622Sfenner
2911541Srgrimes		++igmpstat.igps_rcv_reports;
2921541Srgrimes
2938090Spst		if (ifp->if_flags & IFF_LOOPBACK)
2941541Srgrimes			break;
2951541Srgrimes
29614622Sfenner		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
2971541Srgrimes			++igmpstat.igps_rcv_badreports;
2981541Srgrimes			m_freem(m);
2991541Srgrimes			return;
3001541Srgrimes		}
3011541Srgrimes
3021541Srgrimes		/*
3031541Srgrimes		 * KLUDGE: if the IP source address of the report has an
3041541Srgrimes		 * unspecified (i.e., zero) subnet number, as is allowed for
3051541Srgrimes		 * a booting host, replace it with the correct subnet number
3061541Srgrimes		 * so that a process-level multicast routing demon can
3071541Srgrimes		 * determine which subnet it arrived from.  This is necessary
3081541Srgrimes		 * to compensate for the lack of any way for a process to
3091541Srgrimes		 * determine the arrival interface of an incoming packet.
3101541Srgrimes		 */
31114622Sfenner		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0)
3121541Srgrimes			if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
3131541Srgrimes
3141541Srgrimes		/*
3151541Srgrimes		 * If we belong to the group being reported, stop
3161541Srgrimes		 * our timer for that group.
3171541Srgrimes		 */
3181541Srgrimes		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
3191541Srgrimes
3202531Swollman		if (inm != NULL) {
32114622Sfenner			inm->inm_timer = 0;
32214622Sfenner			++igmpstat.igps_rcv_ourreports;
32314622Sfenner
32414622Sfenner			inm->inm_state = IGMP_OTHERMEMBER;
3252531Swollman		}
32614622Sfenner
3271541Srgrimes		break;
3281541Srgrimes	}
3291541Srgrimes
3301541Srgrimes	/*
3311541Srgrimes	 * Pass all valid IGMP packets up to any process(es) listening
3321541Srgrimes	 * on a raw IGMP socket.
3331541Srgrimes	 */
3341541Srgrimes	rip_input(m);
3351541Srgrimes}
3361541Srgrimes
3371541Srgrimesvoid
3381541Srgrimesigmp_joingroup(inm)
3391541Srgrimes	struct in_multi *inm;
3401541Srgrimes{
3419209Swollman	int s = splnet();
3421541Srgrimes
34314622Sfenner	if (inm->inm_addr.s_addr == igmp_all_hosts_group
34414622Sfenner	    || inm->inm_ifp->if_flags & IFF_LOOPBACK) {
3451541Srgrimes		inm->inm_timer = 0;
34614622Sfenner		inm->inm_state = IGMP_OTHERMEMBER;
34714622Sfenner	} else {
34814622Sfenner		inm->inm_rti = find_rti(inm->inm_ifp);
34914622Sfenner		igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
3502531Swollman		inm->inm_timer = IGMP_RANDOM_DELAY(
3512531Swollman					IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ);
35214622Sfenner		inm->inm_state = IGMP_IREPORTEDLAST;
3531541Srgrimes		igmp_timers_are_running = 1;
3541541Srgrimes	}
3551541Srgrimes	splx(s);
3561541Srgrimes}
3571541Srgrimes
3581541Srgrimesvoid
3591541Srgrimesigmp_leavegroup(inm)
3601541Srgrimes	struct in_multi *inm;
3611541Srgrimes{
36214622Sfenner	if (inm->inm_state == IGMP_IREPORTEDLAST &&
36314622Sfenner	    inm->inm_addr.s_addr != igmp_all_hosts_group &&
36414622Sfenner	    !(inm->inm_ifp->if_flags & IFF_LOOPBACK) &&
36514622Sfenner	    inm->inm_rti->rti_type != IGMP_V1_ROUTER)
36614622Sfenner		igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group);
3671541Srgrimes}
3681541Srgrimes
3691541Srgrimesvoid
3701541Srgrimesigmp_fasttimo()
3711541Srgrimes{
3721541Srgrimes	register struct in_multi *inm;
3731541Srgrimes	struct in_multistep step;
3749209Swollman	int s;
3751541Srgrimes
3761541Srgrimes	/*
3771541Srgrimes	 * Quick check to see if any work needs to be done, in order
3781541Srgrimes	 * to minimize the overhead of fasttimo processing.
3791541Srgrimes	 */
3809209Swollman
3811541Srgrimes	if (!igmp_timers_are_running)
3821541Srgrimes		return;
3831541Srgrimes
3841541Srgrimes	s = splnet();
3851541Srgrimes	igmp_timers_are_running = 0;
3861541Srgrimes	IN_FIRST_MULTI(step, inm);
3871541Srgrimes	while (inm != NULL) {
3881541Srgrimes		if (inm->inm_timer == 0) {
3891541Srgrimes			/* do nothing */
3901541Srgrimes		} else if (--inm->inm_timer == 0) {
39114622Sfenner			igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
39214622Sfenner			inm->inm_state = IGMP_IREPORTEDLAST;
3931541Srgrimes		} else {
3941541Srgrimes			igmp_timers_are_running = 1;
3951541Srgrimes		}
3961541Srgrimes		IN_NEXT_MULTI(step, inm);
3971541Srgrimes	}
3981541Srgrimes	splx(s);
3991541Srgrimes}
4001541Srgrimes
4012531Swollmanvoid
4022531Swollmanigmp_slowtimo()
4032531Swollman{
4042531Swollman	int s = splnet();
4052531Swollman	register struct router_info *rti =  Head;
4062531Swollman
4072531Swollman#ifdef IGMP_DEBUG
4082531Swollman	printf("[igmp.c,_slowtimo] -- > entering \n");
4092531Swollman#endif
4102531Swollman	while (rti) {
41114622Sfenner	    if (rti->rti_type == IGMP_V1_ROUTER) {
41214622Sfenner		rti->rti_time++;
41314622Sfenner		if (rti->rti_time >= IGMP_AGE_THRESHOLD) {
41414622Sfenner			rti->rti_type = IGMP_V2_ROUTER;
4152531Swollman		}
41614622Sfenner	    }
41714622Sfenner	    rti = rti->rti_next;
4182531Swollman	}
4199209Swollman#ifdef IGMP_DEBUG
4202531Swollman	printf("[igmp.c,_slowtimo] -- > exiting \n");
4212531Swollman#endif
4222531Swollman	splx(s);
4232531Swollman}
4242531Swollman
4251541Srgrimesstatic void
42614622Sfennerigmp_sendpkt(inm, type, addr)
4272531Swollman	struct in_multi *inm;
4282531Swollman	int type;
42914622Sfenner	unsigned long addr;
4301541Srgrimes{
4312531Swollman        struct mbuf *m;
4322531Swollman        struct igmp *igmp;
4332531Swollman        struct ip *ip;
4342531Swollman        struct ip_moptions *imo;
4351541Srgrimes
4362531Swollman        MGETHDR(m, M_DONTWAIT, MT_HEADER);
4372531Swollman        if (m == NULL)
4382531Swollman                return;
4392531Swollman
4402531Swollman	MALLOC(imo, struct ip_moptions *, sizeof *imo, M_IPMOPTS, M_DONTWAIT);
4412531Swollman	if (!imo) {
4422531Swollman		m_free(m);
4431541Srgrimes		return;
4442531Swollman	}
4452531Swollman
4468090Spst	m->m_pkthdr.rcvif = loif;
4471541Srgrimes	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
4482531Swollman	MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip));
4492531Swollman	m->m_data += sizeof(struct ip);
4502531Swollman        m->m_len = IGMP_MINLEN;
4512531Swollman        igmp = mtod(m, struct igmp *);
4522531Swollman        igmp->igmp_type   = type;
4532531Swollman        igmp->igmp_code   = 0;
4542531Swollman        igmp->igmp_group  = inm->inm_addr;
4552531Swollman        igmp->igmp_cksum  = 0;
4562531Swollman        igmp->igmp_cksum  = in_cksum(m, IGMP_MINLEN);
4571541Srgrimes
4582531Swollman        m->m_data -= sizeof(struct ip);
4592531Swollman        m->m_len += sizeof(struct ip);
4602531Swollman        ip = mtod(m, struct ip *);
4612531Swollman        ip->ip_tos        = 0;
4622531Swollman        ip->ip_len        = sizeof(struct ip) + IGMP_MINLEN;
4632531Swollman        ip->ip_off        = 0;
4642531Swollman        ip->ip_p          = IPPROTO_IGMP;
4652531Swollman        ip->ip_src.s_addr = INADDR_ANY;
46614622Sfenner        ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr;
4671541Srgrimes
4682531Swollman        imo->imo_multicast_ifp  = inm->inm_ifp;
4692531Swollman        imo->imo_multicast_ttl  = 1;
4709209Swollman	imo->imo_multicast_vif  = -1;
4712531Swollman        /*
4722531Swollman         * Request loopback of the report if we are acting as a multicast
4732531Swollman         * router, so that the process-level routing demon can hear it.
4742531Swollman         */
4752531Swollman        imo->imo_multicast_loop = (ip_mrouter != NULL);
4761541Srgrimes
47714622Sfenner        ip_output(m, router_alert, (struct route *)0, 0, imo);
4782531Swollman
4792531Swollman	FREE(imo, M_IPMOPTS);
4802531Swollman        ++igmpstat.igps_snd_reports;
4811541Srgrimes}
482