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