igmp.c revision 163606
1139823Simp/*- 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 * 4. Neither the name of the University nor the names of its contributors 181541Srgrimes * may be used to endorse or promote products derived from this software 191541Srgrimes * without specific prior written permission. 201541Srgrimes * 211541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311541Srgrimes * SUCH DAMAGE. 321541Srgrimes * 331541Srgrimes * @(#)igmp.c 8.1 (Berkeley) 7/19/93 3450477Speter * $FreeBSD: head/sys/netinet/igmp.c 163606 2006-10-22 11:52:19Z rwatson $ 351541Srgrimes */ 361541Srgrimes 372531Swollman/* 382531Swollman * Internet Group Management Protocol (IGMP) routines. 392531Swollman * 402531Swollman * Written by Steve Deering, Stanford, May 1988. 412531Swollman * Modified by Rosen Sharma, Stanford, Aug 1994. 429209Swollman * Modified by Bill Fenner, Xerox PARC, Feb 1995. 4314622Sfenner * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995. 442531Swollman * 4514622Sfenner * MULTICAST Revision: 3.5.1.4 462531Swollman */ 471541Srgrimes 48101091Srwatson#include "opt_mac.h" 49101091Srwatson 501541Srgrimes#include <sys/param.h> 511549Srgrimes#include <sys/systm.h> 5229024Sbde#include <sys/malloc.h> 531541Srgrimes#include <sys/mbuf.h> 541541Srgrimes#include <sys/socket.h> 551541Srgrimes#include <sys/protosw.h> 5612296Sphk#include <sys/kernel.h> 576472Swollman#include <sys/sysctl.h> 581541Srgrimes 591541Srgrimes#include <net/if.h> 601541Srgrimes#include <net/route.h> 611541Srgrimes 621541Srgrimes#include <netinet/in.h> 631541Srgrimes#include <netinet/in_var.h> 641541Srgrimes#include <netinet/in_systm.h> 651541Srgrimes#include <netinet/ip.h> 661541Srgrimes#include <netinet/ip_var.h> 67152592Sandre#include <netinet/ip_options.h> 681541Srgrimes#include <netinet/igmp.h> 691541Srgrimes#include <netinet/igmp_var.h> 701541Srgrimes 7160105Sjlemon#include <machine/in_cksum.h> 7260105Sjlemon 73163606Srwatson#include <security/mac/mac_framework.h> 74163606Srwatson 7542776Sfennerstatic MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); 7630309Sphk 77119181Srwatsonstatic struct router_info *find_rti(struct ifnet *ifp); 78119181Srwatsonstatic void igmp_sendpkt(struct in_multi *, int, unsigned long); 7912579Sbde 8012704Sphkstatic struct igmpstat igmpstat; 812531Swollman 82119181SrwatsonSYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RW, &igmpstat, 83119181Srwatson igmpstat, ""); 8412296Sphk 85130333Srwatson/* 86130333Srwatson * igmp_mtx protects all mutable global variables in igmp.c, as well as 87130333Srwatson * the data fields in struct router_info. In general, a router_info 88130333Srwatson * structure will be valid as long as the referencing struct in_multi is 89130333Srwatson * valid, so no reference counting is used. We allow unlocked reads of 90130333Srwatson * router_info data when accessed via an in_multi read-only. 91130333Srwatson */ 92130333Srwatsonstatic struct mtx igmp_mtx; 93119181Srwatsonstatic SLIST_HEAD(, router_info) router_info_head; 949209Swollmanstatic int igmp_timers_are_running; 95130333Srwatson 96130333Srwatson/* 97130333Srwatson * XXXRW: can we define these such that these can be made const? In any 98130333Srwatson * case, these shouldn't be changed after igmp_init() and therefore don't 99130333Srwatson * need locking. 100130333Srwatson */ 1011541Srgrimesstatic u_long igmp_all_hosts_group; 10214622Sfennerstatic u_long igmp_all_rtrs_group; 103130333Srwatson 10414622Sfennerstatic struct mbuf *router_alert; 105119181Srwatsonstatic struct route igmprt; 1061541Srgrimes 107119180Srwatson#ifdef IGMP_DEBUG 108119180Srwatson#define IGMP_PRINTF(x) printf(x) 109119180Srwatson#else 110119180Srwatson#define IGMP_PRINTF(x) 111119180Srwatson#endif 112119180Srwatson 1131541Srgrimesvoid 114119181Srwatsonigmp_init(void) 1151541Srgrimes{ 11614622Sfenner struct ipoption *ra; 11714622Sfenner 1181541Srgrimes /* 1191541Srgrimes * To avoid byte-swapping the same value over and over again. 1201541Srgrimes */ 1211541Srgrimes igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 12214622Sfenner igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP); 1239209Swollman 1249209Swollman igmp_timers_are_running = 0; 1259209Swollman 12614622Sfenner /* 12714622Sfenner * Construct a Router Alert option to use in outgoing packets 12814622Sfenner */ 129111119Simp MGET(router_alert, M_DONTWAIT, MT_DATA); 13014622Sfenner ra = mtod(router_alert, struct ipoption *); 13114622Sfenner ra->ipopt_dst.s_addr = 0; 13214622Sfenner ra->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ 13314622Sfenner ra->ipopt_list[1] = 0x04; /* 4 bytes long */ 13414622Sfenner ra->ipopt_list[2] = 0x00; 13514622Sfenner ra->ipopt_list[3] = 0x00; 13614622Sfenner router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; 13714622Sfenner 138130333Srwatson mtx_init(&igmp_mtx, "igmp_mtx", NULL, MTX_DEF); 139119180Srwatson SLIST_INIT(&router_info_head); 1401541Srgrimes} 1411541Srgrimes 14212704Sphkstatic struct router_info * 143119181Srwatsonfind_rti(struct ifnet *ifp) 1442531Swollman{ 145119180Srwatson struct router_info *rti; 1462531Swollman 147130333Srwatson mtx_assert(&igmp_mtx, MA_OWNED); 148119180Srwatson IGMP_PRINTF("[igmp.c, _find_rti] --> entering \n"); 149119180Srwatson SLIST_FOREACH(rti, &router_info_head, rti_list) { 150119180Srwatson if (rti->rti_ifp == ifp) { 151119180Srwatson IGMP_PRINTF( 152119180Srwatson "[igmp.c, _find_rti] --> found old entry \n"); 153119181Srwatson return rti; 154119181Srwatson } 155119181Srwatson } 15642776Sfenner MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, M_NOWAIT); 157144163Ssam if (rti == NULL) { 158144163Ssam IGMP_PRINTF( "[igmp.c, _find_rti] --> no memory for entry\n"); 159144163Ssam return NULL; 160144163Ssam } 161119181Srwatson rti->rti_ifp = ifp; 162119181Srwatson rti->rti_type = IGMP_V2_ROUTER; 163119181Srwatson rti->rti_time = 0; 164119180Srwatson SLIST_INSERT_HEAD(&router_info_head, rti, rti_list); 165119180Srwatson 166119180Srwatson IGMP_PRINTF("[igmp.c, _find_rti] --> created an entry \n"); 167119181Srwatson return rti; 1682531Swollman} 1692531Swollman 1701541Srgrimesvoid 171119181Srwatsonigmp_input(register struct mbuf *m, int off) 1721541Srgrimes{ 173107113Sluigi register int iphlen = off; 174107113Sluigi register struct igmp *igmp; 175107113Sluigi register struct ip *ip; 176107113Sluigi register int igmplen; 177107113Sluigi register struct ifnet *ifp = m->m_pkthdr.rcvif; 178107113Sluigi register int minlen; 179107113Sluigi register struct in_multi *inm; 180107113Sluigi register struct in_ifaddr *ia; 1811541Srgrimes struct in_multistep step; 1822531Swollman struct router_info *rti; 1838546Sdg int timer; /** timer value in the igmp query header **/ 1841541Srgrimes 1851541Srgrimes ++igmpstat.igps_rcv_total; 1861541Srgrimes 1871541Srgrimes ip = mtod(m, struct ip *); 1881541Srgrimes igmplen = ip->ip_len; 1891541Srgrimes 1901541Srgrimes /* 1911541Srgrimes * Validate lengths 1921541Srgrimes */ 1931541Srgrimes if (igmplen < IGMP_MINLEN) { 1941541Srgrimes ++igmpstat.igps_rcv_tooshort; 1951541Srgrimes m_freem(m); 1961541Srgrimes return; 1971541Srgrimes } 1981541Srgrimes minlen = iphlen + IGMP_MINLEN; 1991541Srgrimes if ((m->m_flags & M_EXT || m->m_len < minlen) && 2001541Srgrimes (m = m_pullup(m, minlen)) == 0) { 2011541Srgrimes ++igmpstat.igps_rcv_tooshort; 2021541Srgrimes return; 2031541Srgrimes } 2041541Srgrimes 2051541Srgrimes /* 2061541Srgrimes * Validate checksum 2071541Srgrimes */ 2081541Srgrimes m->m_data += iphlen; 2091541Srgrimes m->m_len -= iphlen; 2101541Srgrimes igmp = mtod(m, struct igmp *); 2111541Srgrimes if (in_cksum(m, igmplen)) { 2121541Srgrimes ++igmpstat.igps_rcv_badsum; 2131541Srgrimes m_freem(m); 2141541Srgrimes return; 2151541Srgrimes } 2161541Srgrimes m->m_data -= iphlen; 2171541Srgrimes m->m_len += iphlen; 2182531Swollman 2191541Srgrimes ip = mtod(m, struct ip *); 2208546Sdg timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 22141702Sdillon if (timer == 0) 22241702Sdillon timer = 1; 2231541Srgrimes 22414622Sfenner /* 22514622Sfenner * In the IGMPv2 specification, there are 3 states and a flag. 22614622Sfenner * 22714622Sfenner * In Non-Member state, we simply don't have a membership record. 22814622Sfenner * In Delaying Member state, our timer is running (inm->inm_timer) 22914622Sfenner * In Idle Member state, our timer is not running (inm->inm_timer==0) 23014622Sfenner * 23114622Sfenner * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if 23214622Sfenner * we have heard a report from another member, or IGMP_IREPORTEDLAST 23314622Sfenner * if I sent the last report. 23414622Sfenner */ 2351541Srgrimes switch (igmp->igmp_type) { 23614622Sfenner case IGMP_MEMBERSHIP_QUERY: 2371541Srgrimes ++igmpstat.igps_rcv_queries; 2381541Srgrimes 2398090Spst if (ifp->if_flags & IFF_LOOPBACK) 2401541Srgrimes break; 2411541Srgrimes 2422531Swollman if (igmp->igmp_code == 0) { 24314622Sfenner /* 24414622Sfenner * Old router. Remember that the querier on this 24514622Sfenner * interface is old, and set the timer to the 24614622Sfenner * value in RFC 1112. 24714622Sfenner */ 2484028Spst 249130333Srwatson mtx_lock(&igmp_mtx); 250130333Srwatson rti = find_rti(ifp); 251144163Ssam if (rti == NULL) { 252144163Ssam mtx_unlock(&igmp_mtx); 253144163Ssam m_freem(m); 254144163Ssam return; 255144163Ssam } 25614622Sfenner rti->rti_type = IGMP_V1_ROUTER; 25714622Sfenner rti->rti_time = 0; 258130333Srwatson mtx_unlock(&igmp_mtx); 2594028Spst 26014622Sfenner timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; 2614028Spst 26214622Sfenner if (ip->ip_dst.s_addr != igmp_all_hosts_group || 26314622Sfenner igmp->igmp_group.s_addr != 0) { 2642531Swollman ++igmpstat.igps_rcv_badqueries; 2652531Swollman m_freem(m); 2662531Swollman return; 2672531Swollman } 26814622Sfenner } else { 2692531Swollman /* 27014622Sfenner * New router. Simply do the new validity check. 2712531Swollman */ 27214622Sfenner 27314622Sfenner if (igmp->igmp_group.s_addr != 0 && 27414622Sfenner !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { 27514622Sfenner ++igmpstat.igps_rcv_badqueries; 27614622Sfenner m_freem(m); 27714622Sfenner return; 27814622Sfenner } 27914622Sfenner } 2802531Swollman 28114622Sfenner /* 28214622Sfenner * - Start the timers in all of our membership records 28314622Sfenner * that the query applies to for the interface on 28414622Sfenner * which the query arrived excl. those that belong 28514622Sfenner * to the "all-hosts" group (224.0.0.1). 28614622Sfenner * - Restart any timer that is already running but has 28714622Sfenner * a value longer than the requested timeout. 28814622Sfenner * - Use the value specified in the query message as 28914622Sfenner * the maximum timeout. 29014622Sfenner */ 291148682Srwatson IN_MULTI_LOCK(); 29214622Sfenner IN_FIRST_MULTI(step, inm); 29314622Sfenner while (inm != NULL) { 29414622Sfenner if (inm->inm_ifp == ifp && 29514622Sfenner inm->inm_addr.s_addr != igmp_all_hosts_group && 29614622Sfenner (igmp->igmp_group.s_addr == 0 || 29714622Sfenner igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { 29814622Sfenner if (inm->inm_timer == 0 || 29914622Sfenner inm->inm_timer > timer) { 30014622Sfenner inm->inm_timer = 30114622Sfenner IGMP_RANDOM_DELAY(timer); 3022531Swollman igmp_timers_are_running = 1; 3032531Swollman } 3041541Srgrimes } 3051541Srgrimes IN_NEXT_MULTI(step, inm); 3061541Srgrimes } 307148682Srwatson IN_MULTI_UNLOCK(); 3089209Swollman 3091541Srgrimes break; 3101541Srgrimes 31114622Sfenner case IGMP_V1_MEMBERSHIP_REPORT: 31214622Sfenner case IGMP_V2_MEMBERSHIP_REPORT: 3139209Swollman /* 31414622Sfenner * For fast leave to work, we have to know that we are the 31514622Sfenner * last person to send a report for this group. Reports 31614622Sfenner * can potentially get looped back if we are a multicast 31714622Sfenner * router, so discard reports sourced by me. 3189209Swollman */ 31914622Sfenner IFP_TO_IA(ifp, ia); 32014622Sfenner if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) 32114622Sfenner break; 32214622Sfenner 3231541Srgrimes ++igmpstat.igps_rcv_reports; 3241541Srgrimes 3258090Spst if (ifp->if_flags & IFF_LOOPBACK) 3261541Srgrimes break; 3271541Srgrimes 32814622Sfenner if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { 3291541Srgrimes ++igmpstat.igps_rcv_badreports; 3301541Srgrimes m_freem(m); 3311541Srgrimes return; 3321541Srgrimes } 3331541Srgrimes 3341541Srgrimes /* 3351541Srgrimes * KLUDGE: if the IP source address of the report has an 3361541Srgrimes * unspecified (i.e., zero) subnet number, as is allowed for 3371541Srgrimes * a booting host, replace it with the correct subnet number 33896432Sdd * so that a process-level multicast routing daemon can 3391541Srgrimes * determine which subnet it arrived from. This is necessary 3401541Srgrimes * to compensate for the lack of any way for a process to 3411541Srgrimes * determine the arrival interface of an incoming packet. 3421541Srgrimes */ 34314622Sfenner if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) 3441541Srgrimes if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 3451541Srgrimes 3461541Srgrimes /* 3471541Srgrimes * If we belong to the group being reported, stop 3481541Srgrimes * our timer for that group. 3491541Srgrimes */ 350148682Srwatson IN_MULTI_LOCK(); 3511541Srgrimes IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 3522531Swollman if (inm != NULL) { 35314622Sfenner inm->inm_timer = 0; 35414622Sfenner ++igmpstat.igps_rcv_ourreports; 35514622Sfenner 35614622Sfenner inm->inm_state = IGMP_OTHERMEMBER; 3572531Swollman } 358148682Srwatson IN_MULTI_UNLOCK(); 35914622Sfenner 3601541Srgrimes break; 3611541Srgrimes } 3621541Srgrimes 3631541Srgrimes /* 3641541Srgrimes * Pass all valid IGMP packets up to any process(es) listening 3651541Srgrimes * on a raw IGMP socket. 3661541Srgrimes */ 36782890Sjulian rip_input(m, off); 3681541Srgrimes} 3691541Srgrimes 3701541Srgrimesvoid 371119181Srwatsonigmp_joingroup(struct in_multi *inm) 3721541Srgrimes{ 3731541Srgrimes 374148682Srwatson IN_MULTI_LOCK_ASSERT(); 375148682Srwatson 37614622Sfenner if (inm->inm_addr.s_addr == igmp_all_hosts_group 37714622Sfenner || inm->inm_ifp->if_flags & IFF_LOOPBACK) { 3781541Srgrimes inm->inm_timer = 0; 37914622Sfenner inm->inm_state = IGMP_OTHERMEMBER; 38014622Sfenner } else { 381130333Srwatson mtx_lock(&igmp_mtx); 38214622Sfenner inm->inm_rti = find_rti(inm->inm_ifp); 383130333Srwatson mtx_unlock(&igmp_mtx); 384144163Ssam if (inm->inm_rti != NULL) { 385144163Ssam igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); 386144163Ssam inm->inm_timer = IGMP_RANDOM_DELAY( 3872531Swollman IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); 388144163Ssam inm->inm_state = IGMP_IREPORTEDLAST; 389144163Ssam igmp_timers_are_running = 1; 390144163Ssam } 391144163Ssam /* XXX handling of failure case? */ 3921541Srgrimes } 3931541Srgrimes} 3941541Srgrimes 3951541Srgrimesvoid 396119181Srwatsonigmp_leavegroup(struct in_multi *inm) 3971541Srgrimes{ 398119181Srwatson 399148682Srwatson IN_MULTI_LOCK_ASSERT(); 400148682Srwatson 40114622Sfenner if (inm->inm_state == IGMP_IREPORTEDLAST && 40214622Sfenner inm->inm_addr.s_addr != igmp_all_hosts_group && 40314622Sfenner !(inm->inm_ifp->if_flags & IFF_LOOPBACK) && 40414622Sfenner inm->inm_rti->rti_type != IGMP_V1_ROUTER) 40514622Sfenner igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group); 4061541Srgrimes} 4071541Srgrimes 4081541Srgrimesvoid 409119181Srwatsonigmp_fasttimo(void) 4101541Srgrimes{ 411107113Sluigi register struct in_multi *inm; 4121541Srgrimes struct in_multistep step; 4131541Srgrimes 4141541Srgrimes /* 4151541Srgrimes * Quick check to see if any work needs to be done, in order 4161541Srgrimes * to minimize the overhead of fasttimo processing. 4171541Srgrimes */ 4189209Swollman 4191541Srgrimes if (!igmp_timers_are_running) 4201541Srgrimes return; 4211541Srgrimes 422148682Srwatson IN_MULTI_LOCK(); 4231541Srgrimes igmp_timers_are_running = 0; 4241541Srgrimes IN_FIRST_MULTI(step, inm); 4251541Srgrimes while (inm != NULL) { 4261541Srgrimes if (inm->inm_timer == 0) { 4271541Srgrimes /* do nothing */ 4281541Srgrimes } else if (--inm->inm_timer == 0) { 42914622Sfenner igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); 43014622Sfenner inm->inm_state = IGMP_IREPORTEDLAST; 4311541Srgrimes } else { 4321541Srgrimes igmp_timers_are_running = 1; 4331541Srgrimes } 4341541Srgrimes IN_NEXT_MULTI(step, inm); 4351541Srgrimes } 436148682Srwatson IN_MULTI_UNLOCK(); 4371541Srgrimes} 4381541Srgrimes 4392531Swollmanvoid 440119181Srwatsonigmp_slowtimo(void) 4412531Swollman{ 442119180Srwatson struct router_info *rti; 4432531Swollman 444119180Srwatson IGMP_PRINTF("[igmp.c,_slowtimo] -- > entering \n"); 445130333Srwatson mtx_lock(&igmp_mtx); 446119180Srwatson SLIST_FOREACH(rti, &router_info_head, rti_list) { 447119181Srwatson if (rti->rti_type == IGMP_V1_ROUTER) { 448119181Srwatson rti->rti_time++; 449119181Srwatson if (rti->rti_time >= IGMP_AGE_THRESHOLD) 450119181Srwatson rti->rti_type = IGMP_V2_ROUTER; 4512531Swollman } 4522531Swollman } 453130333Srwatson mtx_unlock(&igmp_mtx); 454119180Srwatson IGMP_PRINTF("[igmp.c,_slowtimo] -- > exiting \n"); 4552531Swollman} 4562531Swollman 4571541Srgrimesstatic void 458119181Srwatsonigmp_sendpkt(struct in_multi *inm, int type, unsigned long addr) 4591541Srgrimes{ 460119181Srwatson struct mbuf *m; 461119181Srwatson struct igmp *igmp; 462119181Srwatson struct ip *ip; 463119181Srwatson struct ip_moptions imo; 4641541Srgrimes 465148682Srwatson IN_MULTI_LOCK_ASSERT(); 466148682Srwatson 467151967Sandre MGETHDR(m, M_DONTWAIT, MT_DATA); 468119181Srwatson if (m == NULL) 469119181Srwatson return; 4702531Swollman 4718090Spst m->m_pkthdr.rcvif = loif; 472101091Srwatson#ifdef MAC 473101091Srwatson mac_create_mbuf_linklayer(inm->inm_ifp, m); 474101091Srwatson#endif 4751541Srgrimes m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 4762531Swollman MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip)); 4772531Swollman m->m_data += sizeof(struct ip); 478119181Srwatson m->m_len = IGMP_MINLEN; 479119181Srwatson igmp = mtod(m, struct igmp *); 480119181Srwatson igmp->igmp_type = type; 481119181Srwatson igmp->igmp_code = 0; 482119181Srwatson igmp->igmp_group = inm->inm_addr; 483119181Srwatson igmp->igmp_cksum = 0; 484119181Srwatson igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 4851541Srgrimes 486119181Srwatson m->m_data -= sizeof(struct ip); 487119181Srwatson m->m_len += sizeof(struct ip); 488119181Srwatson ip = mtod(m, struct ip *); 489119181Srwatson ip->ip_tos = 0; 490119181Srwatson ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 491119181Srwatson ip->ip_off = 0; 492119181Srwatson ip->ip_p = IPPROTO_IGMP; 493119181Srwatson ip->ip_src.s_addr = INADDR_ANY; 494119181Srwatson ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr; 4951541Srgrimes 496119181Srwatson imo.imo_multicast_ifp = inm->inm_ifp; 497119181Srwatson imo.imo_multicast_ttl = 1; 49815292Swollman imo.imo_multicast_vif = -1; 499119181Srwatson /* 500119181Srwatson * Request loopback of the report if we are acting as a multicast 501119181Srwatson * router, so that the process-level routing daemon can hear it. 502119181Srwatson */ 503119181Srwatson imo.imo_multicast_loop = (ip_mrouter != NULL); 5041541Srgrimes 50515292Swollman /* 50615292Swollman * XXX 50715292Swollman * Do we have to worry about reentrancy here? Don't think so. 50815292Swollman */ 509119181Srwatson ip_output(m, router_alert, &igmprt, 0, &imo, NULL); 5102531Swollman 511119181Srwatson ++igmpstat.igps_snd_reports; 5121541Srgrimes} 513