Annotation of sys/netinet6/nd6_nbr.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: nd6_nbr.c,v 1.45 2007/06/08 09:31:38 henning Exp $ */
! 2: /* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */
! 3:
! 4: /*
! 5: * Copyright (C) 1995, 1996, 1997, and 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: #include <sys/param.h>
! 34: #include <sys/systm.h>
! 35: #include <sys/malloc.h>
! 36: #include <sys/mbuf.h>
! 37: #include <sys/socket.h>
! 38: #include <sys/sockio.h>
! 39: #include <sys/time.h>
! 40: #include <sys/kernel.h>
! 41: #include <sys/errno.h>
! 42: #include <sys/ioctl.h>
! 43: #include <sys/syslog.h>
! 44: #include <sys/queue.h>
! 45: #include <sys/timeout.h>
! 46:
! 47: #include <net/if.h>
! 48: #include <net/if_types.h>
! 49: #include <net/if_dl.h>
! 50: #include <net/route.h>
! 51:
! 52: #include <netinet/in.h>
! 53: #include <netinet/in_var.h>
! 54: #include <netinet6/in6_var.h>
! 55: #include <netinet/ip6.h>
! 56: #include <netinet6/ip6_var.h>
! 57: #include <netinet6/nd6.h>
! 58: #include <netinet/icmp6.h>
! 59:
! 60: #include <dev/rndvar.h>
! 61:
! 62: #include "carp.h"
! 63: #if NCARP > 0
! 64: #include <netinet/ip_carp.h>
! 65: #endif
! 66:
! 67: #define SDL(s) ((struct sockaddr_dl *)s)
! 68:
! 69: struct dadq;
! 70: static struct dadq *nd6_dad_find(struct ifaddr *);
! 71: static void nd6_dad_starttimer(struct dadq *, int);
! 72: static void nd6_dad_stoptimer(struct dadq *);
! 73: static void nd6_dad_timer(struct ifaddr *);
! 74: static void nd6_dad_ns_output(struct dadq *, struct ifaddr *);
! 75: static void nd6_dad_ns_input(struct ifaddr *);
! 76: static void nd6_dad_na_input(struct ifaddr *);
! 77:
! 78: static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/
! 79: static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */
! 80:
! 81: /*
! 82: * Input an Neighbor Solicitation Message.
! 83: *
! 84: * Based on RFC 2461
! 85: * Based on RFC 2462 (duplicated address detection)
! 86: */
! 87: void
! 88: nd6_ns_input(m, off, icmp6len)
! 89: struct mbuf *m;
! 90: int off, icmp6len;
! 91: {
! 92: struct ifnet *ifp = m->m_pkthdr.rcvif;
! 93: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 94: struct nd_neighbor_solicit *nd_ns;
! 95: struct in6_addr saddr6 = ip6->ip6_src;
! 96: struct in6_addr daddr6 = ip6->ip6_dst;
! 97: struct in6_addr taddr6;
! 98: struct in6_addr myaddr6;
! 99: char *lladdr = NULL;
! 100: struct ifaddr *ifa = NULL;
! 101: int lladdrlen = 0;
! 102: int anycast = 0, proxy = 0, tentative = 0;
! 103: int router = ip6_forwarding;
! 104: int tlladdr;
! 105: union nd_opts ndopts;
! 106: struct sockaddr_dl *proxydl = NULL;
! 107:
! 108: IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len);
! 109: if (nd_ns == NULL) {
! 110: icmp6stat.icp6s_tooshort++;
! 111: return;
! 112: }
! 113: ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */
! 114: taddr6 = nd_ns->nd_ns_target;
! 115:
! 116: if (ip6->ip6_hlim != 255) {
! 117: nd6log((LOG_ERR,
! 118: "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n",
! 119: ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
! 120: ip6_sprintf(&ip6->ip6_dst), ifp->if_xname));
! 121: goto bad;
! 122: }
! 123:
! 124: if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
! 125: /* dst has to be solicited node multicast address. */
! 126: /* don't check ifindex portion */
! 127: if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL &&
! 128: daddr6.s6_addr32[1] == 0 &&
! 129: daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE &&
! 130: daddr6.s6_addr8[12] == 0xff) {
! 131: ; /*good*/
! 132: } else {
! 133: nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
! 134: "(wrong ip6 dst)\n"));
! 135: goto bad;
! 136: }
! 137: }
! 138:
! 139: if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
! 140: nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"));
! 141: goto bad;
! 142: }
! 143:
! 144: if (IN6_IS_SCOPE_EMBED(&taddr6))
! 145: taddr6.s6_addr16[1] = htons(ifp->if_index);
! 146:
! 147: icmp6len -= sizeof(*nd_ns);
! 148: nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
! 149: if (nd6_options(&ndopts) < 0) {
! 150: nd6log((LOG_INFO,
! 151: "nd6_ns_input: invalid ND option, ignored\n"));
! 152: /* nd6_options have incremented stats */
! 153: goto freeit;
! 154: }
! 155:
! 156: if (ndopts.nd_opts_src_lladdr) {
! 157: lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
! 158: lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
! 159: }
! 160:
! 161: if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
! 162: nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
! 163: "(link-layer address option)\n"));
! 164: goto bad;
! 165: }
! 166:
! 167: /*
! 168: * Attaching target link-layer address to the NA?
! 169: * (RFC 2461 7.2.4)
! 170: *
! 171: * NS IP dst is unicast/anycast MUST NOT add
! 172: * NS IP dst is solicited-node multicast MUST add
! 173: *
! 174: * In implementation, we add target link-layer address by default.
! 175: * We do not add one in MUST NOT cases.
! 176: */
! 177: #if 0 /* too much! */
! 178: ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6);
! 179: if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST))
! 180: tlladdr = 0;
! 181: else
! 182: #endif
! 183: if (!IN6_IS_ADDR_MULTICAST(&daddr6))
! 184: tlladdr = 0;
! 185: else
! 186: tlladdr = 1;
! 187:
! 188: /*
! 189: * Target address (taddr6) must be either:
! 190: * (1) Valid unicast/anycast address for my receiving interface,
! 191: * (2) Unicast address for which I'm offering proxy service, or
! 192: * (3) "tentative" address on which DAD is being performed.
! 193: */
! 194: /* (1) and (3) check. */
! 195: #if NCARP > 0
! 196: if (ifp->if_type == IFT_CARP) {
! 197: ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
! 198: if (ifa && !carp_iamatch6(ifp, ifa))
! 199: ifa = NULL;
! 200: } else {
! 201: ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
! 202: }
! 203: #else
! 204: ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
! 205: #endif
! 206:
! 207: /* (2) check. */
! 208: if (!ifa) {
! 209: struct rtentry *rt;
! 210: struct sockaddr_in6 tsin6;
! 211:
! 212: bzero(&tsin6, sizeof tsin6);
! 213: tsin6.sin6_len = sizeof(struct sockaddr_in6);
! 214: tsin6.sin6_family = AF_INET6;
! 215: tsin6.sin6_addr = taddr6;
! 216:
! 217: rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0);
! 218: if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
! 219: rt->rt_gateway->sa_family == AF_LINK) {
! 220: /*
! 221: * proxy NDP for single entry
! 222: */
! 223: ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
! 224: IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
! 225: if (ifa) {
! 226: proxy = 1;
! 227: proxydl = SDL(rt->rt_gateway);
! 228: router = 0; /* XXX */
! 229: }
! 230: }
! 231: if (rt)
! 232: rtfree(rt);
! 233: }
! 234: if (!ifa) {
! 235: /*
! 236: * We've got an NS packet, and we don't have that address
! 237: * assigned for us. We MUST silently ignore it.
! 238: * See RFC2461 7.2.3.
! 239: */
! 240: goto freeit;
! 241: }
! 242: myaddr6 = *IFA_IN6(ifa);
! 243: anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
! 244: tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
! 245: if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
! 246: goto freeit;
! 247:
! 248: if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
! 249: nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s "
! 250: "(if %d, NS packet %d)\n",
! 251: ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
! 252: goto bad;
! 253: }
! 254:
! 255: if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
! 256: log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n",
! 257: ip6_sprintf(&saddr6));
! 258: goto freeit;
! 259: }
! 260:
! 261: /*
! 262: * We have neighbor solicitation packet, with target address equals to
! 263: * one of my tentative address.
! 264: *
! 265: * src addr how to process?
! 266: * --- ---
! 267: * multicast of course, invalid (rejected in ip6_input)
! 268: * unicast somebody is doing address resolution -> ignore
! 269: * unspec dup address detection
! 270: *
! 271: * The processing is defined in RFC 2462.
! 272: */
! 273: if (tentative) {
! 274: /*
! 275: * If source address is unspecified address, it is for
! 276: * duplicated address detection.
! 277: *
! 278: * If not, the packet is for address resolution;
! 279: * silently ignore it.
! 280: */
! 281: if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
! 282: nd6_dad_ns_input(ifa);
! 283:
! 284: goto freeit;
! 285: }
! 286:
! 287: /*
! 288: * If the source address is unspecified address, entries must not
! 289: * be created or updated.
! 290: * It looks that sender is performing DAD. Output NA toward
! 291: * all-node multicast address, to tell the sender that I'm using
! 292: * the address.
! 293: * S bit ("solicited") must be zero.
! 294: */
! 295: if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
! 296: saddr6 = in6addr_linklocal_allnodes;
! 297: saddr6.s6_addr16[1] = htons(ifp->if_index);
! 298: nd6_na_output(ifp, &saddr6, &taddr6,
! 299: ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
! 300: (router ? ND_NA_FLAG_ROUTER : 0),
! 301: tlladdr, (struct sockaddr *)proxydl);
! 302: goto freeit;
! 303: }
! 304:
! 305: nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0);
! 306:
! 307: nd6_na_output(ifp, &saddr6, &taddr6,
! 308: ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
! 309: (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
! 310: tlladdr, (struct sockaddr *)proxydl);
! 311: freeit:
! 312: m_freem(m);
! 313: return;
! 314:
! 315: bad:
! 316: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)));
! 317: nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)));
! 318: nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)));
! 319: icmp6stat.icp6s_badns++;
! 320: m_freem(m);
! 321: }
! 322:
! 323: /*
! 324: * Output an Neighbor Solicitation Message. Caller specifies:
! 325: * - ICMP6 header source IP6 address
! 326: * - ND6 header target IP6 address
! 327: * - ND6 header source datalink address
! 328: *
! 329: * Based on RFC 2461
! 330: * Based on RFC 2462 (duplicated address detection)
! 331: */
! 332: void
! 333: nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
! 334: struct ifnet *ifp;
! 335: struct in6_addr *daddr6, *taddr6;
! 336: struct llinfo_nd6 *ln; /* for source address determination */
! 337: int dad; /* duplicated address detection */
! 338: {
! 339: struct mbuf *m;
! 340: struct ip6_hdr *ip6;
! 341: struct nd_neighbor_solicit *nd_ns;
! 342: struct sockaddr_in6 src_sa, dst_sa;
! 343: struct ip6_moptions im6o;
! 344: int icmp6len;
! 345: int maxlen;
! 346: caddr_t mac;
! 347: struct route_in6 ro;
! 348:
! 349: bzero(&ro, sizeof(ro));
! 350:
! 351: if (IN6_IS_ADDR_MULTICAST(taddr6))
! 352: return;
! 353:
! 354: /* estimate the size of message */
! 355: maxlen = sizeof(*ip6) + sizeof(*nd_ns);
! 356: maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
! 357: #ifdef DIAGNOSTIC
! 358: if (max_linkhdr + maxlen >= MCLBYTES) {
! 359: printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES "
! 360: "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
! 361: panic("nd6_ns_output: insufficient MCLBYTES");
! 362: /* NOTREACHED */
! 363: }
! 364: #endif
! 365:
! 366: MGETHDR(m, M_DONTWAIT, MT_DATA);
! 367: if (m && max_linkhdr + maxlen >= MHLEN) {
! 368: MCLGET(m, M_DONTWAIT);
! 369: if ((m->m_flags & M_EXT) == 0) {
! 370: m_free(m);
! 371: m = NULL;
! 372: }
! 373: }
! 374: if (m == NULL)
! 375: return;
! 376: m->m_pkthdr.rcvif = NULL;
! 377:
! 378: if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
! 379: m->m_flags |= M_MCAST;
! 380: im6o.im6o_multicast_ifp = ifp;
! 381: im6o.im6o_multicast_hlim = 255;
! 382: im6o.im6o_multicast_loop = 0;
! 383: }
! 384:
! 385: icmp6len = sizeof(*nd_ns);
! 386: m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len;
! 387: m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */
! 388:
! 389: /* fill neighbor solicitation packet */
! 390: ip6 = mtod(m, struct ip6_hdr *);
! 391: ip6->ip6_flow = 0;
! 392: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
! 393: ip6->ip6_vfc |= IPV6_VERSION;
! 394: /* ip6->ip6_plen will be set later */
! 395: ip6->ip6_nxt = IPPROTO_ICMPV6;
! 396: ip6->ip6_hlim = 255;
! 397: /* determine the source and destination addresses */
! 398: bzero(&src_sa, sizeof(src_sa));
! 399: bzero(&dst_sa, sizeof(dst_sa));
! 400: src_sa.sin6_family = dst_sa.sin6_family = AF_INET6;
! 401: src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6);
! 402: if (daddr6)
! 403: dst_sa.sin6_addr = *daddr6;
! 404: else {
! 405: dst_sa.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
! 406: dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
! 407: dst_sa.sin6_addr.s6_addr32[1] = 0;
! 408: dst_sa.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
! 409: dst_sa.sin6_addr.s6_addr32[3] = taddr6->s6_addr32[3];
! 410: dst_sa.sin6_addr.s6_addr8[12] = 0xff;
! 411: }
! 412: ip6->ip6_dst = dst_sa.sin6_addr;
! 413: if (!dad) {
! 414: /*
! 415: * RFC2461 7.2.2:
! 416: * "If the source address of the packet prompting the
! 417: * solicitation is the same as one of the addresses assigned
! 418: * to the outgoing interface, that address SHOULD be placed
! 419: * in the IP Source Address of the outgoing solicitation.
! 420: * Otherwise, any one of the addresses assigned to the
! 421: * interface should be used."
! 422: *
! 423: * We use the source address for the prompting packet
! 424: * (saddr6), if:
! 425: * - saddr6 is given from the caller (by giving "ln"), and
! 426: * - saddr6 belongs to the outgoing interface.
! 427: * Otherwise, we perform the source address selection as usual.
! 428: */
! 429: struct ip6_hdr *hip6; /* hold ip6 */
! 430: struct in6_addr *saddr6;
! 431:
! 432: if (ln && ln->ln_hold) {
! 433: hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
! 434: /* XXX pullup? */
! 435: if (sizeof(*hip6) < ln->ln_hold->m_len)
! 436: saddr6 = &hip6->ip6_src;
! 437: else
! 438: saddr6 = NULL;
! 439: } else
! 440: saddr6 = NULL;
! 441: if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6))
! 442: src_sa.sin6_addr = *saddr6;
! 443: else {
! 444: struct in6_addr *src0;
! 445: int error;
! 446:
! 447: bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
! 448: src0 = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL,
! 449: &error);
! 450: if (src0 == NULL) {
! 451: nd6log((LOG_DEBUG,
! 452: "nd6_ns_output: source can't be "
! 453: "determined: dst=%s, error=%d\n",
! 454: ip6_sprintf(&dst_sa.sin6_addr), error));
! 455: goto bad;
! 456: }
! 457: src_sa.sin6_addr = *src0;
! 458: }
! 459: } else {
! 460: /*
! 461: * Source address for DAD packet must always be IPv6
! 462: * unspecified address. (0::0)
! 463: * We actually don't have to 0-clear the address (we did it
! 464: * above), but we do so here explicitly to make the intention
! 465: * clearer.
! 466: */
! 467: bzero(&src_sa.sin6_addr, sizeof(src_sa.sin6_addr));
! 468: }
! 469: ip6->ip6_src = src_sa.sin6_addr;
! 470: nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
! 471: nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
! 472: nd_ns->nd_ns_code = 0;
! 473: nd_ns->nd_ns_reserved = 0;
! 474: nd_ns->nd_ns_target = *taddr6;
! 475:
! 476: if (IN6_IS_SCOPE_EMBED(&nd_ns->nd_ns_target))
! 477: nd_ns->nd_ns_target.s6_addr16[1] = 0;
! 478:
! 479: /*
! 480: * Add source link-layer address option.
! 481: *
! 482: * spec implementation
! 483: * --- ---
! 484: * DAD packet MUST NOT do not add the option
! 485: * there's no link layer address:
! 486: * impossible do not add the option
! 487: * there's link layer address:
! 488: * Multicast NS MUST add one add the option
! 489: * Unicast NS SHOULD add one add the option
! 490: */
! 491: if (!dad && (mac = nd6_ifptomac(ifp))) {
! 492: int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
! 493: struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
! 494: /* 8 byte alignments... */
! 495: optlen = (optlen + 7) & ~7;
! 496:
! 497: m->m_pkthdr.len += optlen;
! 498: m->m_len += optlen;
! 499: icmp6len += optlen;
! 500: bzero((caddr_t)nd_opt, optlen);
! 501: nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
! 502: nd_opt->nd_opt_len = optlen >> 3;
! 503: bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
! 504: }
! 505:
! 506: ip6->ip6_plen = htons((u_short)icmp6len);
! 507: nd_ns->nd_ns_cksum = 0;
! 508: nd_ns->nd_ns_cksum =
! 509: in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
! 510:
! 511: ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL);
! 512: icmp6_ifstat_inc(ifp, ifs6_out_msg);
! 513: icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
! 514: icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
! 515:
! 516: if (ro.ro_rt) { /* we don't cache this route. */
! 517: RTFREE(ro.ro_rt);
! 518: }
! 519: return;
! 520:
! 521: bad:
! 522: if (ro.ro_rt) {
! 523: RTFREE(ro.ro_rt);
! 524: }
! 525: m_freem(m);
! 526: return;
! 527: }
! 528:
! 529: /*
! 530: * Neighbor advertisement input handling.
! 531: *
! 532: * Based on RFC 2461
! 533: * Based on RFC 2462 (duplicated address detection)
! 534: *
! 535: * the following items are not implemented yet:
! 536: * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
! 537: * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
! 538: */
! 539: void
! 540: nd6_na_input(m, off, icmp6len)
! 541: struct mbuf *m;
! 542: int off, icmp6len;
! 543: {
! 544: struct ifnet *ifp = m->m_pkthdr.rcvif;
! 545: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
! 546: struct nd_neighbor_advert *nd_na;
! 547: #if 0
! 548: struct in6_addr saddr6 = ip6->ip6_src;
! 549: #endif
! 550: struct in6_addr daddr6 = ip6->ip6_dst;
! 551: struct in6_addr taddr6;
! 552: int flags;
! 553: int is_router;
! 554: int is_solicited;
! 555: int is_override;
! 556: char *lladdr = NULL;
! 557: int lladdrlen = 0;
! 558: struct ifaddr *ifa;
! 559: struct llinfo_nd6 *ln;
! 560: struct rtentry *rt;
! 561: struct sockaddr_dl *sdl;
! 562: union nd_opts ndopts;
! 563:
! 564: if (ip6->ip6_hlim != 255) {
! 565: nd6log((LOG_ERR,
! 566: "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n",
! 567: ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
! 568: ip6_sprintf(&ip6->ip6_dst), ifp->if_xname));
! 569: goto bad;
! 570: }
! 571:
! 572: IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);
! 573: if (nd_na == NULL) {
! 574: icmp6stat.icp6s_tooshort++;
! 575: return;
! 576: }
! 577: taddr6 = nd_na->nd_na_target;
! 578: flags = nd_na->nd_na_flags_reserved;
! 579: is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
! 580: is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
! 581: is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
! 582:
! 583: if (IN6_IS_SCOPE_EMBED(&taddr6))
! 584: taddr6.s6_addr16[1] = htons(ifp->if_index);
! 585:
! 586: if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
! 587: nd6log((LOG_ERR,
! 588: "nd6_na_input: invalid target address %s\n",
! 589: ip6_sprintf(&taddr6)));
! 590: goto bad;
! 591: }
! 592: if (is_solicited && IN6_IS_ADDR_MULTICAST(&daddr6)) {
! 593: nd6log((LOG_ERR,
! 594: "nd6_na_input: a solicited adv is multicasted\n"));
! 595: goto bad;
! 596: }
! 597:
! 598: icmp6len -= sizeof(*nd_na);
! 599: nd6_option_init(nd_na + 1, icmp6len, &ndopts);
! 600: if (nd6_options(&ndopts) < 0) {
! 601: nd6log((LOG_INFO,
! 602: "nd6_na_input: invalid ND option, ignored\n"));
! 603: /* nd6_options have incremented stats */
! 604: goto freeit;
! 605: }
! 606:
! 607: if (ndopts.nd_opts_tgt_lladdr) {
! 608: lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
! 609: lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
! 610: }
! 611:
! 612: ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
! 613:
! 614: /*
! 615: * Target address matches one of my interface address.
! 616: *
! 617: * If my address is tentative, this means that there's somebody
! 618: * already using the same address as mine. This indicates DAD failure.
! 619: * This is defined in RFC 2462.
! 620: *
! 621: * Otherwise, process as defined in RFC 2461.
! 622: */
! 623: if (ifa
! 624: && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
! 625: nd6_dad_na_input(ifa);
! 626: goto freeit;
! 627: }
! 628:
! 629: /* Just for safety, maybe unnecessary. */
! 630: if (ifa) {
! 631: log(LOG_ERR,
! 632: "nd6_na_input: duplicate IP6 address %s\n",
! 633: ip6_sprintf(&taddr6));
! 634: goto freeit;
! 635: }
! 636:
! 637: if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
! 638: nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s "
! 639: "(if %d, NA packet %d)\n", ip6_sprintf(&taddr6),
! 640: ifp->if_addrlen, lladdrlen - 2));
! 641: goto bad;
! 642: }
! 643:
! 644: /*
! 645: * If no neighbor cache entry is found, NA SHOULD silently be
! 646: * discarded.
! 647: */
! 648: rt = nd6_lookup(&taddr6, 0, ifp);
! 649: if ((rt == NULL) ||
! 650: ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
! 651: ((sdl = SDL(rt->rt_gateway)) == NULL))
! 652: goto freeit;
! 653:
! 654: if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
! 655: /*
! 656: * If the link-layer has address, and no lladdr option came,
! 657: * discard the packet.
! 658: */
! 659: if (ifp->if_addrlen && !lladdr)
! 660: goto freeit;
! 661:
! 662: /*
! 663: * Record link-layer address, and update the state.
! 664: */
! 665: sdl->sdl_alen = ifp->if_addrlen;
! 666: bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
! 667: if (is_solicited) {
! 668: ln->ln_state = ND6_LLINFO_REACHABLE;
! 669: ln->ln_byhint = 0;
! 670: if (!ND6_LLINFO_PERMANENT(ln)) {
! 671: nd6_llinfo_settimer(ln,
! 672: (long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
! 673: }
! 674: } else {
! 675: ln->ln_state = ND6_LLINFO_STALE;
! 676: nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
! 677: }
! 678: if ((ln->ln_router = is_router) != 0) {
! 679: /*
! 680: * This means a router's state has changed from
! 681: * non-reachable to probably reachable, and might
! 682: * affect the status of associated prefixes..
! 683: */
! 684: pfxlist_onlink_check();
! 685: }
! 686: } else {
! 687: int llchange;
! 688:
! 689: /*
! 690: * Check if the link-layer address has changed or not.
! 691: */
! 692: if (!lladdr)
! 693: llchange = 0;
! 694: else {
! 695: if (sdl->sdl_alen) {
! 696: if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
! 697: llchange = 1;
! 698: else
! 699: llchange = 0;
! 700: } else
! 701: llchange = 1;
! 702: }
! 703:
! 704: /*
! 705: * This is VERY complex. Look at it with care.
! 706: *
! 707: * override solicit lladdr llchange action
! 708: * (L: record lladdr)
! 709: *
! 710: * 0 0 n -- (2c)
! 711: * 0 0 y n (2b) L
! 712: * 0 0 y y (1) REACHABLE->STALE
! 713: * 0 1 n -- (2c) *->REACHABLE
! 714: * 0 1 y n (2b) L *->REACHABLE
! 715: * 0 1 y y (1) REACHABLE->STALE
! 716: * 1 0 n -- (2a)
! 717: * 1 0 y n (2a) L
! 718: * 1 0 y y (2a) L *->STALE
! 719: * 1 1 n -- (2a) *->REACHABLE
! 720: * 1 1 y n (2a) L *->REACHABLE
! 721: * 1 1 y y (2a) L *->REACHABLE
! 722: */
! 723: if (!is_override && (lladdr && llchange)) { /* (1) */
! 724: /*
! 725: * If state is REACHABLE, make it STALE.
! 726: * no other updates should be done.
! 727: */
! 728: if (ln->ln_state == ND6_LLINFO_REACHABLE) {
! 729: ln->ln_state = ND6_LLINFO_STALE;
! 730: nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
! 731: }
! 732: goto freeit;
! 733: } else if (is_override /* (2a) */
! 734: || (!is_override && (lladdr && !llchange)) /* (2b) */
! 735: || !lladdr) { /* (2c) */
! 736: /*
! 737: * Update link-local address, if any.
! 738: */
! 739: if (lladdr) {
! 740: sdl->sdl_alen = ifp->if_addrlen;
! 741: bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
! 742: }
! 743:
! 744: /*
! 745: * If solicited, make the state REACHABLE.
! 746: * If not solicited and the link-layer address was
! 747: * changed, make it STALE.
! 748: */
! 749: if (is_solicited) {
! 750: ln->ln_state = ND6_LLINFO_REACHABLE;
! 751: ln->ln_byhint = 0;
! 752: if (!ND6_LLINFO_PERMANENT(ln)) {
! 753: nd6_llinfo_settimer(ln,
! 754: (long)ND_IFINFO(ifp)->reachable * hz);
! 755: }
! 756: } else {
! 757: if (lladdr && llchange) {
! 758: ln->ln_state = ND6_LLINFO_STALE;
! 759: nd6_llinfo_settimer(ln,
! 760: (long)nd6_gctimer * hz);
! 761: }
! 762: }
! 763: }
! 764:
! 765: if (ln->ln_router && !is_router) {
! 766: /*
! 767: * The peer dropped the router flag.
! 768: * Remove the sender from the Default Router List and
! 769: * update the Destination Cache entries.
! 770: */
! 771: struct nd_defrouter *dr;
! 772: struct in6_addr *in6;
! 773: int s;
! 774:
! 775: in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
! 776:
! 777: /*
! 778: * Lock to protect the default router list.
! 779: * XXX: this might be unnecessary, since this function
! 780: * is only called under the network software interrupt
! 781: * context. However, we keep it just for safety.
! 782: */
! 783: s = splsoftnet();
! 784: dr = defrouter_lookup(in6, rt->rt_ifp);
! 785: if (dr)
! 786: defrtrlist_del(dr);
! 787: else if (!ip6_forwarding) {
! 788: /*
! 789: * Even if the neighbor is not in the default
! 790: * router list, the neighbor may be used
! 791: * as a next hop for some destinations
! 792: * (e.g. redirect case). So we must
! 793: * call rt6_flush explicitly.
! 794: */
! 795: rt6_flush(&ip6->ip6_src, rt->rt_ifp);
! 796: }
! 797: splx(s);
! 798: }
! 799: ln->ln_router = is_router;
! 800: }
! 801: rt->rt_flags &= ~RTF_REJECT;
! 802: ln->ln_asked = 0;
! 803: if (ln->ln_hold) {
! 804: /*
! 805: * we assume ifp is not a loopback here, so just set the 2nd
! 806: * argument as the 1st one.
! 807: */
! 808: nd6_output(ifp, ifp, ln->ln_hold,
! 809: (struct sockaddr_in6 *)rt_key(rt), rt);
! 810: ln->ln_hold = NULL;
! 811: }
! 812:
! 813: freeit:
! 814: m_freem(m);
! 815: return;
! 816:
! 817: bad:
! 818: icmp6stat.icp6s_badna++;
! 819: m_freem(m);
! 820: }
! 821:
! 822: /*
! 823: * Neighbor advertisement output handling.
! 824: *
! 825: * Based on RFC 2461
! 826: *
! 827: * the following items are not implemented yet:
! 828: * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
! 829: * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
! 830: */
! 831: void
! 832: nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
! 833: struct ifnet *ifp;
! 834: struct in6_addr *daddr6, *taddr6;
! 835: u_long flags;
! 836: int tlladdr; /* 1 if include target link-layer address */
! 837: struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */
! 838: {
! 839: struct mbuf *m;
! 840: struct ip6_hdr *ip6;
! 841: struct nd_neighbor_advert *nd_na;
! 842: struct ip6_moptions im6o;
! 843: struct sockaddr_in6 src_sa, dst_sa;
! 844: struct in6_addr *src0;
! 845: int icmp6len, maxlen, error;
! 846: caddr_t mac;
! 847: struct route_in6 ro;
! 848:
! 849: mac = NULL;
! 850: bzero(&ro, sizeof(ro));
! 851:
! 852: /* estimate the size of message */
! 853: maxlen = sizeof(*ip6) + sizeof(*nd_na);
! 854: maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
! 855: #ifdef DIAGNOSTIC
! 856: if (max_linkhdr + maxlen >= MCLBYTES) {
! 857: printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES "
! 858: "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
! 859: panic("nd6_na_output: insufficient MCLBYTES");
! 860: /* NOTREACHED */
! 861: }
! 862: #endif
! 863:
! 864: MGETHDR(m, M_DONTWAIT, MT_DATA);
! 865: if (m && max_linkhdr + maxlen >= MHLEN) {
! 866: MCLGET(m, M_DONTWAIT);
! 867: if ((m->m_flags & M_EXT) == 0) {
! 868: m_free(m);
! 869: m = NULL;
! 870: }
! 871: }
! 872: if (m == NULL)
! 873: return;
! 874: m->m_pkthdr.rcvif = NULL;
! 875:
! 876: if (IN6_IS_ADDR_MULTICAST(daddr6)) {
! 877: m->m_flags |= M_MCAST;
! 878: im6o.im6o_multicast_ifp = ifp;
! 879: im6o.im6o_multicast_hlim = 255;
! 880: im6o.im6o_multicast_loop = 0;
! 881: }
! 882:
! 883: icmp6len = sizeof(*nd_na);
! 884: m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
! 885: m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */
! 886:
! 887: /* fill neighbor advertisement packet */
! 888: ip6 = mtod(m, struct ip6_hdr *);
! 889: ip6->ip6_flow = 0;
! 890: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
! 891: ip6->ip6_vfc |= IPV6_VERSION;
! 892: ip6->ip6_nxt = IPPROTO_ICMPV6;
! 893: ip6->ip6_hlim = 255;
! 894: bzero(&src_sa, sizeof(src_sa));
! 895: bzero(&dst_sa, sizeof(dst_sa));
! 896: src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6);
! 897: src_sa.sin6_family = dst_sa.sin6_family = AF_INET6;
! 898: dst_sa.sin6_addr = *daddr6;
! 899: if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
! 900: /* reply to DAD */
! 901: dst_sa.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
! 902: dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
! 903: dst_sa.sin6_addr.s6_addr32[1] = 0;
! 904: dst_sa.sin6_addr.s6_addr32[2] = 0;
! 905: dst_sa.sin6_addr.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
! 906:
! 907: flags &= ~ND_NA_FLAG_SOLICITED;
! 908: }
! 909: ip6->ip6_dst = dst_sa.sin6_addr;
! 910:
! 911: /*
! 912: * Select a source whose scope is the same as that of the dest.
! 913: */
! 914: bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
! 915: src0 = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &error);
! 916: if (src0 == NULL) {
! 917: nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
! 918: "determined: dst=%s, error=%d\n",
! 919: ip6_sprintf(&dst_sa.sin6_addr), error));
! 920: goto bad;
! 921: }
! 922: src_sa.sin6_addr = *src0;
! 923: ip6->ip6_src = src_sa.sin6_addr;
! 924: nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
! 925: nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
! 926: nd_na->nd_na_code = 0;
! 927: nd_na->nd_na_target = *taddr6;
! 928: if (IN6_IS_SCOPE_EMBED(&nd_na->nd_na_target))
! 929: nd_na->nd_na_target.s6_addr16[1] = 0;
! 930:
! 931: /*
! 932: * "tlladdr" indicates NS's condition for adding tlladdr or not.
! 933: * see nd6_ns_input() for details.
! 934: * Basically, if NS packet is sent to unicast/anycast addr,
! 935: * target lladdr option SHOULD NOT be included.
! 936: */
! 937: if (tlladdr) {
! 938: /*
! 939: * sdl0 != NULL indicates proxy NA. If we do proxy, use
! 940: * lladdr in sdl0. If we are not proxying (sending NA for
! 941: * my address) use lladdr configured for the interface.
! 942: */
! 943: if (sdl0 == NULL) {
! 944: mac = nd6_ifptomac(ifp);
! 945: } else if (sdl0->sa_family == AF_LINK) {
! 946: struct sockaddr_dl *sdl;
! 947: sdl = (struct sockaddr_dl *)sdl0;
! 948: if (sdl->sdl_alen == ifp->if_addrlen)
! 949: mac = LLADDR(sdl);
! 950: }
! 951: }
! 952: if (tlladdr && mac) {
! 953: int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
! 954: struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
! 955:
! 956: /* roundup to 8 bytes alignment! */
! 957: optlen = (optlen + 7) & ~7;
! 958:
! 959: m->m_pkthdr.len += optlen;
! 960: m->m_len += optlen;
! 961: icmp6len += optlen;
! 962: bzero((caddr_t)nd_opt, optlen);
! 963: nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
! 964: nd_opt->nd_opt_len = optlen >> 3;
! 965: bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
! 966: } else
! 967: flags &= ~ND_NA_FLAG_OVERRIDE;
! 968:
! 969: ip6->ip6_plen = htons((u_short)icmp6len);
! 970: nd_na->nd_na_flags_reserved = flags;
! 971: nd_na->nd_na_cksum = 0;
! 972: nd_na->nd_na_cksum =
! 973: in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
! 974:
! 975: ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL);
! 976:
! 977: icmp6_ifstat_inc(ifp, ifs6_out_msg);
! 978: icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
! 979: icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
! 980:
! 981: if (ro.ro_rt) { /* we don't cache this route. */
! 982: RTFREE(ro.ro_rt);
! 983: }
! 984: return;
! 985:
! 986: bad:
! 987: if (ro.ro_rt) {
! 988: RTFREE(ro.ro_rt);
! 989: }
! 990: m_freem(m);
! 991: return;
! 992: }
! 993:
! 994: caddr_t
! 995: nd6_ifptomac(ifp)
! 996: struct ifnet *ifp;
! 997: {
! 998: switch (ifp->if_type) {
! 999: case IFT_ETHER:
! 1000: case IFT_FDDI:
! 1001: case IFT_IEEE1394:
! 1002: case IFT_PROPVIRTUAL:
! 1003: case IFT_CARP:
! 1004: case IFT_L2VLAN:
! 1005: case IFT_IEEE80211:
! 1006: return ((caddr_t)(ifp + 1));
! 1007: default:
! 1008: return NULL;
! 1009: }
! 1010: }
! 1011:
! 1012: TAILQ_HEAD(dadq_head, dadq);
! 1013: struct dadq {
! 1014: TAILQ_ENTRY(dadq) dad_list;
! 1015: struct ifaddr *dad_ifa;
! 1016: int dad_count; /* max NS to send */
! 1017: int dad_ns_tcount; /* # of trials to send NS */
! 1018: int dad_ns_ocount; /* NS sent so far */
! 1019: int dad_ns_icount;
! 1020: int dad_na_icount;
! 1021: struct timeout dad_timer_ch;
! 1022: };
! 1023:
! 1024: static struct dadq_head dadq;
! 1025: static int dad_init = 0;
! 1026:
! 1027: static struct dadq *
! 1028: nd6_dad_find(ifa)
! 1029: struct ifaddr *ifa;
! 1030: {
! 1031: struct dadq *dp;
! 1032:
! 1033: TAILQ_FOREACH(dp, &dadq, dad_list) {
! 1034: if (dp->dad_ifa == ifa)
! 1035: return dp;
! 1036: }
! 1037: return NULL;
! 1038: }
! 1039:
! 1040: static void
! 1041: nd6_dad_starttimer(dp, ticks)
! 1042: struct dadq *dp;
! 1043: int ticks;
! 1044: {
! 1045:
! 1046: timeout_set(&dp->dad_timer_ch, (void (*)(void *))nd6_dad_timer,
! 1047: (void *)dp->dad_ifa);
! 1048: timeout_add(&dp->dad_timer_ch, ticks);
! 1049: }
! 1050:
! 1051: static void
! 1052: nd6_dad_stoptimer(dp)
! 1053: struct dadq *dp;
! 1054: {
! 1055:
! 1056: timeout_del(&dp->dad_timer_ch);
! 1057: }
! 1058:
! 1059: /*
! 1060: * Start Duplicated Address Detection (DAD) for specified interface address.
! 1061: */
! 1062: void
! 1063: nd6_dad_start(ifa, tick)
! 1064: struct ifaddr *ifa;
! 1065: int *tick; /* minimum delay ticks for IFF_UP event */
! 1066: {
! 1067: struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
! 1068: struct dadq *dp;
! 1069:
! 1070: if (!dad_init) {
! 1071: TAILQ_INIT(&dadq);
! 1072: dad_init++;
! 1073: }
! 1074:
! 1075: /*
! 1076: * If we don't need DAD, don't do it.
! 1077: * There are several cases:
! 1078: * - DAD is disabled (ip6_dad_count == 0)
! 1079: * - the interface address is anycast
! 1080: */
! 1081: if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) {
! 1082: log(LOG_DEBUG,
! 1083: "nd6_dad_start: called with non-tentative address "
! 1084: "%s(%s)\n",
! 1085: ip6_sprintf(&ia->ia_addr.sin6_addr),
! 1086: ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
! 1087: return;
! 1088: }
! 1089: if (ia->ia6_flags & IN6_IFF_ANYCAST) {
! 1090: ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
! 1091: return;
! 1092: }
! 1093: if (!ip6_dad_count) {
! 1094: ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
! 1095: return;
! 1096: }
! 1097: if (!ifa->ifa_ifp)
! 1098: panic("nd6_dad_start: ifa->ifa_ifp == NULL");
! 1099: if (!(ifa->ifa_ifp->if_flags & IFF_UP))
! 1100: return;
! 1101: if (nd6_dad_find(ifa) != NULL) {
! 1102: /* DAD already in progress */
! 1103: return;
! 1104: }
! 1105:
! 1106: dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT);
! 1107: if (dp == NULL) {
! 1108: log(LOG_ERR, "nd6_dad_start: memory allocation failed for "
! 1109: "%s(%s)\n",
! 1110: ip6_sprintf(&ia->ia_addr.sin6_addr),
! 1111: ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
! 1112: return;
! 1113: }
! 1114: bzero(dp, sizeof(*dp));
! 1115: bzero(&dp->dad_timer_ch, sizeof(dp->dad_timer_ch));
! 1116: TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
! 1117:
! 1118: nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", ifa->ifa_ifp->if_xname,
! 1119: ip6_sprintf(&ia->ia_addr.sin6_addr)));
! 1120:
! 1121: /*
! 1122: * Send NS packet for DAD, ip6_dad_count times.
! 1123: * Note that we must delay the first transmission, if this is the
! 1124: * first packet to be sent from the interface after interface
! 1125: * (re)initialization.
! 1126: */
! 1127: dp->dad_ifa = ifa;
! 1128: ifa->ifa_refcnt++; /* just for safety */
! 1129: dp->dad_count = ip6_dad_count;
! 1130: dp->dad_ns_icount = dp->dad_na_icount = 0;
! 1131: dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
! 1132: if (tick == NULL) {
! 1133: nd6_dad_ns_output(dp, ifa);
! 1134: nd6_dad_starttimer(dp,
! 1135: (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
! 1136: } else {
! 1137: int ntick;
! 1138:
! 1139: if (*tick == 0)
! 1140: ntick = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz);
! 1141: else
! 1142: ntick = *tick + arc4random() % (hz / 2);
! 1143: *tick = ntick;
! 1144: nd6_dad_starttimer(dp, ntick);
! 1145: }
! 1146: }
! 1147:
! 1148: /*
! 1149: * terminate DAD unconditionally. used for address removals.
! 1150: */
! 1151: void
! 1152: nd6_dad_stop(ifa)
! 1153: struct ifaddr *ifa;
! 1154: {
! 1155: struct dadq *dp;
! 1156:
! 1157: if (!dad_init)
! 1158: return;
! 1159: dp = nd6_dad_find(ifa);
! 1160: if (!dp) {
! 1161: /* DAD wasn't started yet */
! 1162: return;
! 1163: }
! 1164:
! 1165: nd6_dad_stoptimer(dp);
! 1166:
! 1167: TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
! 1168: free(dp, M_IP6NDP);
! 1169: dp = NULL;
! 1170: IFAFREE(ifa);
! 1171: }
! 1172:
! 1173: static void
! 1174: nd6_dad_timer(ifa)
! 1175: struct ifaddr *ifa;
! 1176: {
! 1177: int s;
! 1178: struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
! 1179: struct dadq *dp;
! 1180:
! 1181: s = splsoftnet(); /* XXX */
! 1182:
! 1183: /* Sanity check */
! 1184: if (ia == NULL) {
! 1185: log(LOG_ERR, "nd6_dad_timer: called with null parameter\n");
! 1186: goto done;
! 1187: }
! 1188: dp = nd6_dad_find(ifa);
! 1189: if (dp == NULL) {
! 1190: log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n");
! 1191: goto done;
! 1192: }
! 1193: if (ia->ia6_flags & IN6_IFF_DUPLICATED) {
! 1194: log(LOG_ERR, "nd6_dad_timer: called with duplicated address "
! 1195: "%s(%s)\n",
! 1196: ip6_sprintf(&ia->ia_addr.sin6_addr),
! 1197: ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
! 1198: goto done;
! 1199: }
! 1200: if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
! 1201: log(LOG_ERR, "nd6_dad_timer: called with non-tentative address "
! 1202: "%s(%s)\n",
! 1203: ip6_sprintf(&ia->ia_addr.sin6_addr),
! 1204: ifa->ifa_ifp ? ifa->ifa_ifp->if_xname : "???");
! 1205: goto done;
! 1206: }
! 1207:
! 1208: /* timeouted with IFF_{RUNNING,UP} check */
! 1209: if (dp->dad_ns_tcount > dad_maxtry) {
! 1210: nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n",
! 1211: ifa->ifa_ifp->if_xname));
! 1212:
! 1213: TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
! 1214: free(dp, M_IP6NDP);
! 1215: dp = NULL;
! 1216: IFAFREE(ifa);
! 1217: goto done;
! 1218: }
! 1219:
! 1220: /* Need more checks? */
! 1221: if (dp->dad_ns_ocount < dp->dad_count) {
! 1222: /*
! 1223: * We have more NS to go. Send NS packet for DAD.
! 1224: */
! 1225: nd6_dad_ns_output(dp, ifa);
! 1226: nd6_dad_starttimer(dp,
! 1227: (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
! 1228: } else {
! 1229: /*
! 1230: * We have transmitted sufficient number of DAD packets.
! 1231: * See what we've got.
! 1232: */
! 1233: int duplicate;
! 1234:
! 1235: duplicate = 0;
! 1236:
! 1237: if (dp->dad_na_icount) {
! 1238: /*
! 1239: * the check is in nd6_dad_na_input(),
! 1240: * but just in case
! 1241: */
! 1242: duplicate++;
! 1243: }
! 1244:
! 1245: if (dp->dad_ns_icount) {
! 1246: /* We've seen NS, means DAD has failed. */
! 1247: duplicate++;
! 1248: }
! 1249:
! 1250: if (duplicate) {
! 1251: /* (*dp) will be freed in nd6_dad_duplicated() */
! 1252: dp = NULL;
! 1253: nd6_dad_duplicated(ifa);
! 1254: } else {
! 1255: /*
! 1256: * We are done with DAD. No NA came, no NS came.
! 1257: * duplicated address found.
! 1258: */
! 1259: ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
! 1260:
! 1261: nd6log((LOG_DEBUG,
! 1262: "%s: DAD complete for %s - no duplicates found\n",
! 1263: ifa->ifa_ifp->if_xname,
! 1264: ip6_sprintf(&ia->ia_addr.sin6_addr)));
! 1265:
! 1266: TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
! 1267: free(dp, M_IP6NDP);
! 1268: dp = NULL;
! 1269: IFAFREE(ifa);
! 1270: }
! 1271: }
! 1272:
! 1273: done:
! 1274: splx(s);
! 1275: }
! 1276:
! 1277: void
! 1278: nd6_dad_duplicated(ifa)
! 1279: struct ifaddr *ifa;
! 1280: {
! 1281: struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
! 1282: struct dadq *dp;
! 1283:
! 1284: dp = nd6_dad_find(ifa);
! 1285: if (dp == NULL) {
! 1286: log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n");
! 1287: return;
! 1288: }
! 1289:
! 1290: log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
! 1291: "NS in/out=%d/%d, NA in=%d\n",
! 1292: ifa->ifa_ifp->if_xname, ip6_sprintf(&ia->ia_addr.sin6_addr),
! 1293: dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
! 1294:
! 1295: ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
! 1296: ia->ia6_flags |= IN6_IFF_DUPLICATED;
! 1297:
! 1298: /* We are done with DAD, with duplicated address found. (failure) */
! 1299: nd6_dad_stoptimer(dp);
! 1300:
! 1301: log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
! 1302: ifa->ifa_ifp->if_xname, ip6_sprintf(&ia->ia_addr.sin6_addr));
! 1303: log(LOG_ERR, "%s: manual intervention required\n",
! 1304: ifa->ifa_ifp->if_xname);
! 1305:
! 1306: TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
! 1307: free(dp, M_IP6NDP);
! 1308: dp = NULL;
! 1309: IFAFREE(ifa);
! 1310: }
! 1311:
! 1312: static void
! 1313: nd6_dad_ns_output(dp, ifa)
! 1314: struct dadq *dp;
! 1315: struct ifaddr *ifa;
! 1316: {
! 1317: struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
! 1318: struct ifnet *ifp = ifa->ifa_ifp;
! 1319:
! 1320: dp->dad_ns_tcount++;
! 1321: if ((ifp->if_flags & IFF_UP) == 0) {
! 1322: #if 0
! 1323: printf("%s: interface down?\n", ifp->if_xname);
! 1324: #endif
! 1325: return;
! 1326: }
! 1327: if ((ifp->if_flags & IFF_RUNNING) == 0) {
! 1328: #if 0
! 1329: printf("%s: interface not running?\n", ifp->if_xname);
! 1330: #endif
! 1331: return;
! 1332: }
! 1333:
! 1334: dp->dad_ns_ocount++;
! 1335: nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1);
! 1336: }
! 1337:
! 1338: static void
! 1339: nd6_dad_ns_input(ifa)
! 1340: struct ifaddr *ifa;
! 1341: {
! 1342: struct in6_ifaddr *ia;
! 1343: struct ifnet *ifp;
! 1344: struct in6_addr *taddr6;
! 1345: struct dadq *dp;
! 1346: int duplicate;
! 1347:
! 1348: if (!ifa)
! 1349: panic("ifa == NULL in nd6_dad_ns_input");
! 1350:
! 1351: ia = (struct in6_ifaddr *)ifa;
! 1352: ifp = ifa->ifa_ifp;
! 1353: taddr6 = &ia->ia_addr.sin6_addr;
! 1354: duplicate = 0;
! 1355: dp = nd6_dad_find(ifa);
! 1356:
! 1357: /* Quickhack - completely ignore DAD NS packets */
! 1358: if (dad_ignore_ns) {
! 1359: nd6log((LOG_INFO,
! 1360: "nd6_dad_ns_input: ignoring DAD NS packet for "
! 1361: "address %s(%s)\n", ip6_sprintf(taddr6),
! 1362: ifa->ifa_ifp->if_xname));
! 1363: return;
! 1364: }
! 1365:
! 1366: /*
! 1367: * if I'm yet to start DAD, someone else started using this address
! 1368: * first. I have a duplicate and you win.
! 1369: */
! 1370: if (!dp || dp->dad_ns_ocount == 0)
! 1371: duplicate++;
! 1372:
! 1373: /* XXX more checks for loopback situation - see nd6_dad_timer too */
! 1374:
! 1375: if (duplicate) {
! 1376: dp = NULL; /* will be freed in nd6_dad_duplicated() */
! 1377: nd6_dad_duplicated(ifa);
! 1378: } else {
! 1379: /*
! 1380: * not sure if I got a duplicate.
! 1381: * increment ns count and see what happens.
! 1382: */
! 1383: if (dp)
! 1384: dp->dad_ns_icount++;
! 1385: }
! 1386: }
! 1387:
! 1388: static void
! 1389: nd6_dad_na_input(ifa)
! 1390: struct ifaddr *ifa;
! 1391: {
! 1392: struct dadq *dp;
! 1393:
! 1394: if (!ifa)
! 1395: panic("ifa == NULL in nd6_dad_na_input");
! 1396:
! 1397: dp = nd6_dad_find(ifa);
! 1398: if (dp)
! 1399: dp->dad_na_icount++;
! 1400:
! 1401: /* remove the address. */
! 1402: nd6_dad_duplicated(ifa);
! 1403: }
CVSweb