igmp.c revision 6472
1279430Srstone/* 2279430Srstone * Copyright (c) 1988 Stephen Deering. 3279430Srstone * Copyright (c) 1992, 1993 4279430Srstone * The Regents of the University of California. All rights reserved. 5279430Srstone * 6279430Srstone * This code is derived from software contributed to Berkeley by 7279430Srstone * Stephen Deering of Stanford University. 8279430Srstone * 9279430Srstone * Redistribution and use in source and binary forms, with or without 10279430Srstone * modification, are permitted provided that the following conditions 11279430Srstone * are met: 12279430Srstone * 1. Redistributions of source code must retain the above copyright 13279430Srstone * notice, this list of conditions and the following disclaimer. 14279430Srstone * 2. Redistributions in binary form must reproduce the above copyright 15279430Srstone * notice, this list of conditions and the following disclaimer in the 16279430Srstone * documentation and/or other materials provided with the distribution. 17279430Srstone * 3. All advertising materials mentioning features or use of this software 18279430Srstone * must display the following acknowledgement: 19279430Srstone * This product includes software developed by the University of 20279430Srstone * California, Berkeley and its contributors. 21279430Srstone * 4. Neither the name of the University nor the names of its contributors 22279430Srstone * may be used to endorse or promote products derived from this software 23279430Srstone * without specific prior written permission. 24279430Srstone * 25279430Srstone * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26279430Srstone * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27279430Srstone * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28279430Srstone * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29279430Srstone * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30279430Srstone * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31279430Srstone * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32279430Srstone * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33279430Srstone * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34279430Srstone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35279430Srstone * SUCH DAMAGE. 36279430Srstone * 37279430Srstone * @(#)igmp.c 8.1 (Berkeley) 7/19/93 38279430Srstone * $Id: igmp.c,v 1.6 1994/10/31 06:36:47 pst Exp $ 39279430Srstone */ 40279430Srstone 41279430Srstone/* 42279430Srstone * Internet Group Management Protocol (IGMP) routines. 43279430Srstone * 44279430Srstone * Written by Steve Deering, Stanford, May 1988. 45279430Srstone * Modified by Rosen Sharma, Stanford, Aug 1994. 46279430Srstone * 47279430Srstone * MULTICAST 1.4 48292637Sngie */ 49279430Srstone 50279430Srstone#include <sys/param.h> 51279430Srstone#include <sys/systm.h> 52279430Srstone#include <sys/mbuf.h> 53279430Srstone#include <sys/socket.h> 54279430Srstone#include <sys/protosw.h> 55279430Srstone#include <sys/proc.h> /* XXX needed for sysctl.h */ 56279430Srstone#include <vm/vm.h> /* XXX needed for sysctl.h */ 57279430Srstone#include <sys/sysctl.h> 58279430Srstone 59279430Srstone#include <net/if.h> 60279430Srstone#include <net/route.h> 61279430Srstone 62279430Srstone#include <netinet/in.h> 63292637Sngie#include <netinet/in_var.h> 64279430Srstone#include <netinet/in_systm.h> 65279430Srstone#include <netinet/ip.h> 66279430Srstone#include <netinet/ip_var.h> 67279430Srstone#include <netinet/igmp.h> 68292637Sngie#include <netinet/igmp_var.h> 69279430Srstone 70279430Srstoneextern struct ifnet loif; 71279430Srstone 72279430Srstonestruct igmpstat igmpstat; 73279430Srstone 74279430Srstonestatic int igmp_timers_are_running = 0; 75279430Srstonestatic u_long igmp_all_hosts_group; 76279430Srstonestatic struct router_info *Head = 0; 77279430Srstone 78279430Srstonestatic void igmp_sendpkt(struct in_multi *, int); 79279430Srstonestatic void igmp_sendleave(struct in_multi *); 80279430Srstone 81279430Srstonevoid 82279430Srstoneigmp_init() 83279430Srstone{ 84279430Srstone /* 85279430Srstone * To avoid byte-swapping the same value over and over again. 86279430Srstone */ 87292637Sngie igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); 88279430Srstone Head = (struct router_info *) 0; 89279430Srstone} 90279430Srstone 91279430Srstoneint 92279430Srstonefill_rti(inm) 93279430Srstone struct in_multi *inm; 94279430Srstone{ 95279430Srstone register struct router_info *rti = Head; 96279430Srstone 97279430Srstone#ifdef IGMP_DEBUG 98279430Srstone printf("[igmp.c, _fill_rti] --> entering \n"); 99279430Srstone#endif 100279430Srstone while (rti) { 101279430Srstone if (rti->ifp == inm->inm_ifp){ /* ? is it ok to compare */ 102292637Sngie /* pointers */ 103279430Srstone inm->inm_rti = rti; 104279430Srstone#ifdef IGMP_DEBUG 105279430Srstone printf("[igmp.c, _fill_rti] --> found old entry \n"); 106292637Sngie#endif 107279430Srstone if (rti->type == IGMP_OLD_ROUTER) 108279430Srstone return IGMP_HOST_MEMBERSHIP_REPORT; 109279430Srstone else 110279430Srstone return IGMP_HOST_NEW_MEMBERSHIP_REPORT; 111279430Srstone } 112279430Srstone rti = rti->next; 113279430Srstone } 114279430Srstone MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT); 115279430Srstone rti->ifp = inm->inm_ifp; 116279430Srstone rti->type = IGMP_NEW_ROUTER; 117279430Srstone rti->time = IGMP_AGE_THRESHOLD; 118279430Srstone rti->next = Head; 119279430Srstone Head = rti; 120279430Srstone inm->inm_rti = rti; 121279430Srstone#ifdef IGMP_DEBUG 122279430Srstone printf("[igmp.c, _fill_rti] --> created new entry \n"); 123279430Srstone#endif 124279430Srstone return IGMP_HOST_NEW_MEMBERSHIP_REPORT; 125279430Srstone} 126292637Sngie 127279430Srstonestruct router_info * 128279430Srstonefind_rti(ifp) 129279430Srstone struct ifnet *ifp; 130279430Srstone{ 131279430Srstone register struct router_info *rti = Head; 132279430Srstone 133279430Srstone#ifdef IGMP_DEBUG 134279430Srstone printf("[igmp.c, _find_rti] --> entering \n"); 135279430Srstone#endif 136279430Srstone while (rti) { 137279430Srstone if (rti->ifp == ifp){ /* ? is it ok to compare pointers */ 138279430Srstone#ifdef IGMP_DEBUG 139279430Srstone printf("[igmp.c, _find_rti] --> found old entry \n"); 140279430Srstone#endif 141279430Srstone return rti; 142279430Srstone } 143279430Srstone rti = rti->next; 144292637Sngie } 145279430Srstone MALLOC(rti, struct router_info *, sizeof *rti, M_MRTABLE, M_NOWAIT); 146279430Srstone rti->ifp = ifp; 147279430Srstone rti->type = IGMP_NEW_ROUTER; 148279430Srstone rti->time = IGMP_AGE_THRESHOLD; 149279430Srstone rti->next = Head; 150292637Sngie Head = rti; 151319370Sngie#ifdef IGMP_DEBUG 152279430Srstone printf("[igmp.c, _find_rti] --> created an entry \n"); 153279430Srstone#endif 154279430Srstone return rti; 155279430Srstone} 156279430Srstone 157279430Srstonevoid 158279430Srstoneigmp_input(m, iphlen) 159279430Srstone register struct mbuf *m; 160279430Srstone register int iphlen; 161279430Srstone{ 162279430Srstone register struct igmp *igmp; 163279430Srstone register struct ip *ip; 164279430Srstone register int igmplen; 165279430Srstone register struct ifnet *ifp = m->m_pkthdr.rcvif; 166279430Srstone register int minlen; 167279430Srstone register struct in_multi *inm; 168279430Srstone register struct in_ifaddr *ia; 169279430Srstone struct in_multistep step; 170279430Srstone struct router_info *rti; 171279430Srstone 172279430Srstone static int timer; /** timer value in the igmp query header **/ 173279430Srstone 174279430Srstone ++igmpstat.igps_rcv_total; 175279430Srstone 176279430Srstone ip = mtod(m, struct ip *); 177279430Srstone igmplen = ip->ip_len; 178279430Srstone 179279430Srstone /* 180279430Srstone * Validate lengths 181279430Srstone */ 182279430Srstone if (igmplen < IGMP_MINLEN) { 183279430Srstone ++igmpstat.igps_rcv_tooshort; 184279430Srstone m_freem(m); 185279430Srstone return; 186279430Srstone } 187279430Srstone minlen = iphlen + IGMP_MINLEN; 188279430Srstone if ((m->m_flags & M_EXT || m->m_len < minlen) && 189279430Srstone (m = m_pullup(m, minlen)) == 0) { 190279430Srstone ++igmpstat.igps_rcv_tooshort; 191279430Srstone return; 192292637Sngie } 193279430Srstone 194279430Srstone /* 195279430Srstone * Validate checksum 196279430Srstone */ 197279430Srstone m->m_data += iphlen; 198279430Srstone m->m_len -= iphlen; 199279430Srstone igmp = mtod(m, struct igmp *); 200279430Srstone if (in_cksum(m, igmplen)) { 201279430Srstone ++igmpstat.igps_rcv_badsum; 202279430Srstone m_freem(m); 203279430Srstone return; 204279430Srstone } 205279430Srstone m->m_data -= iphlen; 206279430Srstone m->m_len += iphlen; 207279430Srstone 208279430Srstone ip = mtod(m, struct ip *); 209279430Srstone timer = ntohs(igmp->igmp_code); 210279430Srstone rti = find_rti(ifp); 211279430Srstone 212279430Srstone switch (igmp->igmp_type) { 213279430Srstone 214279430Srstone case IGMP_HOST_MEMBERSHIP_QUERY: 215279430Srstone ++igmpstat.igps_rcv_queries; 216279430Srstone 217279430Srstone if (ifp == &loif) 218279430Srstone break; 219279430Srstone 220279430Srstone if (igmp->igmp_code == 0) { 221279430Srstone 222279430Srstone rti->type = IGMP_OLD_ROUTER; rti->time = 0; 223279430Srstone 224279430Srstone /* 225279430Srstone ** Do exactly as RFC 1112 says 226279430Srstone */ 227279430Srstone 228279430Srstone if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 229279430Srstone ++igmpstat.igps_rcv_badqueries; 230279430Srstone m_freem(m); 231279430Srstone return; 232279430Srstone } 233279430Srstone 234279430Srstone /* 235279430Srstone * Start the timers in all of our membership records for 236279430Srstone * the interface on which the query arrived, except those 237279430Srstone * that are already running and those that belong to the 238279430Srstone * "all-hosts" group. 239279430Srstone */ 240279430Srstone IN_FIRST_MULTI(step, inm); 241279430Srstone while (inm != NULL) { 242279430Srstone if (inm->inm_ifp == ifp 243279430Srstone && inm->inm_timer == 0 244292637Sngie && inm->inm_addr.s_addr 245292637Sngie != igmp_all_hosts_group) { 246279430Srstone 247279430Srstone inm->inm_state = IGMP_DELAYING_MEMBER; 248279430Srstone inm->inm_timer = IGMP_RANDOM_DELAY( 249279430Srstone IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ ); 250279430Srstone 251279430Srstone igmp_timers_are_running = 1; 252279430Srstone } 253279430Srstone IN_NEXT_MULTI(step, inm); 254279430Srstone } 255279430Srstone } else { 256279430Srstone /* 257279430Srstone ** New Router 258279430Srstone */ 259292637Sngie 260292637Sngie if (ip->ip_dst.s_addr != igmp_all_hosts_group) { 261279430Srstone if (!(m->m_flags & M_MCAST)) { 262279430Srstone ++igmpstat.igps_rcv_badqueries; 263279430Srstone m_freem(m); 264279430Srstone return; 265279430Srstone } 266279430Srstone } 267292634Sngie if (ip->ip_dst.s_addr == igmp_all_hosts_group) { 268292634Sngie 269292634Sngie /* 270292634Sngie * - Start the timers in all of our membership records 271292634Sngie * for the interface on which the query arrived 272292634Sngie * excl. those that belong to the "all-hosts" group. 273292634Sngie * - For timers already running check if they need to 274292634Sngie * be reset. 275292634Sngie * - Use the igmp->igmp_code filed as the maximum 276292634Sngie * delay possible 277292634Sngie */ 278292634Sngie IN_FIRST_MULTI(step, inm); 279292634Sngie while (inm != NULL){ 280292634Sngie switch(inm->inm_state){ 281292634Sngie case IGMP_IDLE_MEMBER: 282292634Sngie case IGMP_LAZY_MEMBER: 283292634Sngie case IGMP_AWAKENING_MEMBER: 284292634Sngie if (inm->inm_ifp == ifp && 285292634Sngie inm->inm_addr.s_addr != 286292634Sngie igmp_all_hosts_group) { 287292634Sngie inm->inm_timer = IGMP_RANDOM_DELAY(timer); 288292634Sngie igmp_timers_are_running = 1; 289292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 290292634Sngie } 291292634Sngie break; 292292634Sngie case IGMP_DELAYING_MEMBER: 293292634Sngie if (inm->inm_ifp == ifp && 294292634Sngie (inm->inm_timer > 295292634Sngie timer * PR_FASTHZ / IGMP_TIMER_SCALE) 296292634Sngie && 297292634Sngie inm->inm_addr.s_addr != 298292634Sngie igmp_all_hosts_group) { 299292634Sngie inm->inm_timer = IGMP_RANDOM_DELAY(timer); 300292634Sngie igmp_timers_are_running = 1; 301292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 302292634Sngie } 303292634Sngie break; 304292634Sngie case IGMP_SLEEPING_MEMBER: 305292634Sngie inm->inm_state = IGMP_AWAKENING_MEMBER; 306292634Sngie break; 307292634Sngie } 308292634Sngie IN_NEXT_MULTI(step, inm); 309292634Sngie } 310292634Sngie } else { 311292634Sngie /* 312292634Sngie ** group specific query 313292634Sngie */ 314292634Sngie 315292634Sngie IN_FIRST_MULTI(step, inm); 316292634Sngie while (inm != NULL) { 317292634Sngie if (inm->inm_addr.s_addr == ip->ip_dst.s_addr) { 318292634Sngie switch(inm->inm_state ){ 319292634Sngie case IGMP_IDLE_MEMBER: 320292634Sngie case IGMP_LAZY_MEMBER: 321292634Sngie case IGMP_AWAKENING_MEMBER: 322292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 323292634Sngie if (inm->inm_ifp == ifp ) { 324292634Sngie inm->inm_timer = IGMP_RANDOM_DELAY(timer); 325292634Sngie igmp_timers_are_running = 1; 326292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 327292634Sngie } 328292634Sngie break; 329292634Sngie case IGMP_DELAYING_MEMBER: 330292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 331292634Sngie if (inm->inm_ifp == ifp && 332292634Sngie (inm->inm_timer > 333292634Sngie timer * PR_FASTHZ / IGMP_TIMER_SCALE) ) { 334292634Sngie inm->inm_timer = IGMP_RANDOM_DELAY(timer); 335292634Sngie igmp_timers_are_running = 1; 336292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 337292634Sngie } 338292634Sngie break; 339292634Sngie case IGMP_SLEEPING_MEMBER: 340292634Sngie inm->inm_state = IGMP_AWAKENING_MEMBER; 341292634Sngie break; 342292634Sngie } 343292634Sngie } 344292634Sngie IN_NEXT_MULTI(step, inm); 345292634Sngie } 346292634Sngie } 347292634Sngie } 348292634Sngie break; 349292634Sngie 350292634Sngie case IGMP_HOST_MEMBERSHIP_REPORT: 351292634Sngie ++igmpstat.igps_rcv_reports; 352292634Sngie 353292634Sngie if (ifp == &loif) 354292634Sngie break; 355292634Sngie 356292634Sngie if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 357292634Sngie igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 358292634Sngie ++igmpstat.igps_rcv_badreports; 359292634Sngie m_freem(m); 360292634Sngie return; 361292634Sngie } 362292634Sngie 363292634Sngie /* 364292634Sngie * KLUDGE: if the IP source address of the report has an 365292634Sngie * unspecified (i.e., zero) subnet number, as is allowed for 366292634Sngie * a booting host, replace it with the correct subnet number 367292634Sngie * so that a process-level multicast routing demon can 368292634Sngie * determine which subnet it arrived from. This is necessary 369292634Sngie * to compensate for the lack of any way for a process to 370292634Sngie * determine the arrival interface of an incoming packet. 371292634Sngie */ 372292634Sngie if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 373292634Sngie IFP_TO_IA(ifp, ia); 374292634Sngie if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 375292634Sngie } 376292634Sngie 377292634Sngie /* 378292634Sngie * If we belong to the group being reported, stop 379292634Sngie * our timer for that group. 380292634Sngie */ 381292634Sngie IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 382292634Sngie if (inm != NULL) { 383292634Sngie inm->inm_timer = 0; 384292634Sngie ++igmpstat.igps_rcv_ourreports; 385292634Sngie } 386292634Sngie 387292634Sngie if (inm != NULL) { 388292634Sngie inm->inm_timer = 0; 389292634Sngie ++igmpstat.igps_rcv_ourreports; 390292634Sngie 391292634Sngie switch(inm->inm_state){ 392292634Sngie case IGMP_IDLE_MEMBER: 393292634Sngie case IGMP_LAZY_MEMBER: 394292634Sngie case IGMP_AWAKENING_MEMBER: 395292634Sngie case IGMP_SLEEPING_MEMBER: 396292634Sngie inm->inm_state = IGMP_SLEEPING_MEMBER; 397292634Sngie break; 398292634Sngie case IGMP_DELAYING_MEMBER: 399292634Sngie /** check this out - this was if (oldrouter) **/ 400292634Sngie if (inm->inm_rti->type == IGMP_OLD_ROUTER) 401292634Sngie inm->inm_state = IGMP_LAZY_MEMBER; 402292634Sngie else inm->inm_state = IGMP_SLEEPING_MEMBER; 403292634Sngie break; 404292634Sngie } 405292634Sngie } 406292634Sngie 407292634Sngie break; 408292634Sngie 409292634Sngie case IGMP_HOST_NEW_MEMBERSHIP_REPORT: 410292634Sngie /* 411292634Sngie * an new report 412292634Sngie */ 413292634Sngie ++igmpstat.igps_rcv_reports; 414292634Sngie 415292634Sngie if (ifp == &loif) 416292634Sngie break; 417292634Sngie 418292634Sngie if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) || 419292634Sngie igmp->igmp_group.s_addr != ip->ip_dst.s_addr) { 420292634Sngie ++igmpstat.igps_rcv_badreports; 421292634Sngie m_freem(m); 422292634Sngie return; 423292634Sngie } 424292634Sngie 425292634Sngie /* 426292634Sngie * KLUDGE: if the IP source address of the report has an 427292634Sngie * unspecified (i.e., zero) subnet number, as is allowed for 428292634Sngie * a booting host, replace it with the correct subnet number 429292634Sngie * so that a process-level multicast routing demon can 430292634Sngie * determine which subnet it arrived from. This is necessary 431292634Sngie * to compensate for the lack of any way for a process to 432292634Sngie * determine the arrival interface of an incoming packet. 433292634Sngie */ 434292634Sngie if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) { 435292634Sngie IFP_TO_IA(ifp, ia); 436292634Sngie if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); 437292634Sngie } 438292634Sngie 439292634Sngie /* 440292634Sngie * If we belong to the group being reported, stop 441292634Sngie * our timer for that group. 442292634Sngie */ 443292637Sngie IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); 444292634Sngie if (inm != NULL) { 445292634Sngie inm->inm_timer = 0; 446292634Sngie ++igmpstat.igps_rcv_ourreports; 447292634Sngie 448292634Sngie switch(inm->inm_state){ 449292634Sngie case IGMP_DELAYING_MEMBER: 450292634Sngie case IGMP_IDLE_MEMBER: 451292634Sngie inm->inm_state = IGMP_LAZY_MEMBER; 452292634Sngie break; 453292634Sngie case IGMP_AWAKENING_MEMBER: 454292634Sngie inm->inm_state = IGMP_LAZY_MEMBER; 455292634Sngie break; 456292634Sngie case IGMP_LAZY_MEMBER: 457292634Sngie case IGMP_SLEEPING_MEMBER: 458292634Sngie break; 459292634Sngie } 460292634Sngie } 461292634Sngie } 462292634Sngie 463292634Sngie /* 464292634Sngie * Pass all valid IGMP packets up to any process(es) listening 465292634Sngie * on a raw IGMP socket. 466292634Sngie */ 467292634Sngie rip_input(m); 468292634Sngie} 469292634Sngie 470292634Sngievoid 471292634Sngieigmp_joingroup(inm) 472292634Sngie struct in_multi *inm; 473292634Sngie{ 474292634Sngie register int s = splnet(); 475292634Sngie 476292634Sngie inm->inm_state = IGMP_IDLE_MEMBER; 477292634Sngie 478292634Sngie if (inm->inm_addr.s_addr == igmp_all_hosts_group || 479292634Sngie inm->inm_ifp == &loif) 480292634Sngie inm->inm_timer = 0; 481292634Sngie else { 482292634Sngie igmp_sendpkt(inm,fill_rti(inm)); 483292634Sngie inm->inm_timer = IGMP_RANDOM_DELAY( 484292634Sngie IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); 485292634Sngie inm->inm_state = IGMP_DELAYING_MEMBER; 486292634Sngie igmp_timers_are_running = 1; 487292634Sngie } 488292634Sngie splx(s); 489292634Sngie} 490292634Sngie 491292634Sngievoid 492292634Sngieigmp_leavegroup(inm) 493292634Sngie struct in_multi *inm; 494292634Sngie{ 495292634Sngie /* 496292634Sngie * No action required on leaving a group. 497292634Sngie */ 498292634Sngie switch(inm->inm_state){ 499292634Sngie case IGMP_DELAYING_MEMBER: 500292634Sngie case IGMP_IDLE_MEMBER: 501292634Sngie if (!(inm->inm_addr.s_addr == igmp_all_hosts_group || 502292634Sngie inm->inm_ifp == &loif)) 503292634Sngie if (inm->inm_rti->type != IGMP_OLD_ROUTER) 504292634Sngie igmp_sendleave(inm); 505292634Sngie break; 506292634Sngie case IGMP_LAZY_MEMBER: 507292634Sngie case IGMP_AWAKENING_MEMBER: 508292634Sngie case IGMP_SLEEPING_MEMBER: 509292634Sngie break; 510292634Sngie } 511292634Sngie} 512292634Sngie 513292634Sngievoid 514292634Sngieigmp_fasttimo() 515292634Sngie{ 516292634Sngie register struct in_multi *inm; 517292634Sngie register int s; 518292634Sngie struct in_multistep step; 519292634Sngie 520292634Sngie /* 521292634Sngie * Quick check to see if any work needs to be done, in order 522292634Sngie * to minimize the overhead of fasttimo processing. 523292634Sngie */ 524292634Sngie if (!igmp_timers_are_running) 525292634Sngie return; 526292634Sngie 527292634Sngie s = splnet(); 528292634Sngie igmp_timers_are_running = 0; 529292634Sngie IN_FIRST_MULTI(step, inm); 530292634Sngie while (inm != NULL) { 531292634Sngie if (inm->inm_timer == 0) { 532292634Sngie /* do nothing */ 533292634Sngie } else if (--inm->inm_timer == 0) { 534292634Sngie if (inm->inm_state == IGMP_DELAYING_MEMBER) { 535292634Sngie if (inm->inm_rti->type == IGMP_OLD_ROUTER) 536292634Sngie igmp_sendpkt(inm, IGMP_HOST_MEMBERSHIP_REPORT); 537292634Sngie else 538292634Sngie igmp_sendpkt(inm, IGMP_HOST_NEW_MEMBERSHIP_REPORT); 539279430Srstone inm->inm_state = IGMP_IDLE_MEMBER; 540279430Srstone } 541279430Srstone } else { 542279430Srstone igmp_timers_are_running = 1; 543279430Srstone } 544279430Srstone IN_NEXT_MULTI(step, inm); 545279430Srstone } 546279430Srstone splx(s); 547279430Srstone} 548279430Srstone 549279430Srstonevoid 550279430Srstoneigmp_slowtimo() 551292634Sngie{ 552292634Sngie int s = splnet(); 553292634Sngie register struct router_info *rti = Head; 554292634Sngie 555292634Sngie#ifdef IGMP_DEBUG 556292634Sngie printf("[igmp.c,_slowtimo] -- > entering \n"); 557292634Sngie#endif 558292634Sngie while (rti) { 559292634Sngie rti->time ++; 560292634Sngie if (rti->time >= IGMP_AGE_THRESHOLD){ 561292634Sngie rti->type = IGMP_NEW_ROUTER; 562292634Sngie rti->time = IGMP_AGE_THRESHOLD; 563292634Sngie } 564292634Sngie rti = rti->next; 565292634Sngie } 566292634Sngie#ifdef IGMP_DEBUG 567279430Srstone printf("[igmp.c,_slowtimo] -- > exiting \n"); 568#endif 569 splx(s); 570} 571 572static void 573igmp_sendpkt(inm, type) 574 struct in_multi *inm; 575 int type; 576{ 577 struct mbuf *m; 578 struct igmp *igmp; 579 struct ip *ip; 580 struct ip_moptions *imo; 581 582 MGETHDR(m, M_DONTWAIT, MT_HEADER); 583 if (m == NULL) 584 return; 585 586 MALLOC(imo, struct ip_moptions *, sizeof *imo, M_IPMOPTS, M_DONTWAIT); 587 if (!imo) { 588 m_free(m); 589 return; 590 } 591 592 m->m_pkthdr.rcvif = &loif; 593 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; 594 MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip)); 595 m->m_data += sizeof(struct ip); 596 m->m_len = IGMP_MINLEN; 597 igmp = mtod(m, struct igmp *); 598 igmp->igmp_type = type; 599 igmp->igmp_code = 0; 600 igmp->igmp_group = inm->inm_addr; 601 igmp->igmp_cksum = 0; 602 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); 603 604 m->m_data -= sizeof(struct ip); 605 m->m_len += sizeof(struct ip); 606 ip = mtod(m, struct ip *); 607 ip->ip_tos = 0; 608 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; 609 ip->ip_off = 0; 610 ip->ip_p = IPPROTO_IGMP; 611 ip->ip_src.s_addr = INADDR_ANY; 612 ip->ip_dst = igmp->igmp_group; 613 614 imo->imo_multicast_ifp = inm->inm_ifp; 615 imo->imo_multicast_ttl = 1; 616 /* 617 * Request loopback of the report if we are acting as a multicast 618 * router, so that the process-level routing demon can hear it. 619 */ 620 imo->imo_multicast_loop = (ip_mrouter != NULL); 621 622 ip_output(m, (struct mbuf *)0, (struct route *)0, 0, imo); 623 624 FREE(imo, M_IPMOPTS); 625 ++igmpstat.igps_snd_reports; 626 627} 628 629static void 630igmp_sendleave(inm) 631 struct in_multi *inm; 632{ 633 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE); 634} 635 636int 637igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 638 void *newp, size_t newlen) 639{ 640 /* All sysctl names at this level are terminal. */ 641 if (namelen != 1) 642 return ENOTDIR; /* XXX overloaded */ 643 644 switch(name[0]) { 645 case IGMPCTL_STATS: 646 return sysctl_rdstruct(oldp, oldlenp, newp, &igmpstat, 647 sizeof igmpstat); 648 default: 649 return ENOPROTOOPT; 650 } 651} 652