Annotation of sys/netinet6/mld6.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: mld6.c,v 1.24 2007/06/01 00:52:38 henning Exp $ */
! 2: /* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */
! 3:
! 4: /*
! 5: * Copyright (C) 1998 WIDE Project.
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the project nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: */
! 32:
! 33: /*
! 34: * Copyright (c) 1988 Stephen Deering.
! 35: * Copyright (c) 1992, 1993
! 36: * The Regents of the University of California. All rights reserved.
! 37: *
! 38: * This code is derived from software contributed to Berkeley by
! 39: * Stephen Deering of Stanford University.
! 40: *
! 41: * Redistribution and use in source and binary forms, with or without
! 42: * modification, are permitted provided that the following conditions
! 43: * are met:
! 44: * 1. Redistributions of source code must retain the above copyright
! 45: * notice, this list of conditions and the following disclaimer.
! 46: * 2. Redistributions in binary form must reproduce the above copyright
! 47: * notice, this list of conditions and the following disclaimer in the
! 48: * documentation and/or other materials provided with the distribution.
! 49: * 3. Neither the name of the University nor the names of its contributors
! 50: * may be used to endorse or promote products derived from this software
! 51: * without specific prior written permission.
! 52: *
! 53: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 54: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 55: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 56: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 57: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 58: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 59: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 60: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 61: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 62: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 63: * SUCH DAMAGE.
! 64: *
! 65: * @(#)igmp.c 8.1 (Berkeley) 7/19/93
! 66: */
! 67:
! 68: #include <sys/param.h>
! 69: #include <sys/systm.h>
! 70: #include <sys/mbuf.h>
! 71: #include <sys/socket.h>
! 72: #include <sys/protosw.h>
! 73: #include <sys/syslog.h>
! 74: #include <dev/rndvar.h>
! 75:
! 76: #include <net/if.h>
! 77:
! 78: #include <netinet/in.h>
! 79: #include <netinet/in_var.h>
! 80: #include <netinet/ip6.h>
! 81: #include <netinet6/ip6_var.h>
! 82: #include <netinet/icmp6.h>
! 83: #include <netinet6/mld6_var.h>
! 84:
! 85: /*
! 86: * Protocol constants
! 87: */
! 88:
! 89: /* denotes that the MLD max response delay field specifies time in milliseconds */
! 90: #define MLD_TIMER_SCALE 1000
! 91: /*
! 92: * time between repetitions of a node's initial report of interest in a
! 93: * multicast address(in seconds)
! 94: */
! 95: #define MLD_UNSOLICITED_REPORT_INTERVAL 10
! 96:
! 97: static struct ip6_pktopts ip6_opts;
! 98: static int mld_timers_are_running;
! 99: /* XXX: These are necessary for KAME's link-local hack */
! 100: static struct in6_addr mld_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
! 101: static struct in6_addr mld_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
! 102:
! 103: static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
! 104:
! 105: void
! 106: mld6_init()
! 107: {
! 108: static u_int8_t hbh_buf[8];
! 109: struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
! 110: u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
! 111:
! 112: mld_timers_are_running = 0;
! 113:
! 114: /* ip6h_nxt will be fill in later */
! 115: hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
! 116:
! 117: /* XXX: grotty hard coding... */
! 118: hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
! 119: hbh_buf[3] = 0;
! 120: hbh_buf[4] = IP6OPT_ROUTER_ALERT;
! 121: hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
! 122: bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
! 123:
! 124: ip6_opts.ip6po_hbh = hbh;
! 125: }
! 126:
! 127: void
! 128: mld6_start_listening(in6m)
! 129: struct in6_multi *in6m;
! 130: {
! 131: int s = splsoftnet();
! 132:
! 133: /*
! 134: * RFC2710 page 10:
! 135: * The node never sends a Report or Done for the link-scope all-nodes
! 136: * address.
! 137: * MLD messages are never sent for multicast addresses whose scope is 0
! 138: * (reserved) or 1 (node-local).
! 139: */
! 140: mld_all_nodes_linklocal.s6_addr16[1] =
! 141: htons(in6m->in6m_ifp->if_index); /* XXX */
! 142: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal) ||
! 143: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
! 144: in6m->in6m_timer = 0;
! 145: in6m->in6m_state = MLD_OTHERLISTENER;
! 146: } else {
! 147: mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
! 148: in6m->in6m_timer =
! 149: MLD_RANDOM_DELAY(MLD_UNSOLICITED_REPORT_INTERVAL *
! 150: PR_FASTHZ);
! 151: in6m->in6m_state = MLD_IREPORTEDLAST;
! 152: mld_timers_are_running = 1;
! 153: }
! 154: splx(s);
! 155: }
! 156:
! 157: void
! 158: mld6_stop_listening(in6m)
! 159: struct in6_multi *in6m;
! 160: {
! 161: mld_all_nodes_linklocal.s6_addr16[1] =
! 162: htons(in6m->in6m_ifp->if_index); /* XXX */
! 163: mld_all_routers_linklocal.s6_addr16[1] =
! 164: htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
! 165:
! 166: if (in6m->in6m_state == MLD_IREPORTEDLAST &&
! 167: (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal)) &&
! 168: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_INTFACELOCAL)
! 169: mld6_sendpkt(in6m, MLD_LISTENER_DONE,
! 170: &mld_all_routers_linklocal);
! 171: }
! 172:
! 173: void
! 174: mld6_input(m, off)
! 175: struct mbuf *m;
! 176: int off;
! 177: {
! 178: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 179: struct mld_hdr *mldh;
! 180: struct ifnet *ifp = m->m_pkthdr.rcvif;
! 181: struct in6_multi *in6m;
! 182: struct in6_ifaddr *ia;
! 183: int timer; /* timer value in the MLD query header */
! 184:
! 185: IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
! 186: if (mldh == NULL) {
! 187: icmp6stat.icp6s_tooshort++;
! 188: return;
! 189: }
! 190:
! 191: /* source address validation */
! 192: ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
! 193: if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
! 194: #if 0
! 195: log(LOG_ERR,
! 196: "mld_input: src %s is not link-local (grp=%s)\n",
! 197: ip6_sprintf(&ip6->ip6_src),
! 198: ip6_sprintf(&mldh->mld_addr));
! 199: #endif
! 200: /*
! 201: * spec (RFC2710) does not explicitly
! 202: * specify to discard the packet from a non link-local
! 203: * source address. But we believe it's expected to do so.
! 204: */
! 205: m_freem(m);
! 206: return;
! 207: }
! 208:
! 209: /*
! 210: * In the MLD6 specification, there are 3 states and a flag.
! 211: *
! 212: * In Non-Listener state, we simply don't have a membership record.
! 213: * In Delaying Listener state, our timer is running (in6m->in6m_timer)
! 214: * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
! 215: *
! 216: * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
! 217: * we have heard a report from another member, or MLD_IREPORTEDLAST
! 218: * if we sent the last report.
! 219: */
! 220: switch(mldh->mld_type) {
! 221: case MLD_LISTENER_QUERY:
! 222: if (ifp->if_flags & IFF_LOOPBACK)
! 223: break;
! 224:
! 225: if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
! 226: !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
! 227: break; /* print error or log stat? */
! 228: if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
! 229: mldh->mld_addr.s6_addr16[1] =
! 230: htons(ifp->if_index); /* XXX */
! 231:
! 232: /*
! 233: * - Start the timers in all of our membership records
! 234: * that the query applies to for the interface on
! 235: * which the query arrived excl. those that belong
! 236: * to the "all-nodes" group (ff02::1).
! 237: * - Restart any timer that is already running but has
! 238: * A value longer than the requested timeout.
! 239: * - Use the value specified in the query message as
! 240: * the maximum timeout.
! 241: */
! 242: IFP_TO_IA6(ifp, ia);
! 243: if (ia == NULL)
! 244: break;
! 245:
! 246: /*
! 247: * XXX: System timer resolution is too low to handle Max
! 248: * Response Delay, so set 1 to the internal timer even if
! 249: * the calculated value equals to zero when Max Response
! 250: * Delay is positive.
! 251: */
! 252: timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD_TIMER_SCALE;
! 253: if (timer == 0 && mldh->mld_maxdelay)
! 254: timer = 1;
! 255: mld_all_nodes_linklocal.s6_addr16[1] =
! 256: htons(ifp->if_index); /* XXX */
! 257:
! 258: LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) {
! 259: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
! 260: &mld_all_nodes_linklocal) ||
! 261: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
! 262: IPV6_ADDR_SCOPE_LINKLOCAL)
! 263: continue;
! 264:
! 265: if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
! 266: IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
! 267: &in6m->in6m_addr))
! 268: {
! 269: if (timer == 0) {
! 270: /* send a report immediately */
! 271: mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
! 272: NULL);
! 273: in6m->in6m_timer = 0; /* reset timer */
! 274: in6m->in6m_state = MLD_IREPORTEDLAST;
! 275: } else if (in6m->in6m_timer == 0 || /* idle */
! 276: in6m->in6m_timer > timer) {
! 277: in6m->in6m_timer =
! 278: MLD_RANDOM_DELAY(timer);
! 279: mld_timers_are_running = 1;
! 280: }
! 281: }
! 282: }
! 283:
! 284: if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
! 285: mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
! 286: break;
! 287: case MLD_LISTENER_REPORT:
! 288: /*
! 289: * For fast leave to work, we have to know that we are the
! 290: * last person to send a report for this group. Reports
! 291: * can potentially get looped back if we are a multicast
! 292: * router, so discard reports sourced by me.
! 293: * Note that it is impossible to check IFF_LOOPBACK flag of
! 294: * ifp for this purpose, since ip6_mloopback pass the physical
! 295: * interface to looutput.
! 296: */
! 297: if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
! 298: break;
! 299:
! 300: if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
! 301: break;
! 302:
! 303: if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
! 304: mldh->mld_addr.s6_addr16[1] =
! 305: htons(ifp->if_index); /* XXX */
! 306: /*
! 307: * If we belong to the group being reported, stop
! 308: * our timer for that group.
! 309: */
! 310: IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
! 311: if (in6m) {
! 312: in6m->in6m_timer = 0; /* transit to idle state */
! 313: in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
! 314: }
! 315:
! 316: if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
! 317: mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
! 318: break;
! 319: default: /* this is impossible */
! 320: #if 0
! 321: /*
! 322: * this case should be impossible because of filtering in
! 323: * icmp6_input(). But we explicitly disabled this part
! 324: * just in case.
! 325: */
! 326: log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
! 327: #endif
! 328: break;
! 329: }
! 330:
! 331: m_freem(m);
! 332: }
! 333:
! 334: void
! 335: mld6_fasttimeo()
! 336: {
! 337: struct in6_multi *in6m;
! 338: struct in6_multistep step;
! 339: int s;
! 340:
! 341: /*
! 342: * Quick check to see if any work needs to be done, in order
! 343: * to minimize the overhead of fasttimo processing.
! 344: */
! 345: if (!mld_timers_are_running)
! 346: return;
! 347:
! 348: s = splsoftnet();
! 349: mld_timers_are_running = 0;
! 350: IN6_FIRST_MULTI(step, in6m);
! 351: while (in6m != NULL) {
! 352: if (in6m->in6m_timer == 0) {
! 353: /* do nothing */
! 354: } else if (--in6m->in6m_timer == 0) {
! 355: mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
! 356: in6m->in6m_state = MLD_IREPORTEDLAST;
! 357: } else {
! 358: mld_timers_are_running = 1;
! 359: }
! 360: IN6_NEXT_MULTI(step, in6m);
! 361: }
! 362: splx(s);
! 363: }
! 364:
! 365: static void
! 366: mld6_sendpkt(in6m, type, dst)
! 367: struct in6_multi *in6m;
! 368: int type;
! 369: const struct in6_addr *dst;
! 370: {
! 371: struct mbuf *mh, *md;
! 372: struct mld_hdr *mldh;
! 373: struct ip6_hdr *ip6;
! 374: struct ip6_moptions im6o;
! 375: struct in6_ifaddr *ia;
! 376: struct ifnet *ifp = in6m->in6m_ifp;
! 377: int ignflags;
! 378:
! 379: /*
! 380: * At first, find a link local address on the outgoing interface
! 381: * to use as the source address of the MLD packet.
! 382: * We do not reject tentative addresses for MLD report to deal with
! 383: * the case where we first join a link-local address.
! 384: */
! 385: ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
! 386: if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL)
! 387: return;
! 388: if ((ia->ia6_flags & IN6_IFF_TENTATIVE))
! 389: ia = NULL;
! 390:
! 391: /*
! 392: * Allocate mbufs to store ip6 header and MLD header.
! 393: * We allocate 2 mbufs and make chain in advance because
! 394: * it is more convenient when inserting the hop-by-hop option later.
! 395: */
! 396: MGETHDR(mh, M_DONTWAIT, MT_HEADER);
! 397: if (mh == NULL)
! 398: return;
! 399: MGET(md, M_DONTWAIT, MT_DATA);
! 400: if (md == NULL) {
! 401: m_free(mh);
! 402: return;
! 403: }
! 404: mh->m_next = md;
! 405:
! 406: mh->m_pkthdr.rcvif = NULL;
! 407: mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
! 408: mh->m_len = sizeof(struct ip6_hdr);
! 409: MH_ALIGN(mh, sizeof(struct ip6_hdr));
! 410:
! 411: /* fill in the ip6 header */
! 412: ip6 = mtod(mh, struct ip6_hdr *);
! 413: ip6->ip6_flow = 0;
! 414: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
! 415: ip6->ip6_vfc |= IPV6_VERSION;
! 416: /* ip6_plen will be set later */
! 417: ip6->ip6_nxt = IPPROTO_ICMPV6;
! 418: /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
! 419: ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
! 420: ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
! 421:
! 422: /* fill in the MLD header */
! 423: md->m_len = sizeof(struct mld_hdr);
! 424: mldh = mtod(md, struct mld_hdr *);
! 425: mldh->mld_type = type;
! 426: mldh->mld_code = 0;
! 427: mldh->mld_cksum = 0;
! 428: /* XXX: we assume the function will not be called for query messages */
! 429: mldh->mld_maxdelay = 0;
! 430: mldh->mld_reserved = 0;
! 431: mldh->mld_addr = in6m->in6m_addr;
! 432: if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
! 433: mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
! 434: mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
! 435: sizeof(struct mld_hdr));
! 436:
! 437: /* construct multicast option */
! 438: bzero(&im6o, sizeof(im6o));
! 439: im6o.im6o_multicast_ifp = ifp;
! 440: im6o.im6o_multicast_hlim = 1;
! 441:
! 442: /*
! 443: * Request loopback of the report if we are acting as a multicast
! 444: * router, so that the process-level routing daemon can hear it.
! 445: */
! 446: #ifdef MROUTING
! 447: im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
! 448: #endif
! 449:
! 450: /* increment output statictics */
! 451: icmp6stat.icp6s_outhist[type]++;
! 452: icmp6_ifstat_inc(ifp, ifs6_out_msg);
! 453: switch (type) {
! 454: case MLD_LISTENER_QUERY:
! 455: icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
! 456: break;
! 457: case MLD_LISTENER_REPORT:
! 458: icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
! 459: break;
! 460: case MLD_LISTENER_DONE:
! 461: icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
! 462: break;
! 463: }
! 464:
! 465: ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC, &im6o, NULL,
! 466: NULL);
! 467: }
CVSweb