|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * Copyright (c) 1988 Stephen Deering. ! 24: * Copyright (c) 1992, 1993 ! 25: * The Regents of the University of California. All rights reserved. ! 26: * ! 27: * This code is derived from software contributed to Berkeley by ! 28: * Stephen Deering of Stanford University. ! 29: * ! 30: * Redistribution and use in source and binary forms, with or without ! 31: * modification, are permitted provided that the following conditions ! 32: * are met: ! 33: * 1. Redistributions of source code must retain the above copyright ! 34: * notice, this list of conditions and the following disclaimer. ! 35: * 2. Redistributions in binary form must reproduce the above copyright ! 36: * notice, this list of conditions and the following disclaimer in the ! 37: * documentation and/or other materials provided with the distribution. ! 38: * 3. All advertising materials mentioning features or use of this software ! 39: * must display the following acknowledgement: ! 40: * This product includes software developed by the University of ! 41: * California, Berkeley and its contributors. ! 42: * 4. Neither the name of the University nor the names of its contributors ! 43: * may be used to endorse or promote products derived from this software ! 44: * without specific prior written permission. ! 45: * ! 46: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 47: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 48: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 49: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 50: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 51: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 52: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 53: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 54: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 55: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 56: * SUCH DAMAGE. ! 57: * ! 58: * @(#)igmp.c 8.1 (Berkeley) 7/19/93 ! 59: */ ! 60: ! 61: /* ! 62: * Internet Group Management Protocol (IGMP) routines. ! 63: * ! 64: * Written by Steve Deering, Stanford, May 1988. ! 65: * Modified by Rosen Sharma, Stanford, Aug 1994. ! 66: * Modified by Bill Fenner, Xerox PARC, Feb 1995. ! 67: * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995. ! 68: * ! 69: * MULTICAST Revision: 3.5.1.4 ! 70: */ ! 71: ! 72: #include <sys/param.h> ! 73: #include <sys/systm.h> ! 74: #include <sys/malloc.h> ! 75: #include <sys/mbuf.h> ! 76: #include <sys/socket.h> ! 77: #include <sys/protosw.h> ! 78: #include <sys/kernel.h> ! 79: #include <sys/sysctl.h> ! 80: ! 81: #include <net/if.h> ! 82: #include <net/route.h> ! 83: ! 84: #include <netinet/in.h> ! 85: #include <netinet/in_var.h> ! 86: #include <netinet/in_systm.h> ! 87: #include <netinet/ip.h> ! 88: #include <netinet/ip_var.h> ! 89: #include <netinet/igmp.h> ! 90: #include <netinet/igmp_var.h> ! 91: ! 92: static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state"); ! 93: ! 94: static struct router_info * ! 95: find_rti __P((struct ifnet *ifp)); ! 96: ! 97: static struct igmpstat igmpstat; ! 98: ! 99: SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD, ! 100: &igmpstat, igmpstat, ""); ! 101: ! 102: static int igmp_timers_are_running; ! 103: static u_long igmp_all_hosts_group; ! 104: static u_long igmp_all_rtrs_group; ! 105: static struct mbuf *router_alert; ! 106: static struct router_info *Head; ! 107: ! 108: static void igmp_sendpkt __P((struct in_multi *, int, unsigned long)); ! 109: ! 110: void ! 111: igmp_init() ! 112: { ! 113: struct ipoption *ra; ! 114: ! 115: /* ! 116: * To avoid byte-swapping the same value over and over again. ! 117: */ ! 118: igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP); ! 119: igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP); ! 120: ! 121: igmp_timers_are_running = 0; ! 122: ! 123: /* ! 124: * Construct a Router Alert option to use in outgoing packets ! 125: */ ! 126: MGET(router_alert, M_DONTWAIT, MT_DATA); ! 127: ra = mtod(router_alert, struct ipoption *); ! 128: ra->ipopt_dst.s_addr = 0; ! 129: ra->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */ ! 130: ra->ipopt_list[1] = 0x04; /* 4 bytes long */ ! 131: ra->ipopt_list[2] = 0x00; ! 132: ra->ipopt_list[3] = 0x00; ! 133: router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1]; ! 134: ! 135: Head = (struct router_info *) 0; ! 136: } ! 137: ! 138: static struct router_info * ! 139: find_rti(ifp) ! 140: struct ifnet *ifp; ! 141: { ! 142: register struct router_info *rti = Head; ! 143: ! 144: #if IGMP_DEBUG ! 145: printf("[igmp.c, _find_rti] --> entering \n"); ! 146: #endif ! 147: while (rti) { ! 148: if (rti->rti_ifp == ifp) { ! 149: #if IGMP_DEBUG ! 150: printf("[igmp.c, _find_rti] --> found old entry \n"); ! 151: #endif ! 152: return rti; ! 153: } ! 154: rti = rti->rti_next; ! 155: } ! 156: ! 157: #if ISFB31 ! 158: MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, M_NOWAIT); ! 159: #else ! 160: MALLOC(rti, struct router_info *, sizeof *rti, M_TEMP, M_NOWAIT); ! 161: #endif ! 162: rti->rti_ifp = ifp; ! 163: rti->rti_type = IGMP_V2_ROUTER; ! 164: rti->rti_time = 0; ! 165: rti->rti_next = Head; ! 166: Head = rti; ! 167: #if IGMP_DEBUG ! 168: printf("[igmp.c, _find_rti] --> created an entry \n"); ! 169: #endif ! 170: return rti; ! 171: } ! 172: ! 173: void ! 174: igmp_input(m, iphlen) ! 175: register struct mbuf *m; ! 176: register int iphlen; ! 177: { ! 178: register struct igmp *igmp; ! 179: register struct ip *ip; ! 180: register int igmplen; ! 181: register struct ifnet *ifp = m->m_pkthdr.rcvif; ! 182: register int minlen; ! 183: register struct in_multi *inm; ! 184: register struct in_ifaddr *ia; ! 185: struct in_multistep step; ! 186: struct router_info *rti; ! 187: ! 188: int timer; /** timer value in the igmp query header **/ ! 189: ! 190: ++igmpstat.igps_rcv_total; ! 191: ! 192: ip = mtod(m, struct ip *); ! 193: igmplen = ip->ip_len; ! 194: ! 195: /* ! 196: * Validate lengths ! 197: */ ! 198: if (igmplen < IGMP_MINLEN) { ! 199: ++igmpstat.igps_rcv_tooshort; ! 200: m_freem(m); ! 201: return; ! 202: } ! 203: minlen = iphlen + IGMP_MINLEN; ! 204: if ((m->m_flags & M_EXT || m->m_len < minlen) && ! 205: (m = m_pullup(m, minlen)) == 0) { ! 206: ++igmpstat.igps_rcv_tooshort; ! 207: return; ! 208: } ! 209: ! 210: /* ! 211: * Validate checksum ! 212: */ ! 213: m->m_data += iphlen; ! 214: m->m_len -= iphlen; ! 215: igmp = mtod(m, struct igmp *); ! 216: if (in_cksum(m, igmplen)) { ! 217: ++igmpstat.igps_rcv_badsum; ! 218: m_freem(m); ! 219: return; ! 220: } ! 221: m->m_data -= iphlen; ! 222: m->m_len += iphlen; ! 223: ! 224: ip = mtod(m, struct ip *); ! 225: timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; ! 226: if (timer == 0) ! 227: timer = 1; ! 228: rti = find_rti(ifp); ! 229: ! 230: /* ! 231: * In the IGMPv2 specification, there are 3 states and a flag. ! 232: * ! 233: * In Non-Member state, we simply don't have a membership record. ! 234: * In Delaying Member state, our timer is running (inm->inm_timer) ! 235: * In Idle Member state, our timer is not running (inm->inm_timer==0) ! 236: * ! 237: * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if ! 238: * we have heard a report from another member, or IGMP_IREPORTEDLAST ! 239: * if I sent the last report. ! 240: */ ! 241: switch (igmp->igmp_type) { ! 242: ! 243: case IGMP_MEMBERSHIP_QUERY: ! 244: ++igmpstat.igps_rcv_queries; ! 245: ! 246: if (ifp->if_flags & IFF_LOOPBACK) ! 247: break; ! 248: ! 249: if (igmp->igmp_code == 0) { ! 250: /* ! 251: * Old router. Remember that the querier on this ! 252: * interface is old, and set the timer to the ! 253: * value in RFC 1112. ! 254: */ ! 255: ! 256: rti->rti_type = IGMP_V1_ROUTER; ! 257: rti->rti_time = 0; ! 258: ! 259: timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; ! 260: ! 261: if (ip->ip_dst.s_addr != igmp_all_hosts_group || ! 262: igmp->igmp_group.s_addr != 0) { ! 263: ++igmpstat.igps_rcv_badqueries; ! 264: m_freem(m); ! 265: return; ! 266: } ! 267: } else { ! 268: /* ! 269: * New router. Simply do the new validity check. ! 270: */ ! 271: ! 272: if (igmp->igmp_group.s_addr != 0 && ! 273: !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { ! 274: ++igmpstat.igps_rcv_badqueries; ! 275: m_freem(m); ! 276: return; ! 277: } ! 278: } ! 279: ! 280: /* ! 281: * - Start the timers in all of our membership records ! 282: * that the query applies to for the interface on ! 283: * which the query arrived excl. those that belong ! 284: * to the "all-hosts" group (224.0.0.1). ! 285: * - Restart any timer that is already running but has ! 286: * a value longer than the requested timeout. ! 287: * - Use the value specified in the query message as ! 288: * the maximum timeout. ! 289: */ ! 290: IN_FIRST_MULTI(step, inm); ! 291: while (inm != NULL) { ! 292: if (inm->inm_ifp == ifp && ! 293: inm->inm_addr.s_addr != igmp_all_hosts_group && ! 294: (igmp->igmp_group.s_addr == 0 || ! 295: igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { ! 296: if (inm->inm_timer == 0 || ! 297: inm->inm_timer > timer) { ! 298: inm->inm_timer = ! 299: IGMP_RANDOM_DELAY(timer); ! 300: igmp_timers_are_running = 1; ! 301: } ! 302: } ! 303: IN_NEXT_MULTI(step, inm); ! 304: } ! 305: ! 306: break; ! 307: ! 308: case IGMP_V1_MEMBERSHIP_REPORT: ! 309: case IGMP_V2_MEMBERSHIP_REPORT: ! 310: /* ! 311: * For fast leave to work, we have to know that we are the ! 312: * last person to send a report for this group. Reports ! 313: * can potentially get looped back if we are a multicast ! 314: * router, so discard reports sourced by me. ! 315: */ ! 316: IFP_TO_IA(ifp, ia); ! 317: if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) ! 318: break; ! 319: ! 320: ++igmpstat.igps_rcv_reports; ! 321: ! 322: if (ifp->if_flags & IFF_LOOPBACK) ! 323: break; ! 324: ! 325: if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { ! 326: ++igmpstat.igps_rcv_badreports; ! 327: m_freem(m); ! 328: return; ! 329: } ! 330: ! 331: /* ! 332: * KLUDGE: if the IP source address of the report has an ! 333: * unspecified (i.e., zero) subnet number, as is allowed for ! 334: * a booting host, replace it with the correct subnet number ! 335: * so that a process-level multicast routing demon can ! 336: * determine which subnet it arrived from. This is necessary ! 337: * to compensate for the lack of any way for a process to ! 338: * determine the arrival interface of an incoming packet. ! 339: */ ! 340: if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) ! 341: if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); ! 342: ! 343: /* ! 344: * If we belong to the group being reported, stop ! 345: * our timer for that group. ! 346: */ ! 347: IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); ! 348: ! 349: if (inm != NULL) { ! 350: inm->inm_timer = 0; ! 351: ++igmpstat.igps_rcv_ourreports; ! 352: ! 353: inm->inm_state = IGMP_OTHERMEMBER; ! 354: } ! 355: ! 356: break; ! 357: } ! 358: ! 359: /* ! 360: * Pass all valid IGMP packets up to any process(es) listening ! 361: * on a raw IGMP socket. ! 362: */ ! 363: rip_input(m, iphlen); ! 364: } ! 365: ! 366: void ! 367: igmp_joingroup(inm) ! 368: struct in_multi *inm; ! 369: { ! 370: int s = splnet(); ! 371: ! 372: if (inm->inm_addr.s_addr == igmp_all_hosts_group ! 373: || inm->inm_ifp->if_flags & IFF_LOOPBACK) { ! 374: inm->inm_timer = 0; ! 375: inm->inm_state = IGMP_OTHERMEMBER; ! 376: } else { ! 377: inm->inm_rti = find_rti(inm->inm_ifp); ! 378: igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); ! 379: inm->inm_timer = IGMP_RANDOM_DELAY( ! 380: IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); ! 381: inm->inm_state = IGMP_IREPORTEDLAST; ! 382: igmp_timers_are_running = 1; ! 383: } ! 384: splx(s); ! 385: } ! 386: ! 387: void ! 388: igmp_leavegroup(inm) ! 389: struct in_multi *inm; ! 390: { ! 391: if (inm->inm_state == IGMP_IREPORTEDLAST && ! 392: inm->inm_addr.s_addr != igmp_all_hosts_group && ! 393: !(inm->inm_ifp->if_flags & IFF_LOOPBACK) && ! 394: inm->inm_rti->rti_type != IGMP_V1_ROUTER) ! 395: igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group); ! 396: } ! 397: ! 398: void ! 399: igmp_fasttimo() ! 400: { ! 401: register struct in_multi *inm; ! 402: struct in_multistep step; ! 403: int s; ! 404: ! 405: /* ! 406: * Quick check to see if any work needs to be done, in order ! 407: * to minimize the overhead of fasttimo processing. ! 408: */ ! 409: ! 410: if (!igmp_timers_are_running) ! 411: return; ! 412: ! 413: s = splnet(); ! 414: igmp_timers_are_running = 0; ! 415: IN_FIRST_MULTI(step, inm); ! 416: while (inm != NULL) { ! 417: if (inm->inm_timer == 0) { ! 418: /* do nothing */ ! 419: } else if (--inm->inm_timer == 0) { ! 420: igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); ! 421: inm->inm_state = IGMP_IREPORTEDLAST; ! 422: } else { ! 423: igmp_timers_are_running = 1; ! 424: } ! 425: IN_NEXT_MULTI(step, inm); ! 426: } ! 427: splx(s); ! 428: } ! 429: ! 430: void ! 431: igmp_slowtimo() ! 432: { ! 433: int s = splnet(); ! 434: register struct router_info *rti = Head; ! 435: ! 436: #if IGMP_DEBUG ! 437: printf("[igmp.c,_slowtimo] -- > entering \n"); ! 438: #endif ! 439: while (rti) { ! 440: if (rti->rti_type == IGMP_V1_ROUTER) { ! 441: rti->rti_time++; ! 442: if (rti->rti_time >= IGMP_AGE_THRESHOLD) { ! 443: rti->rti_type = IGMP_V2_ROUTER; ! 444: } ! 445: } ! 446: rti = rti->rti_next; ! 447: } ! 448: #if IGMP_DEBUG ! 449: printf("[igmp.c,_slowtimo] -- > exiting \n"); ! 450: #endif ! 451: splx(s); ! 452: } ! 453: ! 454: static struct route igmprt; ! 455: ! 456: static void ! 457: igmp_sendpkt(inm, type, addr) ! 458: struct in_multi *inm; ! 459: int type; ! 460: unsigned long addr; ! 461: { ! 462: struct mbuf *m; ! 463: struct igmp *igmp; ! 464: struct ip *ip; ! 465: struct ip_moptions imo; ! 466: ! 467: MGETHDR(m, M_DONTWAIT, MT_HEADER); ! 468: if (m == NULL) ! 469: return; ! 470: ! 471: m->m_pkthdr.rcvif = loif; ! 472: m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN; ! 473: MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip)); ! 474: m->m_data += sizeof(struct ip); ! 475: m->m_len = IGMP_MINLEN; ! 476: igmp = mtod(m, struct igmp *); ! 477: igmp->igmp_type = type; ! 478: igmp->igmp_code = 0; ! 479: igmp->igmp_group = inm->inm_addr; ! 480: igmp->igmp_cksum = 0; ! 481: igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); ! 482: ! 483: m->m_data -= sizeof(struct ip); ! 484: m->m_len += sizeof(struct ip); ! 485: ip = mtod(m, struct ip *); ! 486: ip->ip_tos = 0; ! 487: ip->ip_len = sizeof(struct ip) + IGMP_MINLEN; ! 488: ip->ip_off = 0; ! 489: ip->ip_p = IPPROTO_IGMP; ! 490: ip->ip_src.s_addr = INADDR_ANY; ! 491: ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr; ! 492: ! 493: imo.imo_multicast_ifp = inm->inm_ifp; ! 494: imo.imo_multicast_ttl = 1; ! 495: imo.imo_multicast_vif = -1; ! 496: /* ! 497: * Request loopback of the report if we are acting as a multicast ! 498: * router, so that the process-level routing demon can hear it. ! 499: */ ! 500: imo.imo_multicast_loop = (ip_mrouter != NULL); ! 501: ! 502: /* ! 503: * XXX ! 504: * Do we have to worry about reentrancy here? Don't think so. ! 505: */ ! 506: ip_output(m, router_alert, &igmprt, 0, &imo); ! 507: ! 508: ++igmpstat.igps_snd_reports; ! 509: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.