igmp.c revision 119180
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
3850477Speter * $FreeBSD: head/sys/netinet/igmp.c 119180 2003-08-20 17:09:01Z rwatson $
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
52101091Srwatson#include "opt_mac.h"
53101091Srwatson
541541Srgrimes#include <sys/param.h>
551549Srgrimes#include <sys/systm.h>
56101091Srwatson#include <sys/mac.h>
5729024Sbde#include <sys/malloc.h>
581541Srgrimes#include <sys/mbuf.h>
591541Srgrimes#include <sys/socket.h>
601541Srgrimes#include <sys/protosw.h>
6112296Sphk#include <sys/kernel.h>
626472Swollman#include <sys/sysctl.h>
631541Srgrimes
641541Srgrimes#include <net/if.h>
651541Srgrimes#include <net/route.h>
661541Srgrimes
671541Srgrimes#include <netinet/in.h>
681541Srgrimes#include <netinet/in_var.h>
691541Srgrimes#include <netinet/in_systm.h>
701541Srgrimes#include <netinet/ip.h>
711541Srgrimes#include <netinet/ip_var.h>
721541Srgrimes#include <netinet/igmp.h>
731541Srgrimes#include <netinet/igmp_var.h>
741541Srgrimes
7560105Sjlemon#include <machine/in_cksum.h>
7660105Sjlemon
7742776Sfennerstatic MALLOC_DEFINE(M_IGMP, "igmp", "igmp state");
7830309Sphk
79107113Sluigistatic struct router_info *
80107113Sluigi		find_rti(struct ifnet *ifp);
8112579Sbde
8212704Sphkstatic struct igmpstat igmpstat;
832531Swollman
8478667SruSYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RW,
8512296Sphk	&igmpstat, igmpstat, "");
8612296Sphk
879209Swollmanstatic int igmp_timers_are_running;
881541Srgrimesstatic u_long igmp_all_hosts_group;
8914622Sfennerstatic u_long igmp_all_rtrs_group;
9014622Sfennerstatic struct mbuf *router_alert;
91119180Srwatsonstatic SLIST_HEAD(, router_info) router_info_head;
921541Srgrimes
9392723Salfredstatic void igmp_sendpkt(struct in_multi *, int, unsigned long);
941541Srgrimes
95119180Srwatson#ifdef IGMP_DEBUG
96119180Srwatson#define	IGMP_PRINTF(x)	printf(x)
97119180Srwatson#else
98119180Srwatson#define	IGMP_PRINTF(x)
99119180Srwatson#endif
100119180Srwatson
1011541Srgrimesvoid
102107113Sluigiigmp_init()
1031541Srgrimes{
10414622Sfenner	struct ipoption *ra;
10514622Sfenner
1061541Srgrimes	/*
1071541Srgrimes	 * To avoid byte-swapping the same value over and over again.
1081541Srgrimes	 */
1091541Srgrimes	igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
11014622Sfenner	igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP);
1119209Swollman
1129209Swollman	igmp_timers_are_running = 0;
1139209Swollman
11414622Sfenner	/*
11514622Sfenner	 * Construct a Router Alert option to use in outgoing packets
11614622Sfenner	 */
117111119Simp	MGET(router_alert, M_DONTWAIT, MT_DATA);
11814622Sfenner	ra = mtod(router_alert, struct ipoption *);
11914622Sfenner	ra->ipopt_dst.s_addr = 0;
12014622Sfenner	ra->ipopt_list[0] = IPOPT_RA;	/* Router Alert Option */
12114622Sfenner	ra->ipopt_list[1] = 0x04;	/* 4 bytes long */
12214622Sfenner	ra->ipopt_list[2] = 0x00;
12314622Sfenner	ra->ipopt_list[3] = 0x00;
12414622Sfenner	router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
12514622Sfenner
126119180Srwatson	SLIST_INIT(&router_info_head);
1271541Srgrimes}
1281541Srgrimes
12912704Sphkstatic struct router_info *
130107113Sluigifind_rti(ifp)
131107113Sluigi	struct ifnet *ifp;
1322531Swollman{
133119180Srwatson	struct router_info *rti;
1342531Swollman
135119180Srwatson	rti = SLIST_FIRST(&router_info_head);
136119180Srwatson	IGMP_PRINTF("[igmp.c, _find_rti] --> entering \n");
137119180Srwatson	SLIST_FOREACH(rti, &router_info_head, rti_list) {
138119180Srwatson		if (rti->rti_ifp == ifp) {
139119180Srwatson			IGMP_PRINTF(
140119180Srwatson			    "[igmp.c, _find_rti] --> found old entry \n");
1412531Swollman                        return rti;
1422531Swollman                }
1432531Swollman        }
14442776Sfenner	MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, M_NOWAIT);
14514622Sfenner        rti->rti_ifp = ifp;
14614622Sfenner        rti->rti_type = IGMP_V2_ROUTER;
14714622Sfenner        rti->rti_time = 0;
148119180Srwatson	SLIST_INSERT_HEAD(&router_info_head, rti, rti_list);
149119180Srwatson
150119180Srwatson	IGMP_PRINTF("[igmp.c, _find_rti] --> created an entry \n");
1512531Swollman        return rti;
1522531Swollman}
1532531Swollman
1541541Srgrimesvoid
155107113Sluigiigmp_input(m, off)
156107113Sluigi	register struct mbuf *m;
157107113Sluigi	int off;
1581541Srgrimes{
159107113Sluigi	register int iphlen = off;
160107113Sluigi	register struct igmp *igmp;
161107113Sluigi	register struct ip *ip;
162107113Sluigi	register int igmplen;
163107113Sluigi	register struct ifnet *ifp = m->m_pkthdr.rcvif;
164107113Sluigi	register int minlen;
165107113Sluigi	register struct in_multi *inm;
166107113Sluigi	register struct in_ifaddr *ia;
1671541Srgrimes	struct in_multistep step;
1682531Swollman	struct router_info *rti;
1699209Swollman
1708546Sdg	int timer; /** timer value in the igmp query header **/
1711541Srgrimes
1721541Srgrimes	++igmpstat.igps_rcv_total;
1731541Srgrimes
1741541Srgrimes	ip = mtod(m, struct ip *);
1751541Srgrimes	igmplen = ip->ip_len;
1761541Srgrimes
1771541Srgrimes	/*
1781541Srgrimes	 * Validate lengths
1791541Srgrimes	 */
1801541Srgrimes	if (igmplen < IGMP_MINLEN) {
1811541Srgrimes		++igmpstat.igps_rcv_tooshort;
1821541Srgrimes		m_freem(m);
1831541Srgrimes		return;
1841541Srgrimes	}
1851541Srgrimes	minlen = iphlen + IGMP_MINLEN;
1861541Srgrimes	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
1871541Srgrimes	    (m = m_pullup(m, minlen)) == 0) {
1881541Srgrimes		++igmpstat.igps_rcv_tooshort;
1891541Srgrimes		return;
1901541Srgrimes	}
1911541Srgrimes
1921541Srgrimes	/*
1931541Srgrimes	 * Validate checksum
1941541Srgrimes	 */
1951541Srgrimes	m->m_data += iphlen;
1961541Srgrimes	m->m_len -= iphlen;
1971541Srgrimes	igmp = mtod(m, struct igmp *);
1981541Srgrimes	if (in_cksum(m, igmplen)) {
1991541Srgrimes		++igmpstat.igps_rcv_badsum;
2001541Srgrimes		m_freem(m);
2011541Srgrimes		return;
2021541Srgrimes	}
2031541Srgrimes	m->m_data -= iphlen;
2041541Srgrimes	m->m_len += iphlen;
2052531Swollman
2061541Srgrimes	ip = mtod(m, struct ip *);
2078546Sdg	timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
20841702Sdillon	if (timer == 0)
20941702Sdillon		timer = 1;
2102531Swollman	rti = find_rti(ifp);
2111541Srgrimes
21214622Sfenner	/*
21314622Sfenner	 * In the IGMPv2 specification, there are 3 states and a flag.
21414622Sfenner	 *
21514622Sfenner	 * In Non-Member state, we simply don't have a membership record.
21614622Sfenner	 * In Delaying Member state, our timer is running (inm->inm_timer)
21714622Sfenner	 * In Idle Member state, our timer is not running (inm->inm_timer==0)
21814622Sfenner	 *
21914622Sfenner	 * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if
22014622Sfenner	 * we have heard a report from another member, or IGMP_IREPORTEDLAST
22114622Sfenner	 * if I sent the last report.
22214622Sfenner	 */
2231541Srgrimes	switch (igmp->igmp_type) {
2241541Srgrimes
22514622Sfenner	case IGMP_MEMBERSHIP_QUERY:
2261541Srgrimes		++igmpstat.igps_rcv_queries;
2271541Srgrimes
2288090Spst		if (ifp->if_flags & IFF_LOOPBACK)
2291541Srgrimes			break;
2301541Srgrimes
2312531Swollman		if (igmp->igmp_code == 0) {
23214622Sfenner			/*
23314622Sfenner			 * Old router.  Remember that the querier on this
23414622Sfenner			 * interface is old, and set the timer to the
23514622Sfenner			 * value in RFC 1112.
23614622Sfenner			 */
2374028Spst
23814622Sfenner			rti->rti_type = IGMP_V1_ROUTER;
23914622Sfenner			rti->rti_time = 0;
2404028Spst
24114622Sfenner			timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ;
2424028Spst
24314622Sfenner			if (ip->ip_dst.s_addr != igmp_all_hosts_group ||
24414622Sfenner			    igmp->igmp_group.s_addr != 0) {
2452531Swollman				++igmpstat.igps_rcv_badqueries;
2462531Swollman				m_freem(m);
2472531Swollman				return;
2482531Swollman			}
24914622Sfenner		} else {
2502531Swollman			/*
25114622Sfenner			 * New router.  Simply do the new validity check.
2522531Swollman			 */
25314622Sfenner
25414622Sfenner			if (igmp->igmp_group.s_addr != 0 &&
25514622Sfenner			    !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
25614622Sfenner				++igmpstat.igps_rcv_badqueries;
25714622Sfenner				m_freem(m);
25814622Sfenner				return;
25914622Sfenner			}
26014622Sfenner		}
2612531Swollman
26214622Sfenner		/*
26314622Sfenner		 * - Start the timers in all of our membership records
26414622Sfenner		 *   that the query applies to for the interface on
26514622Sfenner		 *   which the query arrived excl. those that belong
26614622Sfenner		 *   to the "all-hosts" group (224.0.0.1).
26714622Sfenner		 * - Restart any timer that is already running but has
26814622Sfenner		 *   a value longer than the requested timeout.
26914622Sfenner		 * - Use the value specified in the query message as
27014622Sfenner		 *   the maximum timeout.
27114622Sfenner		 */
27214622Sfenner		IN_FIRST_MULTI(step, inm);
27314622Sfenner		while (inm != NULL) {
27414622Sfenner			if (inm->inm_ifp == ifp &&
27514622Sfenner			    inm->inm_addr.s_addr != igmp_all_hosts_group &&
27614622Sfenner			    (igmp->igmp_group.s_addr == 0 ||
27714622Sfenner			     igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) {
27814622Sfenner				if (inm->inm_timer == 0 ||
27914622Sfenner				    inm->inm_timer > timer) {
28014622Sfenner					inm->inm_timer =
28114622Sfenner						IGMP_RANDOM_DELAY(timer);
2822531Swollman					igmp_timers_are_running = 1;
2832531Swollman				}
2841541Srgrimes			}
2851541Srgrimes			IN_NEXT_MULTI(step, inm);
2861541Srgrimes		}
2879209Swollman
2881541Srgrimes		break;
2891541Srgrimes
29014622Sfenner	case IGMP_V1_MEMBERSHIP_REPORT:
29114622Sfenner	case IGMP_V2_MEMBERSHIP_REPORT:
2929209Swollman		/*
29314622Sfenner		 * For fast leave to work, we have to know that we are the
29414622Sfenner		 * last person to send a report for this group.  Reports
29514622Sfenner		 * can potentially get looped back if we are a multicast
29614622Sfenner		 * router, so discard reports sourced by me.
2979209Swollman		 */
29814622Sfenner		IFP_TO_IA(ifp, ia);
29914622Sfenner		if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr)
30014622Sfenner			break;
30114622Sfenner
3021541Srgrimes		++igmpstat.igps_rcv_reports;
3031541Srgrimes
3048090Spst		if (ifp->if_flags & IFF_LOOPBACK)
3051541Srgrimes			break;
3061541Srgrimes
30714622Sfenner		if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
3081541Srgrimes			++igmpstat.igps_rcv_badreports;
3091541Srgrimes			m_freem(m);
3101541Srgrimes			return;
3111541Srgrimes		}
3121541Srgrimes
3131541Srgrimes		/*
3141541Srgrimes		 * KLUDGE: if the IP source address of the report has an
3151541Srgrimes		 * unspecified (i.e., zero) subnet number, as is allowed for
3161541Srgrimes		 * a booting host, replace it with the correct subnet number
31796432Sdd		 * so that a process-level multicast routing daemon can
3181541Srgrimes		 * determine which subnet it arrived from.  This is necessary
3191541Srgrimes		 * to compensate for the lack of any way for a process to
3201541Srgrimes		 * determine the arrival interface of an incoming packet.
3211541Srgrimes		 */
32214622Sfenner		if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0)
3231541Srgrimes			if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
3241541Srgrimes
3251541Srgrimes		/*
3261541Srgrimes		 * If we belong to the group being reported, stop
3271541Srgrimes		 * our timer for that group.
3281541Srgrimes		 */
3291541Srgrimes		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
3301541Srgrimes
3312531Swollman		if (inm != NULL) {
33214622Sfenner			inm->inm_timer = 0;
33314622Sfenner			++igmpstat.igps_rcv_ourreports;
33414622Sfenner
33514622Sfenner			inm->inm_state = IGMP_OTHERMEMBER;
3362531Swollman		}
33714622Sfenner
3381541Srgrimes		break;
3391541Srgrimes	}
3401541Srgrimes
3411541Srgrimes	/*
3421541Srgrimes	 * Pass all valid IGMP packets up to any process(es) listening
3431541Srgrimes	 * on a raw IGMP socket.
3441541Srgrimes	 */
34582890Sjulian	rip_input(m, off);
3461541Srgrimes}
3471541Srgrimes
3481541Srgrimesvoid
349107113Sluigiigmp_joingroup(inm)
350107113Sluigi	struct in_multi *inm;
3511541Srgrimes{
3529209Swollman	int s = splnet();
3531541Srgrimes
35414622Sfenner	if (inm->inm_addr.s_addr == igmp_all_hosts_group
35514622Sfenner	    || inm->inm_ifp->if_flags & IFF_LOOPBACK) {
3561541Srgrimes		inm->inm_timer = 0;
35714622Sfenner		inm->inm_state = IGMP_OTHERMEMBER;
35814622Sfenner	} else {
35914622Sfenner		inm->inm_rti = find_rti(inm->inm_ifp);
36014622Sfenner		igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
3612531Swollman		inm->inm_timer = IGMP_RANDOM_DELAY(
3622531Swollman					IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ);
36314622Sfenner		inm->inm_state = IGMP_IREPORTEDLAST;
3641541Srgrimes		igmp_timers_are_running = 1;
3651541Srgrimes	}
3661541Srgrimes	splx(s);
3671541Srgrimes}
3681541Srgrimes
3691541Srgrimesvoid
370107113Sluigiigmp_leavegroup(inm)
371107113Sluigi	struct in_multi *inm;
3721541Srgrimes{
37314622Sfenner	if (inm->inm_state == IGMP_IREPORTEDLAST &&
37414622Sfenner	    inm->inm_addr.s_addr != igmp_all_hosts_group &&
37514622Sfenner	    !(inm->inm_ifp->if_flags & IFF_LOOPBACK) &&
37614622Sfenner	    inm->inm_rti->rti_type != IGMP_V1_ROUTER)
37714622Sfenner		igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group);
3781541Srgrimes}
3791541Srgrimes
3801541Srgrimesvoid
381107113Sluigiigmp_fasttimo()
3821541Srgrimes{
383107113Sluigi	register struct in_multi *inm;
3841541Srgrimes	struct in_multistep step;
3859209Swollman	int s;
3861541Srgrimes
3871541Srgrimes	/*
3881541Srgrimes	 * Quick check to see if any work needs to be done, in order
3891541Srgrimes	 * to minimize the overhead of fasttimo processing.
3901541Srgrimes	 */
3919209Swollman
3921541Srgrimes	if (!igmp_timers_are_running)
3931541Srgrimes		return;
3941541Srgrimes
3951541Srgrimes	s = splnet();
3961541Srgrimes	igmp_timers_are_running = 0;
3971541Srgrimes	IN_FIRST_MULTI(step, inm);
3981541Srgrimes	while (inm != NULL) {
3991541Srgrimes		if (inm->inm_timer == 0) {
4001541Srgrimes			/* do nothing */
4011541Srgrimes		} else if (--inm->inm_timer == 0) {
40214622Sfenner			igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
40314622Sfenner			inm->inm_state = IGMP_IREPORTEDLAST;
4041541Srgrimes		} else {
4051541Srgrimes			igmp_timers_are_running = 1;
4061541Srgrimes		}
4071541Srgrimes		IN_NEXT_MULTI(step, inm);
4081541Srgrimes	}
4091541Srgrimes	splx(s);
4101541Srgrimes}
4111541Srgrimes
4122531Swollmanvoid
413107113Sluigiigmp_slowtimo()
4142531Swollman{
4152531Swollman	int s = splnet();
416119180Srwatson	struct router_info *rti;
4172531Swollman
418119180Srwatson	IGMP_PRINTF("[igmp.c,_slowtimo] -- > entering \n");
419119180Srwatson	SLIST_FOREACH(rti, &router_info_head, rti_list) {
42014622Sfenner	    if (rti->rti_type == IGMP_V1_ROUTER) {
42114622Sfenner		rti->rti_time++;
42214622Sfenner		if (rti->rti_time >= IGMP_AGE_THRESHOLD) {
42314622Sfenner			rti->rti_type = IGMP_V2_ROUTER;
4242531Swollman		}
42514622Sfenner	    }
4262531Swollman	}
427119180Srwatson	IGMP_PRINTF("[igmp.c,_slowtimo] -- > exiting \n");
4282531Swollman	splx(s);
4292531Swollman}
4302531Swollman
43115292Swollmanstatic struct route igmprt;
43215292Swollman
4331541Srgrimesstatic void
434107113Sluigiigmp_sendpkt(inm, type, addr)
435107113Sluigi	struct in_multi *inm;
436107113Sluigi	int type;
437107113Sluigi	unsigned long addr;
4381541Srgrimes{
4392531Swollman        struct mbuf *m;
4402531Swollman        struct igmp *igmp;
4412531Swollman        struct ip *ip;
44215292Swollman        struct ip_moptions imo;
4431541Srgrimes
444111119Simp        MGETHDR(m, M_DONTWAIT, MT_HEADER);
4452531Swollman        if (m == NULL)
4462531Swollman                return;
4472531Swollman
4488090Spst	m->m_pkthdr.rcvif = loif;
449101091Srwatson#ifdef MAC
450101091Srwatson	mac_create_mbuf_linklayer(inm->inm_ifp, m);
451101091Srwatson#endif
4521541Srgrimes	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
4532531Swollman	MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip));
4542531Swollman	m->m_data += sizeof(struct ip);
4552531Swollman        m->m_len = IGMP_MINLEN;
4562531Swollman        igmp = mtod(m, struct igmp *);
4572531Swollman        igmp->igmp_type   = type;
4582531Swollman        igmp->igmp_code   = 0;
4592531Swollman        igmp->igmp_group  = inm->inm_addr;
4602531Swollman        igmp->igmp_cksum  = 0;
4612531Swollman        igmp->igmp_cksum  = in_cksum(m, IGMP_MINLEN);
4621541Srgrimes
4632531Swollman        m->m_data -= sizeof(struct ip);
4642531Swollman        m->m_len += sizeof(struct ip);
4652531Swollman        ip = mtod(m, struct ip *);
4662531Swollman        ip->ip_tos        = 0;
4672531Swollman        ip->ip_len        = sizeof(struct ip) + IGMP_MINLEN;
4682531Swollman        ip->ip_off        = 0;
4692531Swollman        ip->ip_p          = IPPROTO_IGMP;
4702531Swollman        ip->ip_src.s_addr = INADDR_ANY;
47114622Sfenner        ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr;
4721541Srgrimes
47315292Swollman        imo.imo_multicast_ifp  = inm->inm_ifp;
47415292Swollman        imo.imo_multicast_ttl  = 1;
47515292Swollman	imo.imo_multicast_vif  = -1;
4762531Swollman        /*
4772531Swollman         * Request loopback of the report if we are acting as a multicast
47896432Sdd         * router, so that the process-level routing daemon can hear it.
4792531Swollman         */
48015292Swollman        imo.imo_multicast_loop = (ip_mrouter != NULL);
4811541Srgrimes
48215292Swollman	/*
48315292Swollman	 * XXX
48415292Swollman	 * Do we have to worry about reentrancy here?  Don't think so.
48515292Swollman	 */
486105194Ssam        ip_output(m, router_alert, &igmprt, 0, &imo, NULL);
4872531Swollman
4882531Swollman        ++igmpstat.igps_snd_reports;
4891541Srgrimes}
490