Annotation of sys/netinet/igmp.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: igmp.c,v 1.24 2007/07/20 19:00:35 claudio Exp $ */
! 2: /* $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $ */
! 3:
! 4: /*
! 5: * Internet Group Management Protocol (IGMP) routines.
! 6: *
! 7: * Written by Steve Deering, Stanford, May 1988.
! 8: * Modified by Rosen Sharma, Stanford, Aug 1994.
! 9: * Modified by Bill Fenner, Xerox PARC, Feb 1995.
! 10: *
! 11: * MULTICAST Revision: 1.3
! 12: */
! 13:
! 14: #include <sys/param.h>
! 15: #include <sys/mbuf.h>
! 16: #include <sys/socket.h>
! 17: #include <sys/protosw.h>
! 18:
! 19: #include <net/if.h>
! 20: #include <net/route.h>
! 21:
! 22: #include <netinet/in.h>
! 23: #include <netinet/in_var.h>
! 24: #include <netinet/in_systm.h>
! 25: #include <netinet/ip.h>
! 26: #include <netinet/ip_var.h>
! 27: #include <netinet/igmp.h>
! 28: #include <netinet/igmp_var.h>
! 29: #include <dev/rndvar.h>
! 30:
! 31: #include <sys/stdarg.h>
! 32:
! 33: #define IP_MULTICASTOPTS 0
! 34:
! 35: int igmp_timers_are_running;
! 36: static struct router_info *rti_head;
! 37: struct igmpstat igmpstat;
! 38:
! 39: void igmp_sendpkt(struct in_multi *, int, in_addr_t);
! 40: int rti_fill(struct in_multi *);
! 41: struct router_info * rti_find(struct ifnet *);
! 42:
! 43: void
! 44: igmp_init()
! 45: {
! 46:
! 47: /*
! 48: * To avoid byte-swapping the same value over and over again.
! 49: */
! 50: igmp_timers_are_running = 0;
! 51: rti_head = 0;
! 52: }
! 53:
! 54: /* Return -1 for error. */
! 55: int
! 56: rti_fill(inm)
! 57: struct in_multi *inm;
! 58: {
! 59: struct router_info *rti;
! 60:
! 61: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
! 62: if (rti->rti_ifp == inm->inm_ia->ia_ifp) {
! 63: inm->inm_rti = rti;
! 64: if (rti->rti_type == IGMP_v1_ROUTER)
! 65: return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
! 66: else
! 67: return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
! 68: }
! 69: }
! 70:
! 71: rti = (struct router_info *)malloc(sizeof(struct router_info),
! 72: M_MRTABLE, M_NOWAIT);
! 73: if (rti == NULL)
! 74: return (-1);
! 75: rti->rti_ifp = inm->inm_ia->ia_ifp;
! 76: rti->rti_type = IGMP_v2_ROUTER;
! 77: rti->rti_next = rti_head;
! 78: rti_head = rti;
! 79: inm->inm_rti = rti;
! 80: return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
! 81: }
! 82:
! 83: struct router_info *
! 84: rti_find(ifp)
! 85: struct ifnet *ifp;
! 86: {
! 87: struct router_info *rti;
! 88:
! 89: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
! 90: if (rti->rti_ifp == ifp)
! 91: return (rti);
! 92: }
! 93:
! 94: rti = (struct router_info *)malloc(sizeof(struct router_info),
! 95: M_MRTABLE, M_NOWAIT);
! 96: if (rti == NULL)
! 97: return (NULL);
! 98: rti->rti_ifp = ifp;
! 99: rti->rti_type = IGMP_v2_ROUTER;
! 100: rti->rti_next = rti_head;
! 101: rti_head = rti;
! 102: return (rti);
! 103: }
! 104:
! 105: void
! 106: rti_delete(ifp)
! 107: struct ifnet *ifp;
! 108: {
! 109: struct router_info *rti, **prti = &rti_head;
! 110:
! 111: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
! 112: if (rti->rti_ifp == ifp) {
! 113: *prti = rti->rti_next;
! 114: free(rti, M_MRTABLE);
! 115: break;
! 116: }
! 117: prti = &rti->rti_next;
! 118: }
! 119: }
! 120:
! 121: void
! 122: igmp_input(struct mbuf *m, ...)
! 123: {
! 124: int iphlen;
! 125: struct ifnet *ifp = m->m_pkthdr.rcvif;
! 126: struct ip *ip = mtod(m, struct ip *);
! 127: struct igmp *igmp;
! 128: int igmplen;
! 129: int minlen;
! 130: struct in_multi *inm;
! 131: struct in_multistep step;
! 132: struct router_info *rti;
! 133: struct in_ifaddr *ia;
! 134: int timer;
! 135: va_list ap;
! 136:
! 137: va_start(ap, m);
! 138: iphlen = va_arg(ap, int);
! 139: va_end(ap);
! 140:
! 141: ++igmpstat.igps_rcv_total;
! 142:
! 143: igmplen = ntohs(ip->ip_len) - iphlen;
! 144:
! 145: /*
! 146: * Validate lengths
! 147: */
! 148: if (igmplen < IGMP_MINLEN) {
! 149: ++igmpstat.igps_rcv_tooshort;
! 150: m_freem(m);
! 151: return;
! 152: }
! 153: minlen = iphlen + IGMP_MINLEN;
! 154: if ((m->m_flags & M_EXT || m->m_len < minlen) &&
! 155: (m = m_pullup(m, minlen)) == NULL) {
! 156: ++igmpstat.igps_rcv_tooshort;
! 157: return;
! 158: }
! 159:
! 160: /*
! 161: * Validate checksum
! 162: */
! 163: m->m_data += iphlen;
! 164: m->m_len -= iphlen;
! 165: igmp = mtod(m, struct igmp *);
! 166: if (in_cksum(m, igmplen)) {
! 167: ++igmpstat.igps_rcv_badsum;
! 168: m_freem(m);
! 169: return;
! 170: }
! 171: m->m_data -= iphlen;
! 172: m->m_len += iphlen;
! 173: ip = mtod(m, struct ip *);
! 174:
! 175: switch (igmp->igmp_type) {
! 176:
! 177: case IGMP_HOST_MEMBERSHIP_QUERY:
! 178: ++igmpstat.igps_rcv_queries;
! 179:
! 180: if (ifp->if_flags & IFF_LOOPBACK)
! 181: break;
! 182:
! 183: if (igmp->igmp_code == 0) {
! 184: rti = rti_find(ifp);
! 185: if (rti == NULL) {
! 186: m_freem(m);
! 187: return;
! 188: }
! 189: rti->rti_type = IGMP_v1_ROUTER;
! 190: rti->rti_age = 0;
! 191:
! 192: if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
! 193: ++igmpstat.igps_rcv_badqueries;
! 194: m_freem(m);
! 195: return;
! 196: }
! 197:
! 198: /*
! 199: * Start the timers in all of our membership records
! 200: * for the interface on which the query arrived,
! 201: * except those that are already running and those
! 202: * that belong to a "local" group (224.0.0.X).
! 203: */
! 204: IN_FIRST_MULTI(step, inm);
! 205: while (inm != NULL) {
! 206: if (inm->inm_ia->ia_ifp == ifp &&
! 207: inm->inm_timer == 0 &&
! 208: !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
! 209: inm->inm_state = IGMP_DELAYING_MEMBER;
! 210: inm->inm_timer = IGMP_RANDOM_DELAY(
! 211: IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
! 212: igmp_timers_are_running = 1;
! 213: }
! 214: IN_NEXT_MULTI(step, inm);
! 215: }
! 216: } else {
! 217: if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
! 218: ++igmpstat.igps_rcv_badqueries;
! 219: m_freem(m);
! 220: return;
! 221: }
! 222:
! 223: timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
! 224: if (timer == 0)
! 225: timer = 1;
! 226:
! 227: /*
! 228: * Start the timers in all of our membership records
! 229: * for the interface on which the query arrived,
! 230: * except those that are already running and those
! 231: * that belong to a "local" group (224.0.0.X). For
! 232: * timers already running, check if they need to be
! 233: * reset.
! 234: */
! 235: IN_FIRST_MULTI(step, inm);
! 236: while (inm != NULL) {
! 237: if (inm->inm_ia->ia_ifp == ifp &&
! 238: !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
! 239: (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
! 240: ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
! 241: switch (inm->inm_state) {
! 242: case IGMP_DELAYING_MEMBER:
! 243: if (inm->inm_timer <= timer)
! 244: break;
! 245: /* FALLTHROUGH */
! 246: case IGMP_IDLE_MEMBER:
! 247: case IGMP_LAZY_MEMBER:
! 248: case IGMP_AWAKENING_MEMBER:
! 249: inm->inm_state =
! 250: IGMP_DELAYING_MEMBER;
! 251: inm->inm_timer =
! 252: IGMP_RANDOM_DELAY(timer);
! 253: igmp_timers_are_running = 1;
! 254: break;
! 255: case IGMP_SLEEPING_MEMBER:
! 256: inm->inm_state =
! 257: IGMP_AWAKENING_MEMBER;
! 258: break;
! 259: }
! 260: }
! 261: IN_NEXT_MULTI(step, inm);
! 262: }
! 263: }
! 264:
! 265: break;
! 266:
! 267: case IGMP_v1_HOST_MEMBERSHIP_REPORT:
! 268: ++igmpstat.igps_rcv_reports;
! 269:
! 270: if (ifp->if_flags & IFF_LOOPBACK)
! 271: break;
! 272:
! 273: if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
! 274: igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
! 275: ++igmpstat.igps_rcv_badreports;
! 276: m_freem(m);
! 277: return;
! 278: }
! 279:
! 280: /*
! 281: * KLUDGE: if the IP source address of the report has an
! 282: * unspecified (i.e., zero) subnet number, as is allowed for
! 283: * a booting host, replace it with the correct subnet number
! 284: * so that a process-level multicast routing daemon can
! 285: * determine which subnet it arrived from. This is necessary
! 286: * to compensate for the lack of any way for a process to
! 287: * determine the arrival interface of an incoming packet.
! 288: */
! 289: if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
! 290: IFP_TO_IA(ifp, ia);
! 291: if (ia)
! 292: ip->ip_src.s_addr = ia->ia_subnet;
! 293: }
! 294:
! 295: /*
! 296: * If we belong to the group being reported, stop
! 297: * our timer for that group.
! 298: */
! 299: IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
! 300: if (inm != NULL) {
! 301: inm->inm_timer = 0;
! 302: ++igmpstat.igps_rcv_ourreports;
! 303:
! 304: switch (inm->inm_state) {
! 305: case IGMP_IDLE_MEMBER:
! 306: case IGMP_LAZY_MEMBER:
! 307: case IGMP_AWAKENING_MEMBER:
! 308: case IGMP_SLEEPING_MEMBER:
! 309: inm->inm_state = IGMP_SLEEPING_MEMBER;
! 310: break;
! 311: case IGMP_DELAYING_MEMBER:
! 312: if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
! 313: inm->inm_state = IGMP_LAZY_MEMBER;
! 314: else
! 315: inm->inm_state = IGMP_SLEEPING_MEMBER;
! 316: break;
! 317: }
! 318: }
! 319:
! 320: break;
! 321:
! 322: case IGMP_v2_HOST_MEMBERSHIP_REPORT:
! 323: #ifdef MROUTING
! 324: /*
! 325: * Make sure we don't hear our own membership report. Fast
! 326: * leave requires knowing that we are the only member of a
! 327: * group.
! 328: */
! 329: IFP_TO_IA(ifp, ia);
! 330: if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
! 331: break;
! 332: #endif
! 333:
! 334: ++igmpstat.igps_rcv_reports;
! 335:
! 336: if (ifp->if_flags & IFF_LOOPBACK)
! 337: break;
! 338:
! 339: if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
! 340: igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
! 341: ++igmpstat.igps_rcv_badreports;
! 342: m_freem(m);
! 343: return;
! 344: }
! 345:
! 346: /*
! 347: * KLUDGE: if the IP source address of the report has an
! 348: * unspecified (i.e., zero) subnet number, as is allowed for
! 349: * a booting host, replace it with the correct subnet number
! 350: * so that a process-level multicast routing daemon can
! 351: * determine which subnet it arrived from. This is necessary
! 352: * to compensate for the lack of any way for a process to
! 353: * determine the arrival interface of an incoming packet.
! 354: */
! 355: if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
! 356: #ifndef MROUTING
! 357: IFP_TO_IA(ifp, ia);
! 358: #endif
! 359: if (ia)
! 360: ip->ip_src.s_addr = ia->ia_subnet;
! 361: }
! 362:
! 363: /*
! 364: * If we belong to the group being reported, stop
! 365: * our timer for that group.
! 366: */
! 367: IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
! 368: if (inm != NULL) {
! 369: inm->inm_timer = 0;
! 370: ++igmpstat.igps_rcv_ourreports;
! 371:
! 372: switch (inm->inm_state) {
! 373: case IGMP_DELAYING_MEMBER:
! 374: case IGMP_IDLE_MEMBER:
! 375: case IGMP_AWAKENING_MEMBER:
! 376: inm->inm_state = IGMP_LAZY_MEMBER;
! 377: break;
! 378: case IGMP_LAZY_MEMBER:
! 379: case IGMP_SLEEPING_MEMBER:
! 380: break;
! 381: }
! 382: }
! 383:
! 384: break;
! 385:
! 386: }
! 387:
! 388: /*
! 389: * Pass all valid IGMP packets up to any process(es) listening
! 390: * on a raw IGMP socket.
! 391: */
! 392: rip_input(m);
! 393: }
! 394:
! 395: void
! 396: igmp_joingroup(inm)
! 397: struct in_multi *inm;
! 398: {
! 399: int i, s = splsoftnet();
! 400:
! 401: inm->inm_state = IGMP_IDLE_MEMBER;
! 402:
! 403: if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
! 404: (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0) {
! 405: if ((i = rti_fill(inm)) == -1) {
! 406: splx(s);
! 407: return;
! 408: }
! 409: igmp_sendpkt(inm, i, 0);
! 410: inm->inm_state = IGMP_DELAYING_MEMBER;
! 411: inm->inm_timer = IGMP_RANDOM_DELAY(
! 412: IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
! 413: igmp_timers_are_running = 1;
! 414: } else
! 415: inm->inm_timer = 0;
! 416: splx(s);
! 417: }
! 418:
! 419: void
! 420: igmp_leavegroup(inm)
! 421: struct in_multi *inm;
! 422: {
! 423:
! 424: switch (inm->inm_state) {
! 425: case IGMP_DELAYING_MEMBER:
! 426: case IGMP_IDLE_MEMBER:
! 427: if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
! 428: (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0)
! 429: if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
! 430: igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE,
! 431: INADDR_ALLROUTERS_GROUP);
! 432: break;
! 433: case IGMP_LAZY_MEMBER:
! 434: case IGMP_AWAKENING_MEMBER:
! 435: case IGMP_SLEEPING_MEMBER:
! 436: break;
! 437: }
! 438: }
! 439:
! 440: void
! 441: igmp_fasttimo()
! 442: {
! 443: struct in_multi *inm;
! 444: struct in_multistep step;
! 445: int s;
! 446:
! 447: /*
! 448: * Quick check to see if any work needs to be done, in order
! 449: * to minimize the overhead of fasttimo processing.
! 450: */
! 451: if (!igmp_timers_are_running)
! 452: return;
! 453:
! 454: s = splsoftnet();
! 455: igmp_timers_are_running = 0;
! 456: IN_FIRST_MULTI(step, inm);
! 457: while (inm != NULL) {
! 458: if (inm->inm_timer == 0) {
! 459: /* do nothing */
! 460: } else if (--inm->inm_timer == 0) {
! 461: if (inm->inm_state == IGMP_DELAYING_MEMBER) {
! 462: if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
! 463: igmp_sendpkt(inm,
! 464: IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
! 465: else
! 466: igmp_sendpkt(inm,
! 467: IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
! 468: inm->inm_state = IGMP_IDLE_MEMBER;
! 469: }
! 470: } else {
! 471: igmp_timers_are_running = 1;
! 472: }
! 473: IN_NEXT_MULTI(step, inm);
! 474: }
! 475: splx(s);
! 476: }
! 477:
! 478: void
! 479: igmp_slowtimo()
! 480: {
! 481: struct router_info *rti;
! 482: int s;
! 483:
! 484: s = splsoftnet();
! 485: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
! 486: if (rti->rti_type == IGMP_v1_ROUTER &&
! 487: ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
! 488: rti->rti_type = IGMP_v2_ROUTER;
! 489: }
! 490: }
! 491: splx(s);
! 492: }
! 493:
! 494: void
! 495: igmp_sendpkt(inm, type, addr)
! 496: struct in_multi *inm;
! 497: int type;
! 498: in_addr_t addr;
! 499: {
! 500: struct mbuf *m;
! 501: struct igmp *igmp;
! 502: struct ip *ip;
! 503: struct ip_moptions imo;
! 504: #ifdef MROUTING
! 505: extern struct socket *ip_mrouter;
! 506: #endif /* MROUTING */
! 507:
! 508: MGETHDR(m, M_DONTWAIT, MT_HEADER);
! 509: if (m == NULL)
! 510: return;
! 511: /*
! 512: * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
! 513: * is smaller than mbuf size returned by MGETHDR.
! 514: */
! 515: m->m_data += max_linkhdr;
! 516: m->m_len = sizeof(struct ip) + IGMP_MINLEN;
! 517: m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
! 518:
! 519: ip = mtod(m, struct ip *);
! 520: ip->ip_tos = 0;
! 521: ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
! 522: ip->ip_off = 0;
! 523: ip->ip_p = IPPROTO_IGMP;
! 524: ip->ip_src.s_addr = INADDR_ANY;
! 525: if (addr) {
! 526: ip->ip_dst.s_addr = addr;
! 527: } else {
! 528: ip->ip_dst = inm->inm_addr;
! 529: }
! 530:
! 531: m->m_data += sizeof(struct ip);
! 532: m->m_len -= sizeof(struct ip);
! 533: igmp = mtod(m, struct igmp *);
! 534: igmp->igmp_type = type;
! 535: igmp->igmp_code = 0;
! 536: igmp->igmp_group = inm->inm_addr;
! 537: igmp->igmp_cksum = 0;
! 538: igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
! 539: m->m_data -= sizeof(struct ip);
! 540: m->m_len += sizeof(struct ip);
! 541:
! 542: imo.imo_multicast_ifp = inm->inm_ia->ia_ifp;
! 543: imo.imo_multicast_ttl = 1;
! 544: #ifdef RSVP_ISI
! 545: imo.imo_multicast_vif = -1;
! 546: #endif
! 547: /*
! 548: * Request loopback of the report if we are acting as a multicast
! 549: * router, so that the process-level routing daemon can hear it.
! 550: */
! 551: #ifdef MROUTING
! 552: imo.imo_multicast_loop = (ip_mrouter != NULL);
! 553: #else
! 554: imo.imo_multicast_loop = 0;
! 555: #endif /* MROUTING */
! 556:
! 557: ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
! 558: &imo, (void *)NULL);
! 559:
! 560: ++igmpstat.igps_snd_reports;
! 561: }
CVSweb