igmp.c revision 41702
1122394Sharti/* 2150920Sharti * Copyright (c) 1988 Stephen Deering. 3150920Sharti * Copyright (c) 1992, 1993 4150920Sharti * The Regents of the University of California. All rights reserved. 5122394Sharti * 6122394Sharti * This code is derived from software contributed to Berkeley by 7122394Sharti * Stephen Deering of Stanford University. 8122394Sharti * 9208483Suqs * Redistribution and use in source and binary forms, with or without 10133211Sharti * modification, are permitted provided that the following conditions 11133211Sharti * are met: 12133211Sharti * 1. Redistributions of source code must retain the above copyright 13133211Sharti * notice, this list of conditions and the following disclaimer. 14133211Sharti * 2. Redistributions in binary form must reproduce the above copyright 15133211Sharti * notice, this list of conditions and the following disclaimer in the 16122394Sharti * documentation and/or other materials provided with the distribution. 17122394Sharti * 3. All advertising materials mentioning features or use of this software 18122394Sharti * must display the following acknowledgement: 19133211Sharti * This product includes software developed by the University of 20133211Sharti * California, Berkeley and its contributors. 21133211Sharti * 4. Neither the name of the University nor the names of its contributors 22133211Sharti * may be used to endorse or promote products derived from this software 23133211Sharti * without specific prior written permission. 24133211Sharti * 25133211Sharti * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26133211Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27133211Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28133211Sharti * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29133211Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30133211Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31122394Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32150920Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33122394Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34216594Ssyrinx * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35142810Sharti * SUCH DAMAGE. 36122394Sharti * 37122394Sharti * @(#)igmp.c 8.1 (Berkeley) 7/19/93 38122394Sharti * $Id: igmp.c,v 1.25 1997/10/12 20:25:21 phk Exp $ 39122394Sharti */ 40122394Sharti 41122394Sharti/* 42122394Sharti * Internet Group Management Protocol (IGMP) routines. 43122394Sharti * 44122394Sharti * Written by Steve Deering, Stanford, May 1988. 45122394Sharti * Modified by Rosen Sharma, Stanford, Aug 1994. 46122394Sharti * Modified by Bill Fenner, Xerox PARC, Feb 1995. 47122394Sharti * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995. 48122394Sharti * 49122394Sharti * MULTICAST Revision: 3.5.1.4 50122394Sharti */ 51122394Sharti 52122394Sharti#include <sys/param.h> 53122394Sharti#include <sys/systm.h> 54122394Sharti#include <sys/malloc.h> 55122394Sharti#include <sys/mbuf.h> 56122394Sharti#include <sys/socket.h> 57122394Sharti#include <sys/protosw.h> 58122394Sharti#include <sys/kernel.h> 59122394Sharti#include <sys/sysctl.h> 60122394Sharti 61122394Sharti#include <net/if.h> 62122394Sharti#include <net/route.h> 63216294Ssyrinx 64216294Ssyrinx#include <netinet/in.h> 65122394Sharti#include <netinet/in_var.h> 66122394Sharti#include <netinet/in_systm.h> 67122394Sharti#include <netinet/ip.h> 68122394Sharti#include <netinet/ip_var.h> 69122394Sharti#include <netinet/igmp.h> 70122394Sharti#include <netinet/igmp_var.h> 71150920Sharti 72122394Shartistatic MALLOC_DEFINE(M_MRTABLE, "mrt", "multicast routing tables"); 73122394Sharti 74122394Shartistatic struct router_info * 75122394Sharti find_rti __P((struct ifnet *ifp)); 76122394Sharti 77122394Shartistatic struct igmpstat igmpstat; 78122394Sharti 79122394ShartiSYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD, 80122394Sharti &igmpstat, igmpstat, ""); 81122394Sharti 82122394Shartistatic int igmp_timers_are_running; 83122394Shartistatic u_long igmp_all_hosts_group; 84122394Shartistatic u_long igmp_all_rtrs_group; 85122394Shartistatic struct mbuf *router_alert; 86216594Ssyrinxstatic struct router_info *Head; 87122394Sharti 88122394Shartistatic void igmp_sendpkt __P((struct in_multi *, int, unsigned long)); 89122394Sharti 90122394Shartivoid 91156066Shartiigmp_init() 92122394Sharti{ 93122394Sharti struct ipoption *ra; 94122394Sharti 95122394Sharti /* 96122394Sharti * To avoid byte-swapping the same value over and over again. 97122394Sharti */ 98122394Sharti igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 99122394Sharti igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP); 100122394Sharti 101122394Sharti igmp_timers_are_running = 0; 102122394Sharti 103122394Sharti /* 104122394Sharti * Construct a Router Alert option to use in outgoing packets 105216294Ssyrinx */ 106216594Ssyrinx MGET(router_alert, M_DONTWAIT, MT_DATA); 107216294Ssyrinx ra = mtod(router_alert, struct ipoption *); 108216294Ssyrinx ra->ipopt_dst.s_addr = 0; 109216294Ssyrinx ra->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ 110216294Ssyrinx ra->ipopt_list[1] = 0x04; /* 4 bytes long */ 111216294Ssyrinx ra->ipopt_list[2] = 0x00; 112216294Ssyrinx ra->ipopt_list[3] = 0x00; 113216294Ssyrinx router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; 114216294Ssyrinx 115216294Ssyrinx Head = (struct router_info *) 0; 116216594Ssyrinx} 117216594Ssyrinx 118216594Ssyrinxstatic struct router_info * 119216594Ssyrinxfind_rti(ifp) 120216594Ssyrinx struct ifnet *ifp; 121216594Ssyrinx{ 122216594Ssyrinx register struct router_info *rti = Head; 123216594Ssyrinx 124216594Ssyrinx#ifdef IGMP_DEBUG 125216594Ssyrinx printf("[igmp.c, _find_rti] --> entering \n"); 126216594Ssyrinx#endif 127216594Ssyrinx while (rti) { 128216594Ssyrinx if (rti->rti_ifp == ifp) { 129216594Ssyrinx#ifdef IGMP_DEBUG 130216594Ssyrinx printf("[igmp.c, _find_rti] --> found old entry \n"); 131216594Ssyrinx#endif 132216594Ssyrinx return rti; 133216594Ssyrinx } 134216594Ssyrinx rti = rti->rti_next; 135122394Sharti } 136122394Sharti MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT); 137122394Sharti rti->rti_ifp = ifp; 138122394Sharti rti->rti_type = IGMP_V2_ROUTER; 139122394Sharti rti->rti_time = 0; 140122394Sharti rti->rti_next = Head; 141122394Sharti Head = rti; 142122394Sharti#ifdef IGMP_DEBUG 143122394Sharti printf("[igmp.c, _find_rti] --> created an entry \n"); 144122394Sharti#endif 145122394Sharti return rti; 146122394Sharti} 147122394Sharti 148122394Shartivoid 149122394Shartiigmp_input(m, iphlen) 150122394Sharti register struct mbuf *m; 151122394Sharti register int iphlen; 152122394Sharti{ 153122394Sharti register struct igmp *igmp; 154122394Sharti register struct ip *ip; 155122394Sharti register int igmplen; 156122394Sharti register struct ifnet *ifp = m->m_pkthdr.rcvif; 157122394Sharti register int minlen; 158122394Sharti register struct in_multi *inm; 159146525Sharti register struct in_ifaddr *ia; 160146525Sharti struct in_multistep step; 161146525Sharti struct router_info *rti; 162122394Sharti 163122394Sharti int timer; /** timer value in the igmp query header **/ 164122394Sharti 165122394Sharti ++igmpstat.igps_rcv_total; 166122394Sharti 167122394Sharti ip = mtod(m, struct ip *); 168122394Sharti igmplen = ip->ip_len; 169122394Sharti 170122394Sharti /* 171122394Sharti * Validate lengths 172122394Sharti */ 173122394Sharti if (igmplen < IGMP_MINLEN) { 174122394Sharti ++igmpstat.igps_rcv_tooshort; 175122394Sharti m_freem(m); 176122394Sharti return; 177122394Sharti } 178122394Sharti minlen = iphlen + IGMP_MINLEN; 179122394Sharti if ((m->m_flags & M_EXT || m->m_len < minlen) && 180122394Sharti (m = m_pullup(m, minlen)) == 0) { 181122394Sharti ++igmpstat.igps_rcv_tooshort; 182150920Sharti return; 183150920Sharti } 184122394Sharti 185122394Sharti /* 186122394Sharti * Validate checksum 187122394Sharti */ 188122394Sharti m->m_data += iphlen; 189122394Sharti m->m_len -= iphlen; 190122394Sharti igmp = mtod(m, struct igmp *); 191122394Sharti if (in_cksum(m, igmplen)) { 192122394Sharti ++igmpstat.igps_rcv_badsum; 193122394Sharti m_freem(m); 194122394Sharti return; 195122394Sharti } 196122394Sharti m->m_data -= iphlen; 197122394Sharti m->m_len += iphlen; 198122394Sharti 199122394Sharti ip = mtod(m, struct ip *); 200122394Sharti timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; 201122394Sharti if (timer == 0) 202122394Sharti timer = 1; 203142810Sharti rti = find_rti(ifp); 204142810Sharti 205142810Sharti /* 206142810Sharti * In the IGMPv2 specification, there are 3 states and a flag. 207122394Sharti * 208142810Sharti * In Non-Member state, we simply don't have a membership record. 209142810Sharti * In Delaying Member state, our timer is running (inm->inm_timer) 210142810Sharti * In Idle Member state, our timer is not running (inm->inm_timer==0) 211142810Sharti * 212142810Sharti * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if 213122394Sharti * we have heard a report from another member, or IGMP_IREPORTEDLAST 214142810Sharti * if I sent the last report. 215142810Sharti */ 216142810Sharti switch (igmp->igmp_type) { 217142810Sharti 218122394Sharti case IGMP_MEMBERSHIP_QUERY: 219142810Sharti ++igmpstat.igps_rcv_queries; 220142810Sharti 221142810Sharti if (ifp->if_flags & IFF_LOOPBACK) 222142810Sharti break; 223122394Sharti 224122394Sharti if (igmp->igmp_code == 0) { 225216594Ssyrinx /* 226216594Ssyrinx * Old router. Remember that the querier on this 227122394Sharti * interface is old, and set the timer to the 228122394Sharti * value in RFC 1112. 229122394Sharti */ 230122394Sharti 231122394Sharti rti->rti_type = IGMP_V1_ROUTER; 232122394Sharti rti->rti_time = 0; 233122394Sharti 234122394Sharti timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; 235156066Sharti 236156066Sharti if (ip->ip_dst.s_addr != igmp_all_hosts_group || 237122394Sharti igmp->igmp_group.s_addr != 0) { 238122394Sharti ++igmpstat.igps_rcv_badqueries; 239122394Sharti m_freem(m); 240122394Sharti return; 241122394Sharti } 242122394Sharti } else { 243122394Sharti /* 244122394Sharti * New router. Simply do the new validity check. 245122394Sharti */ 246122394Sharti 247122394Sharti if (igmp->igmp_group.s_addr != 0 && 248122394Sharti !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { 249122394Sharti ++igmpstat.igps_rcv_badqueries; 250122394Sharti m_freem(m); 251122394Sharti return; 252122394Sharti } 253122394Sharti } 254122394Sharti 255122394Sharti /* 256122394Sharti * - Start the timers in all of our membership records 257122394Sharti * that the query applies to for the interface on 258122394Sharti * which the query arrived excl. those that belong 259122394Sharti * to the "all-hosts" group (224.0.0.1). 260122394Sharti * - Restart any timer that is already running but has 261122394Sharti * a value longer than the requested timeout. 262122394Sharti * - Use the value specified in the query message as 263122394Sharti * the maximum timeout. 264122394Sharti */ 265216594Ssyrinx IN_FIRST_MULTI(step, inm); 266216294Ssyrinx while (inm != NULL) { 267216294Ssyrinx if (inm->inm_ifp == ifp && 268216294Ssyrinx inm->inm_addr.s_addr != igmp_all_hosts_group && 269216294Ssyrinx (igmp->igmp_group.s_addr == 0 || 270216294Ssyrinx igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { 271216294Ssyrinx if (inm->inm_timer == 0 || 272216294Ssyrinx inm->inm_timer > timer) { 273216294Ssyrinx inm->inm_timer = 274216294Ssyrinx IGMP_RANDOM_DELAY(timer); 275216294Ssyrinx igmp_timers_are_running = 1; 276216294Ssyrinx } 277216294Ssyrinx } 278216294Ssyrinx IN_NEXT_MULTI(step, inm); 279216294Ssyrinx } 280216294Ssyrinx 281216294Ssyrinx break; 282216294Ssyrinx 283216594Ssyrinx case IGMP_V1_MEMBERSHIP_REPORT: 284216594Ssyrinx case IGMP_V2_MEMBERSHIP_REPORT: 285216594Ssyrinx /* 286216594Ssyrinx * For fast leave to work, we have to know that we are the 287216594Ssyrinx * last person to send a report for this group. Reports 288216594Ssyrinx * can potentially get looped back if we are a multicast 289216594Ssyrinx * router, so discard reports sourced by me. 290216594Ssyrinx */ 291216594Ssyrinx IFP_TO_IA(ifp, ia); 292216594Ssyrinx if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) 293216594Ssyrinx break; 294216594Ssyrinx 295216594Ssyrinx ++igmpstat.igps_rcv_reports; 296216594Ssyrinx 297216594Ssyrinx if (ifp->if_flags & IFF_LOOPBACK) 298216594Ssyrinx break; 299216594Ssyrinx 300216594Ssyrinx if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { 301216594Ssyrinx ++igmpstat.igps_rcv_badreports; 302216594Ssyrinx m_freem(m); 303216594Ssyrinx return; 304216594Ssyrinx } 305216594Ssyrinx 306216594Ssyrinx /* 307216594Ssyrinx * KLUDGE: if the IP source address of the report has an 308216594Ssyrinx * unspecified (i.e., zero) subnet number, as is allowed for 309216594Ssyrinx * a booting host, replace it with the correct subnet number 310216594Ssyrinx * so that a process-level multicast routing demon can 311216594Ssyrinx * determine which subnet it arrived from. This is necessary 312216594Ssyrinx * to compensate for the lack of any way for a process to 313216294Ssyrinx * determine the arrival interface of an incoming packet. 314216294Ssyrinx */ 315122394Sharti if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) 316122394Sharti if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 317150920Sharti 318122394Sharti /* 319122394Sharti * If we belong to the group being reported, stop 320122394Sharti * our timer for that group. 321150920Sharti */ 322150920Sharti IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 323122394Sharti 324150920Sharti if (inm != NULL) { 325122394Sharti inm->inm_timer = 0; 326122394Sharti ++igmpstat.igps_rcv_ourreports; 327150920Sharti 328150920Sharti inm->inm_state = IGMP_OTHERMEMBER; 329150920Sharti } 330122394Sharti 331150920Sharti break; 332150920Sharti } 333122394Sharti 334142810Sharti /* 335150920Sharti * Pass all valid IGMP packets up to any process(es) listening 336150920Sharti * on a raw IGMP socket. 337122394Sharti */ 338122394Sharti rip_input(m, iphlen); 339122394Sharti} 340122394Sharti 341122394Shartivoid 342122394Shartiigmp_joingroup(inm) 343122394Sharti struct in_multi *inm; 344122394Sharti{ 345122394Sharti int s = splnet(); 346122394Sharti 347122394Sharti if (inm->inm_addr.s_addr == igmp_all_hosts_group 348122394Sharti || inm->inm_ifp->if_flags & IFF_LOOPBACK) { 349124861Sharti inm->inm_timer = 0; 350122394Sharti inm->inm_state = IGMP_OTHERMEMBER; 351122394Sharti } else { 352122394Sharti inm->inm_rti = find_rti(inm->inm_ifp); 353122394Sharti igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); 354122394Sharti inm->inm_timer = IGMP_RANDOM_DELAY( 355122394Sharti IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); 356122394Sharti inm->inm_state = IGMP_IREPORTEDLAST; 357122394Sharti igmp_timers_are_running = 1; 358122394Sharti } 359122394Sharti splx(s); 360122394Sharti} 361122394Sharti 362122394Shartivoid 363122394Shartiigmp_leavegroup(inm) 364122394Sharti struct in_multi *inm; 365122394Sharti{ 366122394Sharti if (inm->inm_state == IGMP_IREPORTEDLAST && 367122394Sharti inm->inm_addr.s_addr != igmp_all_hosts_group && 368122394Sharti !(inm->inm_ifp->if_flags & IFF_LOOPBACK) && 369122394Sharti inm->inm_rti->rti_type != IGMP_V1_ROUTER) 370122394Sharti igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group); 371122394Sharti} 372122394Sharti 373150920Shartivoid 374150920Shartiigmp_fasttimo() 375122394Sharti{ 376150920Sharti register struct in_multi *inm; 377150920Sharti struct in_multistep step; 378122394Sharti int s; 379122394Sharti 380150920Sharti /* 381150920Sharti * Quick check to see if any work needs to be done, in order 382150920Sharti * to minimize the overhead of fasttimo processing. 383122394Sharti */ 384122394Sharti 385122394Sharti if (!igmp_timers_are_running) 386122394Sharti return; 387150920Sharti 388150920Sharti s = splnet(); 389150920Sharti igmp_timers_are_running = 0; 390150920Sharti IN_FIRST_MULTI(step, inm); 391150920Sharti while (inm != NULL) { 392122394Sharti if (inm->inm_timer == 0) { 393122394Sharti /* do nothing */ 394122394Sharti } else if (--inm->inm_timer == 0) { 395122394Sharti igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); 396122394Sharti inm->inm_state = IGMP_IREPORTEDLAST; 397122394Sharti } else { 398150920Sharti igmp_timers_are_running = 1; 399150920Sharti } 400122394Sharti IN_NEXT_MULTI(step, inm); 401122394Sharti } 402122394Sharti splx(s); 403122394Sharti} 404122394Sharti 405122394Shartivoid 406122394Shartiigmp_slowtimo() 407122394Sharti{ 408122394Sharti int s = splnet(); 409122394Sharti register struct router_info *rti = Head; 410122394Sharti 411122394Sharti#ifdef IGMP_DEBUG 412122394Sharti printf("[igmp.c,_slowtimo] -- > entering \n"); 413122394Sharti#endif 414122394Sharti while (rti) { 415122394Sharti if (rti->rti_type == IGMP_V1_ROUTER) { 416122394Sharti rti->rti_time++; 417122394Sharti if (rti->rti_time >= IGMP_AGE_THRESHOLD) { 418122394Sharti rti->rti_type = IGMP_V2_ROUTER; 419122394Sharti } 420122394Sharti } 421122394Sharti rti = rti->rti_next; 422122394Sharti } 423122394Sharti#ifdef IGMP_DEBUG 424122394Sharti printf("[igmp.c,_slowtimo] -- > exiting \n"); 425122394Sharti#endif 426150920Sharti splx(s); 427122394Sharti} 428122394Sharti 429122394Shartistatic struct route igmprt; 430122394Sharti 431122394Shartistatic void 432122394Shartiigmp_sendpkt(inm, type, addr) 433122394Sharti struct in_multi *inm; 434122394Sharti int type; 435122394Sharti unsigned long addr; 436122394Sharti{ 437122394Sharti struct mbuf *m; 438122394Sharti struct igmp *igmp; 439122394Sharti struct ip *ip; 440150920Sharti struct ip_moptions imo; 441150920Sharti 442122394Sharti MGETHDR(m, M_DONTWAIT, MT_HEADER); 443122394Sharti if (m == NULL) 444122394Sharti return; 445122394Sharti 446122394Sharti m->m_pkthdr.rcvif = loif; 447122394Sharti m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 448150920Sharti MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip)); 449150920Sharti m->m_data += sizeof(struct ip); 450150920Sharti m->m_len = IGMP_MINLEN; 451150920Sharti igmp = mtod(m, struct igmp *); 452122394Sharti igmp->igmp_type = type; 453122394Sharti igmp->igmp_code = 0; 454142810Sharti igmp->igmp_group = inm->inm_addr; 455122394Sharti igmp->igmp_cksum = 0; 456122394Sharti igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 457122394Sharti 458122394Sharti m->m_data -= sizeof(struct ip); 459122394Sharti m->m_len += sizeof(struct ip); 460122394Sharti ip = mtod(m, struct ip *); 461122394Sharti ip->ip_tos = 0; 462122394Sharti ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 463122394Sharti ip->ip_off = 0; 464122394Sharti ip->ip_p = IPPROTO_IGMP; 465122394Sharti ip->ip_src.s_addr = INADDR_ANY; 466122394Sharti ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr; 467122394Sharti 468122394Sharti imo.imo_multicast_ifp = inm->inm_ifp; 469150920Sharti imo.imo_multicast_ttl = 1; 470150920Sharti imo.imo_multicast_vif = -1; 471122394Sharti /* 472150920Sharti * Request loopback of the report if we are acting as a multicast 473150920Sharti * router, so that the process-level routing demon can hear it. 474122394Sharti */ 475122394Sharti imo.imo_multicast_loop = (ip_mrouter != NULL); 476122394Sharti 477122394Sharti /* 478122394Sharti * XXX 479122394Sharti * Do we have to worry about reentrancy here? Don't think so. 480122394Sharti */ 481122394Sharti ip_output(m, router_alert, &igmprt, 0, &imo); 482122394Sharti 483122394Sharti ++igmpstat.igps_snd_reports; 484122394Sharti} 485122394Sharti