]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/in6_ifattach.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_ifattach.c
index b874f6fc2d29254fd01584191ba2b44e3fc10d5a..8b5379f53b71d56528924b8cb897d1a2d682a739 100644 (file)
@@ -1,4 +1,30 @@
-/*     $KAME: in6_ifattach.c,v 1.41 2000/03/16 07:05:34 jinmei Exp $   */
+/*
+ * Copyright (c) 2003-2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/socket.h>
+#include <sys/socketvar.h>
 #include <sys/sockio.h>
 #include <sys/kernel.h>
-#ifdef __bsdi__
-#include <crypto/md5.h>
-#elif defined(__OpenBSD__)
-#include <sys/md5k.h>
-#else
-#include <sys/md5.h>
-#endif
+#include <sys/syslog.h>
+#include <libkern/crypto/sha1.h>
+#include <libkern/OSAtomic.h>
+#include <kern/locks.h>
 
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #include <net/route.h>
+#include <net/kpi_protocol.h>
 
 #include <netinet/in.h>
 #include <netinet/in_var.h>
-#ifndef __NetBSD__
 #include <netinet/if_ether.h>
-#endif
+#include <netinet/in_pcb.h>
+#include <netinet/icmp6.h>
 
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/in6_pcb.h>
 #include <netinet6/in6_ifattach.h>
 #include <netinet6/ip6_var.h>
 #include <netinet6/nd6.h>
+#include <netinet6/scope6_var.h>
 
 #include <net/net_osdep.h>
+#include <dev/random/randomdev.h>
 
-static struct in6_addr llsol;
-
-struct in6_ifstat **in6_ifstat = NULL;
-struct icmp6_ifstat **icmp6_ifstat = NULL;
-size_t in6_ifstatmax = 0;
-size_t icmp6_ifstatmax = 0;
-unsigned long in6_maxmtu = 0;
+u_int32_t in6_maxmtu = 0;
+extern lck_mtx_t *nd6_mutex;
 
-int found_first_ifid = 0;
-#define IFID_LEN 8
-static u_int8_t first_ifid[IFID_LEN];
-
-static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
-static int gen_rand_eui64 __P((u_int8_t *));
+#if IP6_AUTO_LINKLOCAL
+int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL;
+#else
+int ip6_auto_linklocal = 1;    /* enable by default */
+#endif
 
-#define DEBUG 1
+extern struct inpcbinfo udbinfo;
+extern struct inpcbinfo ripcbinfo;
 
-static int
-laddr_to_eui64(dst, src, len)
-       u_int8_t *dst;
-       u_int8_t *src;
-       size_t len;
-{
-       static u_int8_t zero[8];
-
-       bzero(zero, sizeof(zero));
-
-       switch (len) {
-       case 6:
-               if (bcmp(zero, src, 6) == 0)
-                       return EINVAL;
-               dst[0] = src[0];
-               dst[1] = src[1];
-               dst[2] = src[2];
-               dst[3] = 0xff;
-               dst[4] = 0xfe;
-               dst[5] = src[3];
-               dst[6] = src[4];
-               dst[7] = src[5];
-               break;
-       case 8:
-               if (bcmp(zero, src, 8) == 0)
-                       return EINVAL;
-               bcopy(src, dst, len);
-               break;
-       default:
-               return EINVAL;
-       }
+static const unsigned int in6_extra_size = sizeof(struct in6_ifextra);
+static const unsigned int in6_extra_bufsize = in6_extra_size + 
+    sizeof(void *) + sizeof(uint64_t);
 
-       return 0;
-}
+static int get_rand_iid(struct ifnet *, struct in6_addr *);
+static int in6_generate_tmp_iid(u_int8_t *, const u_int8_t *, u_int8_t *);
+static int in6_select_iid_from_all_hw(struct ifnet *, struct ifnet *,
+    struct in6_addr *);
+static int in6_ifattach_linklocal(struct ifnet *, struct in6_aliasreq *);
+static int in6_ifattach_loopback(struct ifnet *);
 
 /*
  * Generate a last-resort interface identifier, when the machine has no
  * IEEE802/EUI64 address sources.
- * The address should be random, and should not change across reboot.
+ * The goal here is to get an interface identifier that is
+ * (1) random enough and (2) does not change across reboot.
+ * We currently use SHA1(hostname) for it.
+ *
+ * in6 - upper 64bits are preserved
  */
 static int
-gen_rand_eui64(dst)
-       u_int8_t *dst;
+get_rand_iid(
+       __unused struct ifnet *ifp,
+       struct in6_addr *in6)   /* upper 64bits are preserved */
 {
-       MD5_CTX ctxt;
-       u_int8_t digest[16];
-#if defined(__FreeBSD__) || defined (__APPLE__)
-       int hostnamelen = strlen(hostname);
-#endif
+       SHA1_CTX ctxt;
+       u_int8_t digest[SHA1_RESULTLEN];
+       int hostnlen    = strlen(hostname);
 
-       /* generate 8bytes of pseudo-random value. */
-       bzero(&ctxt, sizeof(ctxt));
-       MD5Init(&ctxt);
-       MD5Update(&ctxt, hostname, hostnamelen);
-       MD5Final(digest, &ctxt);
+       /* generate 8 bytes of pseudo-random value. */
+       bzero(&ctxt, sizeof (ctxt));
+       SHA1Init(&ctxt);
+       SHA1Update(&ctxt, hostname, hostnlen);
+       SHA1Final(digest, &ctxt);
 
-       /* assumes sizeof(digest) > sizeof(first_ifid) */
-       bcopy(digest, dst, 8);
+       /* assumes sizeof (digest) > sizeof (iid) */
+       bcopy(digest, &in6->s6_addr[8], 8);
 
        /* make sure to set "u" bit to local, and "g" bit to individual. */
-       dst[0] &= 0xfe;
-       dst[0] |= 0x02;         /* EUI64 "local" */
+       in6->s6_addr[8] &= ~ND6_EUI64_GBIT;     /* g bit to "individual" */
+       in6->s6_addr[8] |= ND6_EUI64_UBIT;      /* u bit to "local" */
+
+       /* convert EUI64 into IPv6 interface identifier */
+       ND6_EUI64_TO_IFID(in6);
+
+       return (0);
+}
+
+static int
+in6_generate_tmp_iid(
+       u_int8_t *seed0,
+       const u_int8_t *seed1,
+       u_int8_t *ret)
+{
+       SHA1_CTX ctxt;
+       u_int8_t seed[16], nullbuf[8], digest[SHA1_RESULTLEN];
+       u_int32_t val32;
+       struct timeval tv;
+
+       /* If there's no history, start with a random seed. */
+       bzero(nullbuf, sizeof (nullbuf));
+       if (bcmp(nullbuf, seed0, sizeof (nullbuf)) == 0) {
+               int i;
+
+               for (i = 0; i < 2; i++) {
+                       getmicrotime(&tv);
+                       val32 = RandomULong() ^ tv.tv_usec;
+                       bcopy(&val32, seed + sizeof (val32) * i,
+                           sizeof (val32));
+               }
+       } else {
+               bcopy(seed0, seed, 8);
+       }
+
+       /* copy the right-most 64-bits of the given address */
+       /* XXX assumption on the size of IFID */
+       bcopy(seed1, &seed[8], 8);
+
+       if ((0)) {              /* for debugging purposes only */
+               int i;
+
+               printf("%s: new randomized ID from: ", __func__);
+               for (i = 0; i < 16; i++)
+                       printf("%02x", seed[i]);
+               printf(" ");
+       }
+
+       /* generate 16 bytes of pseudo-random value. */
+       bzero(&ctxt, sizeof (ctxt));
+       SHA1Init(&ctxt);
+       SHA1Update(&ctxt, seed, sizeof (seed));
+       SHA1Final(digest, &ctxt);
+
+       /*
+        * RFC 4941 3.2.1. (3)
+        * Take the left-most 64-bits of the SHA1 digest and set bit 6 (the
+        * left-most bit is numbered 0) to zero.
+        */
+       bcopy(digest, ret, 8);
+       ret[0] &= ~ND6_EUI64_UBIT;
+
+       /*
+        * XXX: we'd like to ensure that the generated value is not zero
+        * for simplicity.  If the caclculated digest happens to be zero,
+        * use a random non-zero value as the last resort.
+        */
+       if (bcmp(nullbuf, ret, sizeof (nullbuf)) == 0) {
+               nd6log((LOG_INFO,
+                   "%s: computed SHA1 value is zero.\n", __func__));
+
+               getmicrotime(&tv);
+               val32 = random() ^ tv.tv_usec;
+               val32 = 1 + (val32 % (0xffffffff - 1));
+       }
 
-       return 0;
+       /*
+        * RFC 4941 3.2.1. (4)
+        * Take the next 64-bits of the SHA1 digest and save them in
+        * stable storage as the history value to be used in the next
+        * iteration of the algorithm.
+        */
+       bcopy(&digest[8], seed0, 8);
+
+       if ((0)) {              /* for debugging purposes only */
+               int i;
+
+               printf("to: ");
+               for (i = 0; i < 16; i++)
+                       printf("%02x", digest[i]);
+               printf("\n");
+       }
+
+       return (0);
 }
 
 /*
- * Find first ifid on list of interfaces.
- * This is assumed that ifp0's interface token (for example, IEEE802 MAC)
- * is globally unique.  We may need to have a flag parameter in the future.
+ * Get interface identifier for the specified interface using the method in
+ * Appendix A of RFC 4291.
+ *
+ * XXX assumes single sockaddr_dl (AF_LINK address) per an interface
+ *
+ * in6 - upper 64bits are preserved
  */
 int
-in6_ifattach_getifid(ifp0)
-       struct ifnet *ifp0;
+in6_iid_from_hw(struct ifnet *ifp, struct in6_addr *in6)
 {
-       struct ifnet *ifp;
-       struct ifaddr *ifa;
-       u_int8_t *addr = NULL;
-       int addrlen = 0;
+       struct ifaddr *ifa = NULL;
        struct sockaddr_dl *sdl;
+       u_int8_t *addr;
+       size_t addrlen;
+       static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+       static u_int8_t allone[8] =
+               { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       int err = -1;
+
+       /* Why doesn't this code use ifnet_addrs? */
+       ifnet_lock_shared(ifp);
+       ifa = ifp->if_lladdr;
+       sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr;
+       if (sdl->sdl_alen == 0) {
+               ifnet_lock_done(ifp);
+               return (-1);
+       }
+       IFA_ADDREF(ifa);        /* for this routine */
+       ifnet_lock_done(ifp);
 
-       if (found_first_ifid)
-               return 0;
+       IFA_LOCK(ifa);
+       addr = (u_int8_t *) LLADDR(sdl);
+       addrlen = sdl->sdl_alen;
 
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       for (ifp = ifnet; ifp; ifp = ifp->if_next)
-#else
-       for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
-#endif
-       {
-               if (ifp0 != NULL && ifp0 != ifp)
-                       continue;
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-               for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#else
-               for (ifa = ifp->if_addrlist.tqh_first;
-                    ifa;
-                    ifa = ifa->ifa_list.tqe_next)
+       /* get EUI64 */
+       switch (ifp->if_type) {
+       case IFT_ETHER:
+       case IFT_FDDI:
+       case IFT_ISO88025:
+       case IFT_ATM:
+       case IFT_IEEE1394:
+       case IFT_L2VLAN:
+       case IFT_IEEE8023ADLAG:
+#if IFT_IEEE80211
+       case IFT_IEEE80211:
 #endif
-               {
-                       if (ifa->ifa_addr->sa_family != AF_LINK)
-                               continue;
-                       sdl = (struct sockaddr_dl *)ifa->ifa_addr;
-                       if (sdl == NULL)
-                               continue;
-                       if (sdl->sdl_alen == 0)
-                               continue;
-                       switch (ifp->if_type) {
-                       case IFT_ETHER:
-                       case IFT_FDDI:
-                       case IFT_ATM:
-                               /* IEEE802/EUI64 cases - what others? */
-                               addr = LLADDR(sdl);
-                               addrlen = sdl->sdl_alen;
-                               /*
-                                * to copy ifid from IEEE802/EUI64 interface,
-                                * u bit of the source needs to be 0.
-                                */
-                               if ((addr[0] & 0x02) != 0)
-                                       break;
-                               goto found;
-                       case IFT_ARCNET:
-                               /*
-                                * ARCnet interface token cannot be used as
-                                * globally unique identifier due to its
-                                * small bitwidth.
-                                */
-                               break;
-                       default:
-                               break;
-                       }
+       case IFT_BRIDGE:
+               /* IEEE802/EUI64 cases - what others? */
+               /* IEEE1394 uses 16byte length address starting with EUI64 */
+               if (addrlen > 8)
+                       addrlen = 8;
+
+               /* look at IEEE802/EUI64 only */
+               if (addrlen != 8 && addrlen != 6)
+                       goto done;
+
+               /*
+                * check for invalid MAC address - on bsdi, we see it a lot
+                * since wildboar configures all-zero MAC on pccard before
+                * card insertion.
+                */
+               if (bcmp(addr, allzero, addrlen) == 0)
+                       goto done;
+               if (bcmp(addr, allone, addrlen) == 0)
+                       goto done;
+
+               /* make EUI64 address */
+               if (addrlen == 8)
+                       bcopy(addr, &in6->s6_addr[8], 8);
+               else if (addrlen == 6) {
+                       in6->s6_addr[8] = addr[0];
+                       in6->s6_addr[9] = addr[1];
+                       in6->s6_addr[10] = addr[2];
+                       in6->s6_addr[11] = 0xff;
+                       in6->s6_addr[12] = 0xfe;
+                       in6->s6_addr[13] = addr[3];
+                       in6->s6_addr[14] = addr[4];
+                       in6->s6_addr[15] = addr[5];
                }
-       }
-#if DEBUG
-       printf("in6_ifattach_getifid: failed to get EUI64");
-#endif
-       return EADDRNOTAVAIL;
+               break;
 
-found:
-       if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
-               found_first_ifid = 1;
+       case IFT_ARCNET:
+               if (addrlen != 1)
+                       goto done;
+               if (!addr[0])
+                       goto done;
 
-       if (found_first_ifid) {
-               printf("%s: supplying EUI64: "
-                       "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
-                       if_name(ifp),
-                       first_ifid[0] & 0xff, first_ifid[1] & 0xff,
-                       first_ifid[2] & 0xff, first_ifid[3] & 0xff,
-                       first_ifid[4] & 0xff, first_ifid[5] & 0xff,
-                       first_ifid[6] & 0xff, first_ifid[7] & 0xff);
+               bzero(&in6->s6_addr[8], 8);
+               in6->s6_addr[15] = addr[0];
 
-               /* invert u bit to convert EUI64 to RFC2373 interface ID. */
-               first_ifid[0] ^= 0x02;
+               /*
+                * due to insufficient bitwidth, we mark it local.
+                */
+               in6->s6_addr[8] &= ~ND6_EUI64_GBIT;     /* g to "individual" */
+               in6->s6_addr[8] |= ND6_EUI64_UBIT;      /* u to "local" */
+               break;
 
-               return 0;
-       } else {
-#if DEBUG
-               printf("in6_ifattach_getifid: failed to get EUI64");
+       case IFT_GIF:
+#if IFT_STF
+       case IFT_STF:
 #endif
-               return EADDRNOTAVAIL;
+               /*
+                * RFC2893 says: "SHOULD use IPv4 address as IID source".
+                * however, IPv4 address is not very suitable as unique
+                * identifier source (can be renumbered).
+                * we don't do this.
+                */
+               goto done;
+
+       case IFT_CELLULAR:
+               goto done;
+
+       default:
+               goto done;
        }
+
+       /* sanity check: g bit must not indicate "group" */
+       if (ND6_EUI64_GROUP(in6))
+               goto done;
+
+       /* convert EUI64 into IPv6 interface identifier */
+       ND6_EUI64_TO_IFID(in6);
+
+       /*
+        * sanity check: iid must not be all zero, avoid conflict with
+        * subnet router anycast
+        */
+       if ((in6->s6_addr[8] & ~(ND6_EUI64_GBIT | ND6_EUI64_UBIT)) == 0x00 &&
+           bcmp(&in6->s6_addr[9], allzero, 7) == 0) {
+               goto done;
+       }
+
+       err = 0;        /* found */
+
+done:
+       /* This must not be the last reference to the lladdr */
+       if (IFA_REMREF_LOCKED(ifa) == NULL) {
+               panic("%s: unexpected (missing) refcnt ifa=%p", __func__, ifa);
+               /* NOTREACHED */
+       }
+       IFA_UNLOCK(ifa);
+       return (err);
 }
 
 /*
- * XXX multiple loopback interface needs more care.  for instance,
- * nodelocal address needs to be configured onto only one of them.
+ * Get interface identifier for the specified interface using the method in
+ * Appendix A of RFC 4291.  If it is not available on ifp0, borrow interface
+ * identifier from other information sources.
+ *
+ * ifp     - primary EUI64 source
+ * altifp  - secondary EUI64 source
+ * in6     - IPv6 address to output IID
  */
-void
-in6_ifattach(ifp, type, laddr, noloop)
-       struct ifnet *ifp;
-       u_int type;
-       caddr_t laddr;
-       /* size_t laddrlen; */
-       int noloop;
+static int
+in6_select_iid_from_all_hw(
+       struct ifnet *ifp0,
+       struct ifnet *altifp,   /* secondary EUI64 source */
+       struct in6_addr *in6)
 {
-       static size_t if_indexlim = 8;
-       struct sockaddr_in6 mltaddr;
-       struct sockaddr_in6 mltmask;
-       struct sockaddr_in6 gate;
-       struct sockaddr_in6 mask;
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       struct ifaddr **ifap;
-#endif
+       struct ifnet *ifp;
 
-       struct in6_ifaddr *ia, *ib, *oia;
-       struct ifaddr *ifa;
-       int rtflag = 0;
-       int s;
-       int error;
+       /* first, try to get it from the interface itself */
+       if (in6_iid_from_hw(ifp0, in6) == 0) {
+               nd6log((LOG_DEBUG, "%s: IID derived from HW interface.\n",
+                   if_name(ifp0)));
+               goto success;
+       }
 
-       if (type == IN6_IFT_P2P && found_first_ifid == 0) {
-               printf("%s: no ifid available for IPv6 link-local address\n",
-                       if_name(ifp));
-#if 0
-               return;
-#else
-               /* last resort */
-               if (gen_rand_eui64(first_ifid) == 0) {
-                       printf("%s: using random value as EUI64: "
-                               "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
-                               if_name(ifp),
-                               first_ifid[0] & 0xff, first_ifid[1] & 0xff,
-                               first_ifid[2] & 0xff, first_ifid[3] & 0xff,
-                               first_ifid[4] & 0xff, first_ifid[5] & 0xff,
-                               first_ifid[6] & 0xff, first_ifid[7] & 0xff);
-                       /*
-                        * invert u bit to convert EUI64 to RFC2373 interface
-                        * ID.
-                        */
-                       first_ifid[0] ^= 0x02;
+       /* try secondary EUI64 source. this basically is for ATM PVC */
+       if (altifp && in6_iid_from_hw(altifp, in6) == 0) {
+               nd6log((LOG_DEBUG, "%s: IID from alterate HW interface %s.\n",
+                   if_name(ifp0), if_name(altifp)));
+               goto success;
+       }
 
-                       found_first_ifid = 1;
+       /* next, try to get it from some other hardware interface */
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
+               if (ifp == ifp0)
+                       continue;
+               if (in6_iid_from_hw(ifp, in6) != 0)
+                       continue;
+
+               /*
+                * to borrow IID from other interface, IID needs to be
+                * globally unique
+                */
+               if (ND6_IFID_UNIVERSAL(in6)) {
+                       nd6log((LOG_DEBUG, "%s: borrowed IID from %s\n",
+                           if_name(ifp0), if_name(ifp)));
+                       ifnet_head_done();
+                       goto success;
                }
-#endif
        }
+       ifnet_head_done();
 
-       if ((ifp->if_flags & IFF_MULTICAST) == 0) {
-               printf("%s: not multicast capable, IPv6 not enabled\n",
-                       if_name(ifp));
-               return;
+       /* last resort: get from random number source */
+       if (get_rand_iid(ifp, in6) == 0) {
+               nd6log((LOG_DEBUG, "%s: IID from PRNG.\n", if_name(ifp0)));
+               goto success;
+       }
+
+       printf("%s: failed to get interface identifier\n", if_name(ifp0));
+       return (-1);
+
+success:
+       nd6log((LOG_INFO, "%s: IID: "
+               "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+               if_name(ifp0),
+               in6->s6_addr[8], in6->s6_addr[9],
+               in6->s6_addr[10], in6->s6_addr[11],
+               in6->s6_addr[12], in6->s6_addr[13],
+               in6->s6_addr[14], in6->s6_addr[15]));
+       return (0);
+}
+
+static int
+in6_ifattach_linklocal(struct ifnet *ifp, struct in6_aliasreq *ifra)
+{
+       struct in6_ifaddr *ia;
+       struct nd_prefix pr0, *pr;
+       int i, error;
+
+       VERIFY(ifra != NULL);
+
+       proto_plumb(PF_INET6, ifp);
+
+       error = in6_update_ifa(ifp, ifra, IN6_IFAUPDATE_DADDELAY, &ia);
+       if (error != 0) {
+               /*
+                * XXX: When the interface does not support IPv6, this call
+                * would fail in the SIOCSIFADDR ioctl.  I believe the
+                * notification is rather confusing in this case, so just
+                * suppress it.  (jinmei@kame.net 20010130)
+                */
+               if (error != EAFNOSUPPORT)
+                       nd6log((LOG_NOTICE, "%s: failed to "
+                           "configure a link-local address on %s "
+                           "(errno=%d)\n",
+                           __func__, if_name(ifp), error));
+               return (EADDRNOTAVAIL);
        }
+       VERIFY(ia != NULL);
 
        /*
-        * We have some arrays that should be indexed by if_index.
-        * since if_index will grow dynamically, they should grow too.
-        *      struct in6_ifstat **in6_ifstat
-        *      struct icmp6_ifstat **icmp6_ifstat
+        * Make the link-local prefix (fe80::%link/64) as on-link.
+        * Since we'd like to manage prefixes separately from addresses,
+        * we make an ND6 prefix structure for the link-local prefix,
+        * and add it to the prefix list as a never-expire prefix.
+        * XXX: this change might affect some existing code base...
         */
-       if (in6_ifstat == NULL || icmp6_ifstat == NULL
-        || if_index >= if_indexlim) {
-               size_t n;
-               caddr_t q;
-               size_t olim;
-
-               olim = if_indexlim;
-               while (if_index >= if_indexlim)
-                       if_indexlim <<= 1;
-
-               /* grow in6_ifstat */
-               n = if_indexlim * sizeof(struct in6_ifstat *);
-               q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
-               bzero(q, n);
-               if (in6_ifstat) {
-                       bcopy((caddr_t)in6_ifstat, q,
-                               olim * sizeof(struct in6_ifstat *));
-                       _FREE((caddr_t)in6_ifstat, M_IFADDR);
-               }
-               in6_ifstat = (struct in6_ifstat **)q;
-               in6_ifstatmax = if_indexlim;
-
-               /* grow icmp6_ifstat */
-               n = if_indexlim * sizeof(struct icmp6_ifstat *);
-               q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
-               bzero(q, n);
-               if (icmp6_ifstat) {
-                       bcopy((caddr_t)icmp6_ifstat, q,
-                               olim * sizeof(struct icmp6_ifstat *));
-                       _FREE((caddr_t)icmp6_ifstat, M_IFADDR);
-               }
-               icmp6_ifstat = (struct icmp6_ifstat **)q;
-               icmp6_ifstatmax = if_indexlim;
+       bzero(&pr0, sizeof (pr0));
+       lck_mtx_init(&pr0.ndpr_lock, ifa_mtx_grp, ifa_mtx_attr);
+       pr0.ndpr_ifp = ifp;
+       /* this should be 64 at this moment. */
+       pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL);
+       pr0.ndpr_mask = ifra->ifra_prefixmask.sin6_addr;
+       pr0.ndpr_prefix = ifra->ifra_addr;
+       /* apply the mask for safety. (nd6_prelist_add will apply it again) */
+       for (i = 0; i < 4; i++) {
+               pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &=
+                       in6mask64.s6_addr32[i];
        }
-
        /*
-        * To prevent to assign link-local address to PnP network
-        * cards multiple times.
-        * This is lengthy for P2P and LOOP but works.
+        * Initialize parameters.  The link-local prefix must always be
+        * on-link, and its lifetimes never expire.
         */
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       ifa = ifp->if_addrlist;
-       if (ifa != NULL) {
-               for ( ; ifa; ifa = ifa->ifa_next) {
-                       ifap = &ifa->ifa_next;
-                       if (ifa->ifa_addr->sa_family != AF_INET6)
-                               continue;
-                       if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
-                               return;
-               }
-       } else
-               ifap = &ifp->if_addrlist;
-#else
-       ifa = TAILQ_FIRST(&ifp->if_addrlist);
-       if (ifa != NULL) {
-               for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) {
-                       if (ifa->ifa_addr->sa_family != AF_INET6)
-                               continue;
-                       if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
-                               return;
+       pr0.ndpr_raf_onlink = 1;
+       pr0.ndpr_raf_auto = 1;  /* probably meaningless */
+       pr0.ndpr_vltime = ND6_INFINITE_LIFETIME;
+       pr0.ndpr_pltime = ND6_INFINITE_LIFETIME;
+       pr0.ndpr_stateflags |= NDPRF_STATIC;
+       /*
+        * Since there is no other link-local addresses, nd6_prefix_lookup()
+        * probably returns NULL.  However, we cannot always expect the result.
+        * For example, if we first remove the (only) existing link-local
+        * address, and then reconfigure another one, the prefix is still
+        * valid with referring to the old link-local address.
+        */
+       if ((pr = nd6_prefix_lookup(&pr0, ND6_PREFIX_EXPIRY_UNSPEC)) == NULL) {
+               if ((error = nd6_prelist_add(&pr0, NULL, &pr, TRUE)) != 0) {
+                       IFA_REMREF(&ia->ia_ifa);
+                       lck_mtx_destroy(&pr0.ndpr_lock, ifa_mtx_grp);
+                       return (error);
                }
-       } else {
-               TAILQ_INIT(&ifp->if_addrlist);
        }
-#endif
+
+       in6_post_msg(ifp, KEV_INET6_NEW_LL_ADDR, ia, NULL);
+       IFA_REMREF(&ia->ia_ifa);
+
+       /* Drop use count held above during lookup/add */
+       if (pr != NULL)
+               NDPR_REMREF(pr);
+
+       lck_mtx_destroy(&pr0.ndpr_lock, ifa_mtx_grp);
+       return (0);
+}
+
+static int
+in6_ifattach_loopback(
+       struct ifnet *ifp)      /* must be IFT_LOOP */
+{
+       struct in6_aliasreq ifra;
+       struct in6_ifaddr *ia;
+       int error;
+
+       bzero(&ifra, sizeof (ifra));
 
        /*
-        * link-local address
+        * in6_update_ifa() does not use ifra_name, but we accurately set it
+        * for safety.
         */
-       ia = (struct in6_ifaddr *)_MALLOC(sizeof(*ia), M_IFADDR, M_WAITOK);
-       bzero((caddr_t)ia, sizeof(*ia));
-       ia->ia_ifa.ifa_addr =    (struct sockaddr *)&ia->ia_addr;
-       if (ifp->if_flags & IFF_POINTOPOINT)
-               ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
-       else
-               ia->ia_ifa.ifa_dstaddr = NULL;
-       ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
-       ia->ia_ifp = ifp;
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       *ifap = (struct ifaddr *)ia;
-#else
-       TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
-#endif
-       ia->ia_ifa.ifa_refcnt++;
+       strlcpy(ifra.ifra_name, if_name(ifp), sizeof (ifra.ifra_name));
+
+       ifra.ifra_prefixmask.sin6_len = sizeof (struct sockaddr_in6);
+       ifra.ifra_prefixmask.sin6_family = AF_INET6;
+       ifra.ifra_prefixmask.sin6_addr = in6mask128;
 
        /*
-        * Also link into the IPv6 address chain beginning with in6_ifaddr.
-        * kazu opposed it, but itojun & jinmei wanted.
+        * Always initialize ia_dstaddr (= broadcast address) to loopback
+        * address.  Follows IPv4 practice - see in_ifinit().
         */
-       if ((oia = in6_ifaddr) != NULL) {
-               for (; oia->ia_next; oia = oia->ia_next)
-                       continue;
-               oia->ia_next = ia;
-       } else
-               in6_ifaddr = ia;
-       ia->ia_ifa.ifa_refcnt++;
-
-       ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
-       ia->ia_prefixmask.sin6_family = AF_INET6;
-       ia->ia_prefixmask.sin6_addr = in6mask64;
-
-       bzero(&ia->ia_addr, sizeof(struct sockaddr_in6));
-       ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
-       ia->ia_addr.sin6_family = AF_INET6;
-       ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
-       ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-       ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
-
-       switch (type) {
-       case IN6_IFT_LOOP:
-               ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
-               ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
-                if (strcmp(ifp->if_name, "lo") == 0) {
-                        ia->ia_ifa.ifa_dlt = lo_attach_inet(ifp);
-                       printf("in6_ifattach: IFT_LOOP setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
-                        ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
-               }
-               break;
-       case IN6_IFT_802:
-               ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               ia->ia_ifa.ifa_flags |= RTF_CLONING;
-               rtflag = RTF_CLONING;
-                 if (strcmp(ifp->if_name, "en") == 0) {
-                       ia->ia_ifa.ifa_dlt = ether_attach_inet6(ifp);
-                       printf("in6_ifattach: IFT_802 setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
-                                ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
-                }
-
-               /* fall through */
-       case IN6_IFT_P2P802:
-               ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               ia->ia_ifa.ifa_flags |= RTF_CLONING;
-               rtflag = RTF_CLONING;
-               if (laddr == NULL)
-                       break;
-               /* XXX use laddrlen */
-               if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
-                               laddr, 6) != 0) {
-                       break;
-               }
-               /* invert u bit to convert EUI64 to RFC2373 interface ID. */
-               ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
-               if (found_first_ifid == 0)
-                       in6_ifattach_getifid(ifp);
-               bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
-               ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
-               ia->ia_dstaddr.sin6_family = AF_INET6;
-
-               if (ia->ia_ifa.ifa_dlt == 0) {
-                       ia->ia_ifa.ifa_dlt = ifp;
-#if DEBUG
-                       printf("in6_ifattach: IFT_P2P802 setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
-                                ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
-#endif
+       ifra.ifra_dstaddr.sin6_len = sizeof (struct sockaddr_in6);
+       ifra.ifra_dstaddr.sin6_family = AF_INET6;
+       ifra.ifra_dstaddr.sin6_addr = in6addr_loopback;
+
+       ifra.ifra_addr.sin6_len = sizeof (struct sockaddr_in6);
+       ifra.ifra_addr.sin6_family = AF_INET6;
+       ifra.ifra_addr.sin6_addr = in6addr_loopback;
+
+       /* the loopback  address should NEVER expire. */
+       ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+       ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+       /* we don't need to perform DAD on loopback interfaces. */
+       ifra.ifra_flags |= IN6_IFF_NODAD;
+
+       /* add the new interface address */
+       error = in6_update_ifa(ifp, &ifra, 0, &ia);
+       if (error != 0) {
+               nd6log((LOG_ERR,
+                   "%s: failed to configure loopback address %s (error=%d)\n",
+                   __func__, if_name(ifp), error));
+               VERIFY(ia == NULL);
+               return (EADDRNOTAVAIL);
+       }
+
+       VERIFY(ia != NULL);
+       IFA_REMREF(&ia->ia_ifa);
+       return (0);
+}
+
+/*
+ * compute NI group address, based on the current hostname setting.
+ * see RFC 4620.
+ *
+ * when ifp == NULL, the caller is responsible for filling scopeid.
+ */
+int
+in6_nigroup(
+       struct ifnet *ifp,
+       const char *name,
+       int namelen,
+       struct in6_addr *in6)
+{
+       const char *p;
+       u_char *q;
+       SHA1_CTX ctxt;
+       u_int8_t digest[SHA1_RESULTLEN];
+       char l;
+       char n[64];     /* a single label must not exceed 63 chars */
+
+       if (!namelen || !name)
+               return (-1);
+
+       p = name;
+       while (p && *p && *p != '.' && p - name < namelen)
+               p++;
+       if (p - name > sizeof (n) - 1)
+               return (-1);    /* label too long */
+       l = p - name;
+       strlcpy(n, name, l);
+       n[(int)l] = '\0';
+       for (q = (u_char *) n; *q; q++) {
+               if ('A' <= *q && *q <= 'Z')
+                       *q = *q - 'A' + 'a';
+       }
+
+       /* generate 16 bytes of pseudo-random value. */
+       bzero(&ctxt, sizeof (ctxt));
+       SHA1Init(&ctxt);
+       SHA1Update(&ctxt, &l, sizeof (l));
+       SHA1Update(&ctxt, n, l);
+       SHA1Final(digest, &ctxt);
+
+       bzero(in6, sizeof (*in6));
+       in6->s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+       in6->s6_addr8[11] = 2;
+       in6->s6_addr8[12] = 0xff;
+       /* copy first 3 bytes of prefix into address */
+       bcopy(digest, &in6->s6_addr8[13], 3);
+       if (in6_setscope(in6, ifp, NULL))
+               return (-1); /* XXX: should not fail */
+
+       return (0);
+}
+
+int
+in6_domifattach(struct ifnet *ifp)
+{
+       int error;
+
+       VERIFY(ifp != NULL);
+
+       error = proto_plumb(PF_INET6, ifp);
+       if (error != 0) {
+               if (error != EEXIST)
+                       log(LOG_ERR, "%s: proto_plumb returned %d if=%s\n",
+                           __func__, error, if_name(ifp));
+       } else {
+               error = in6_ifattach_prelim(ifp);
+               if (error != 0) {
+                       int errorx;
+
+                       log(LOG_ERR,
+                           "%s: in6_ifattach_prelim returned %d if=%s%d\n",
+                           __func__, error, ifp->if_name, ifp->if_unit);
+
+                       errorx = proto_unplumb(PF_INET6, ifp);
+                       if (errorx != 0) /* XXX should not fail */
+                               log(LOG_ERR,
+                                   "%s: proto_unplumb returned %d if=%s%d\n",
+                                   __func__, errorx, ifp->if_name,
+                                   ifp->if_unit);
                }
-               break;
-       case IN6_IFT_P2P:
-               ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               ia->ia_ifa.ifa_flags |= RTF_CLONING;
-               rtflag = RTF_CLONING;
-               bcopy((caddr_t)first_ifid,
-                     (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8],
-                     IFID_LEN);
-               bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
-               ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
-               ia->ia_dstaddr.sin6_family = AF_INET6;
-#if NGIF > 0
-                 if (strcmp(ifp->if_name, "gif") == 0) {
-                       ia->ia_ifa.ifa_dlt = gif_attach_inet(ifp);
-#if DEBUG
-                       printf("in6_ifattach: IFT_P2P setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
-                                ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
-#endif
-                }
-#endif
-               break;
-       case IN6_IFT_ARCNET:
-               ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               ia->ia_ifa.ifa_flags |= RTF_CLONING;
-               rtflag = RTF_CLONING;
-               if (laddr == NULL)
-                       break;
-
-               /* make non-global IF id out of link-level address */
-               bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7);
-               ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr;
-                ia->ia_ifa.ifa_dlt = ifp;
-#if DEBUG
-               printf("in6_ifattach: IFT_ARCNET setting initial ifp=%s%d initial ia=%x ifa_dlt=%x\n",
-                        ifp->if_name, ifp->if_unit, ia, ia->ia_ifa.ifa_dlt);
-#endif
        }
 
-       ia->ia_ifa.ifa_metric = ifp->if_metric;
+       return (error);
+}
 
+int
+in6_ifattach_prelim(struct ifnet *ifp)
+{
+       struct in6_ifextra *ext;
+       void **pbuf, *base;
+       int error = 0;
+       struct in6_ifaddr *ia6 = NULL;
+
+       VERIFY(ifp != NULL);
+
+       /* quirks based on interface type */
+       switch (ifp->if_type) {
+#if IFT_STF
+       case IFT_STF:
+               /*
+                * 6to4 interface is a very special kind of beast.
+                * no multicast, no linklocal.  RFC2529 specifies how to make
+                * linklocals for 6to4 interface, but there's no use and
+                * it is rather harmful to have one.
+                */
+               goto skipmcast;
+#endif
+       default:
+               break;
+       }
 
        /*
-        * give the interface a chance to initialize, in case this
-        * is the first address to be added.
+        * IPv6 requires multicast capability at the interface.
+        *   (previously, this was a silent error.)
         */
-       s = splimp();
-#ifdef __APPLE__
-       error = dlil_ioctl(0, ifp, SIOCSIFADDR, (caddr_t)ia);
-#else
-       error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
-#endif
-       splx(s);
-#if DEBUG
-       printf("in6_ifattach: Calling SIOCSIFADDR for if=%s%d ia=%x error=%x\n", ifp->if_name, ifp->if_unit, ia, error);
-#endif
-       if (error == EOPNOTSUPP)
-               error = 0;
-
-       if (error) {
-               switch (error) {
-               case EAFNOSUPPORT:
-                       printf("%s: IPv6 not supported\n",
-                               if_name(ifp));
-                       break;
-               default:
-                       printf("%s: SIOCSIFADDR error %d\n",
-                               if_name(ifp), error);
-                       break;
-               }
+       if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+               nd6log((LOG_INFO, "in6_ifattach: ",
+                   "%s is not multicast capable, IPv6 not enabled\n",
+                   if_name(ifp)));
+               return (EINVAL);
+       }
 
-               /* undo changes */
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-               *ifap = NULL;
-#else
-               TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+#if IFT_STF
+skipmcast:
 #endif
-               IFAFREE(&ia->ia_ifa);
-               if (oia)
-                       oia->ia_next = ia->ia_next;
-               else
-                       in6_ifaddr = ia->ia_next;
-               IFAFREE(&ia->ia_ifa);
-               return;
-       }
-
-       /* add route to the interface. */
-       rtrequest(RTM_ADD,
-                 (struct sockaddr *)&ia->ia_addr,
-                 (struct sockaddr *)&ia->ia_addr,
-                 (struct sockaddr *)&ia->ia_prefixmask,
-                 RTF_UP|rtflag,
-                 (struct rtentry **)0);
-       ia->ia_flags |= IFA_ROUTE;
-
-       if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
+
+       if (ifp->if_inet6data == NULL) {
+               ext = (struct in6_ifextra *)_MALLOC(in6_extra_bufsize, M_IFADDR,
+                   M_WAITOK|M_ZERO);
+               if (!ext)
+                       return (ENOMEM);
+               base = (void *)P2ROUNDUP((intptr_t)ext + sizeof(uint64_t),
+                   sizeof(uint64_t));
+               VERIFY(((intptr_t)base + in6_extra_size) <=
+                   ((intptr_t)ext + in6_extra_bufsize));
+               pbuf = (void **)((intptr_t)base - sizeof(void *));
+               *pbuf = ext;
+               ifp->if_inet6data = base;
+               VERIFY(IS_P2ALIGNED(ifp->if_inet6data, sizeof(uint64_t)));
+       } else {
+               /*
+                * Since the structure is never freed, we need to zero out
+                * some of its members. We avoid zeroing out the scope6
+                * structure on purpose because other threads might be
+                * using its contents.
+                */
+               bzero(&IN6_IFEXTRA(ifp)->icmp6_ifstat,
+                   sizeof(IN6_IFEXTRA(ifp)->icmp6_ifstat));
+               bzero(&IN6_IFEXTRA(ifp)->in6_ifstat,
+                   sizeof(IN6_IFEXTRA(ifp)->in6_ifstat));
                /*
-                * route local address to loopback
+                * XXX When recycling, nd_ifinfo gets initialized, other
+                * than the lock, inside nd6_ifattach
                 */
-               bzero(&gate, sizeof(gate));
-               gate.sin6_len = sizeof(struct sockaddr_in6);
-               gate.sin6_family = AF_INET6;
-               gate.sin6_addr = in6addr_loopback;
-               bzero(&mask, sizeof(mask));
-               mask.sin6_len = sizeof(struct sockaddr_in6);
-               mask.sin6_family = AF_INET6;
-               mask.sin6_addr = in6mask64;
-               rtrequest(RTM_ADD,
-                         (struct sockaddr *)&ia->ia_addr,
-                         (struct sockaddr *)&gate,
-                         (struct sockaddr *)&mask,
-                         RTF_UP|RTF_HOST,
-                         (struct rtentry **)0);
        }
 
        /*
-        * loopback address
+        * XXX Only initialize NDP ifinfo for the interface
+        * if interface has not yet been configured with
+        * link local IPv6 address.
+        * Could possibly be optimized with an interface flag if need
+        * be. For now using in6ifa_ifpforlinklocal.
         */
-       ib = (struct in6_ifaddr *)NULL;
-       if (type == IN6_IFT_LOOP) {
-               ib = (struct in6_ifaddr *)
-                       _MALLOC(sizeof(*ib), M_IFADDR, M_WAITOK);
-               bzero((caddr_t)ib, sizeof(*ib));
-               ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
-               ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
-               ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
-               ib->ia_ifa.ifa_dlt = lo_attach_inet(ifp);
-               ib->ia_ifp = ifp;
-
-               ia->ia_next = ib;
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-               ia->ia_ifa.ifa_next = (struct ifaddr *)ib;
-#else
-               TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
-                       ifa_list);
-#endif
-               ib->ia_ifa.ifa_refcnt++;
+       ia6 = in6ifa_ifpforlinklocal(ifp, 0);
+       if (ia6 == NULL) {
+               /* initialize NDP variables */
+               nd6_ifattach(ifp);
+       } else {
+               VERIFY(ND_IFINFO(ifp)->initialized);
+               IFA_REMREF(&ia6->ia_ifa);
+               ia6 = NULL;
+       }
+       scope6_ifattach(ifp);
+
+       /* initialize loopback interface address */
+       if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+               error = in6_ifattach_loopback(ifp);
+               if (error != 0) {
+                       log(LOG_ERR, "%s: in6_ifattach_loopback returned %d\n",
+                           __func__, error, ifp->if_name,
+                           ifp->if_unit);
+                       return (error);
+               }
+       }
+
+       /* update dynamically. */
+       if (in6_maxmtu < ifp->if_mtu)
+               in6_maxmtu = ifp->if_mtu;
 
-               ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
-               ib->ia_prefixmask.sin6_family = AF_INET6;
-               ib->ia_prefixmask.sin6_addr = in6mask128;
-               ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
-               ib->ia_addr.sin6_family = AF_INET6;
-               ib->ia_addr.sin6_addr = in6addr_loopback;
+       VERIFY(error == 0);
+       return (0);
+}
 
-               /*
-                * Always initialize ia_dstaddr (= broadcast address)
-                * to loopback address, to make getifaddr happier.
-                *
-                * For BSDI, it is mandatory.  The BSDI version of
-                * ifa_ifwithroute() rejects to add a route to the loopback
-                * interface.  Even for other systems, loopback looks somewhat
-                * special.
-                */
-               ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
-               ib->ia_dstaddr.sin6_family = AF_INET6;
-               ib->ia_dstaddr.sin6_addr = in6addr_loopback;
+int
+in6_ifattach_aliasreq(struct ifnet *ifp, struct ifnet *altifp,
+    struct in6_aliasreq *ifra0)
+{
+       int error;
+       struct in6_ifaddr *ia6;
+       struct in6_aliasreq ifra;
 
-               ib->ia_ifa.ifa_metric = ifp->if_metric;
+       error = in6_ifattach_prelim(ifp);
+       if (error != 0)
+               return (error);
 
-               rtrequest(RTM_ADD,
-                         (struct sockaddr *)&ib->ia_addr,
-                         (struct sockaddr *)&ib->ia_addr,
-                         (struct sockaddr *)&ib->ia_prefixmask,
-                         RTF_UP|RTF_HOST,
-                         (struct rtentry **)0);
+       if (!ip6_auto_linklocal)
+               return (0);
 
-               ib->ia_flags |= IFA_ROUTE;
+       /* assign a link-local address, only if there isn't one here already. */
+       ia6 = in6ifa_ifpforlinklocal(ifp, 0);
+       if (ia6 != NULL) {
+               IFA_REMREF(&ia6->ia_ifa);
+               return (0);
        }
 
+       bzero(&ifra, sizeof (ifra));
+
        /*
-        * join multicast
+        * in6_update_ifa() does not use ifra_name, but we accurately set it
+        * for safety.
         */
-       if (ifp->if_flags & IFF_MULTICAST) {
-               int error;      /* not used */
+       strlcpy(ifra.ifra_name, if_name(ifp), sizeof (ifra.ifra_name));
+
+       /* Initialize the IPv6 interface address in our in6_aliasreq block */
+       if ((ifp->if_eflags & IFEF_NOAUTOIPV6LL) != 0 && ifra0 != NULL) {
+               /* interface provided both addresses for us */
+               struct sockaddr_in6 *sin6 = &ifra.ifra_addr;
+               struct in6_addr *in6 = &sin6->sin6_addr;
+               boolean_t ok = TRUE;
+
+               bcopy(&ifra0->ifra_addr, sin6, sizeof (struct sockaddr_in6));
+
+               if (sin6->sin6_family != AF_INET6 || sin6->sin6_port != 0)
+                       ok = FALSE;
+               if (ok && (in6->s6_addr16[0] != htons(0xfe80)))
+                       ok = FALSE;
+               if (ok) {
+                       if (sin6->sin6_scope_id == 0 && in6->s6_addr16[1] == 0)
+                               in6->s6_addr16[1] = htons(ifp->if_index);
+                       else if (sin6->sin6_scope_id != 0 &&
+                           sin6->sin6_scope_id != ifp->if_index)
+                               ok = FALSE;
+                       else if (in6->s6_addr16[1] != 0 &&
+                           ntohs(in6->s6_addr16[1]) != ifp->if_index)
+                               ok = FALSE;
+               }
+               if (ok && (in6->s6_addr32[1] != 0))
+                       ok = FALSE;
+               if (!ok)
+                       return (EINVAL);
+       } else {
+               ifra.ifra_addr.sin6_family = AF_INET6;
+               ifra.ifra_addr.sin6_len = sizeof (struct sockaddr_in6);
+               ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+               ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+               ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
+               if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+                       ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
+                       ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1);
+               } else {
+                       if (in6_select_iid_from_all_hw(ifp, altifp,
+                           &ifra.ifra_addr.sin6_addr) != 0) {
+                               nd6log((LOG_ERR, "%s: no IID available\n",
+                                   if_name(ifp)));
+                               return (EADDRNOTAVAIL);
+                       }
+               }
+       }
 
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__)
-               /* Restore saved multicast addresses(if any). */
-               in6_restoremkludge(ia, ifp);
-#endif
+       if (in6_setscope(&ifra.ifra_addr.sin6_addr, ifp, NULL))
+               return (EADDRNOTAVAIL);
 
-               bzero(&mltmask, sizeof(mltmask));
-               mltmask.sin6_len = sizeof(struct sockaddr_in6);
-               mltmask.sin6_family = AF_INET6;
-               mltmask.sin6_addr = in6mask32;
+       /* Set the prefix mask */
+       ifra.ifra_prefixmask.sin6_len = sizeof (struct sockaddr_in6);
+       ifra.ifra_prefixmask.sin6_family = AF_INET6;
+       ifra.ifra_prefixmask.sin6_addr = in6mask64;
 
-               /*
-                * join link-local all-nodes address
-                */
-               bzero(&mltaddr, sizeof(mltaddr));
-               mltaddr.sin6_len = sizeof(struct sockaddr_in6);
-               mltaddr.sin6_family = AF_INET6;
-               mltaddr.sin6_addr = in6addr_linklocal_allnodes;
-               mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-               rtrequest(RTM_ADD,
-                         (struct sockaddr *)&mltaddr,
-                         (struct sockaddr *)&ia->ia_addr,
-                         (struct sockaddr *)&mltmask,
-                         RTF_UP|RTF_CLONING,  /* xxx */
-                         (struct rtentry **)0);
-               (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
-
-               if (type == IN6_IFT_LOOP) {
-                       /*
-                        * join node-local all-nodes address
-                        */
-                       mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
-                       rtrequest(RTM_ADD,
-                                 (struct sockaddr *)&mltaddr,
-                                 (struct sockaddr *)&ib->ia_addr,
-                                 (struct sockaddr *)&mltmask,
-                                 RTF_UP,
-                                 (struct rtentry **)0);
-                       (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
-               } else {
-                       /*
-                        * join solicited multicast address
-                        */
-                       bzero(&llsol, sizeof(llsol));
-                       llsol.s6_addr16[0] = htons(0xff02);
-                       llsol.s6_addr16[1] = htons(ifp->if_index);
-                       llsol.s6_addr32[1] = 0;
-                       llsol.s6_addr32[2] = htonl(1);
-                       llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
-                       llsol.s6_addr8[12] = 0xff;
-                       (void)in6_addmulti(&llsol, ifp, &error);
-               }
+       /* link-local addresses should NEVER expire. */
+       ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+       ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+       /* Attach the link-local address */
+       if (in6_ifattach_linklocal(ifp, &ifra) != 0) {
+               nd6log((LOG_INFO,
+                   "%s: %s could not attach link-local address.\n",
+                   __func__, if_name(ifp)));
+               /* NB: not an error */
        }
 
-       /* update dynamically. */
-       if (in6_maxmtu < ifp->if_mtu)
-               in6_maxmtu = ifp->if_mtu;
+       return (0);
+}
+
+int
+in6_ifattach_llstartreq(struct ifnet *ifp, struct in6_llstartreq *llsr)
+{
+       struct in6_aliasreq ifra;
+       struct in6_ifaddr *ia6 = NULL;
+       struct nd_ifinfo *ndi = NULL;
+       int error;
+
+       VERIFY(llsr != NULL);
+
+       error = in6_ifattach_prelim(ifp);
+       if (error != 0)
+               return (error);
+
+       if (!ip6_auto_linklocal || (ifp->if_eflags & IFEF_NOAUTOIPV6LL) != 0)
+               return (0);
+
+       if (nd6_send_opstate == ND6_SEND_OPMODE_DISABLED)
+               return (ENXIO);
 
-       if (in6_ifstat[ifp->if_index] == NULL) {
-               in6_ifstat[ifp->if_index] = (struct in6_ifstat *)
-                       _MALLOC(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK);
-               bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat));
+       ndi = ND_IFINFO(ifp);
+       VERIFY(ndi != NULL && ndi->initialized);
+       if ((ndi->flags & ND6_IFF_INSECURE) != 0) {
+               return (ENXIO);
        }
-       if (icmp6_ifstat[ifp->if_index] == NULL) {
-               icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *)
-                       _MALLOC(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK);
-               bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat));
+
+       /* assign a link-local address, only if there isn't one here already. */
+       ia6 = in6ifa_ifpforlinklocal(ifp, 0);
+       if (ia6 != NULL) {
+               IFA_REMREF(&ia6->ia_ifa);
+               return (0);
        }
 
-       /* initialize NDP variables */
-       nd6_ifattach(ifp);
+       bzero(&ifra, sizeof (ifra));
+       strlcpy(ifra.ifra_name, if_name(ifp), sizeof (ifra.ifra_name));
+
+       ifra.ifra_addr.sin6_family = AF_INET6;
+       ifra.ifra_addr.sin6_len = sizeof (struct sockaddr_in6);
+       ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+       ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+       ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
+       ifra.ifra_flags = IN6_IFF_SECURED;
+
+       in6_cga_node_lock();
+       if (in6_cga_generate(&llsr->llsr_cgaprep, 0,
+           &ifra.ifra_addr.sin6_addr)) {
+               in6_cga_node_unlock();
+               return (EADDRNOTAVAIL);
+       }
+       in6_cga_node_unlock();
 
-       /* mark the address TENTATIVE, if needed. */
-       switch (ifp->if_type) {
-       case IFT_ARCNET:
-       case IFT_ETHER:
-       case IFT_FDDI:
-#if 0
-       case IFT_ATM:
-       case IFT_SLIP:
-       case IFT_PPP:
-#endif
-               ia->ia6_flags |= IN6_IFF_TENTATIVE;
-               /* nd6_dad_start() will be called in in6_if_up */
-               break;
-       case IFT_DUMMY:
-       case IFT_GIF:   /*XXX*/
-       case IFT_LOOP:
-       case IFT_FAITH:
-       default:
-               break;
+       if (in6_setscope(&ifra.ifra_addr.sin6_addr, ifp, NULL))
+               return (EADDRNOTAVAIL);
+
+       /* Set the prefix mask */
+       ifra.ifra_prefixmask.sin6_len = sizeof (struct sockaddr_in6);
+       ifra.ifra_prefixmask.sin6_family = AF_INET6;
+       ifra.ifra_prefixmask.sin6_addr = in6mask64;
+
+       /*
+        * link-local addresses should NEVER expire, but cryptographic
+        * ones may have finite preferred lifetime [if it's important to
+        * keep them from being used by applications as persistent device
+        * identifiers].
+        */
+       ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+       ifra.ifra_lifetime.ia6t_pltime = llsr->llsr_lifetime.ia6t_pltime;
+
+       /* Attach the link-local address */
+       if (in6_ifattach_linklocal(ifp, &ifra) != 0) {
+               /* NB: not an error */
+               nd6log((LOG_INFO,
+                   "%s: %s could not attach link-local address.\n",
+                   __func__, if_name(ifp)));
        }
 
-       return;
+       VERIFY(error == 0);
+       return (error);
 }
 
 /*
  * NOTE: in6_ifdetach() does not support loopback if at this moment.
  */
 void
-in6_ifdetach(ifp)
-       struct ifnet *ifp;
+in6_ifdetach(struct ifnet *ifp)
 {
        struct in6_ifaddr *ia, *oia;
        struct ifaddr *ifa;
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       struct ifaddr *ifaprev = NULL;
-#endif
        struct rtentry *rt;
-       short rtflags;
        struct sockaddr_in6 sin6;
-       struct in6_multi *in6m;
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
-       struct in6_multi *in6m_next;
-#endif
+       struct in6_multi_mship *imm;
+       int unlinked;
 
-       /* nuke prefix list.  this may try to remove some of ifaddrs as well */
-       in6_purgeprefix(ifp);
+       lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED);
 
        /* remove neighbor management table */
        nd6_purge(ifp);
 
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-       for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#else
-       for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#endif
-       {
-               if (ifa->ifa_addr->sa_family != AF_INET6
-                || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-                       ifaprev = ifa;
-#endif
+       /* nuke any of IPv6 addresses we have */
+       lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+       ia = in6_ifaddrs;
+       while (ia != NULL) {
+               if (ia->ia_ifa.ifa_ifp != ifp) {
+                       ia = ia->ia_next;
+                       continue;
+               }
+               IFA_ADDREF(&ia->ia_ifa);        /* for us */
+               lck_rw_done(&in6_ifaddr_rwlock);
+               in6_purgeaddr(&ia->ia_ifa);
+               IFA_REMREF(&ia->ia_ifa);        /* for us */
+               lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+               /*
+                * Purging the address caused in6_ifaddr_rwlock
+                * to be dropped and reacquired;
+                * therefore search again from the beginning
+                * of in6_ifaddrs list.
+                */
+               ia = in6_ifaddrs;
+       }
+       lck_rw_done(&in6_ifaddr_rwlock);
+
+       ifnet_lock_exclusive(ifp);
+
+       /* undo everything done by in6_ifattach(), just in case */
+       ifa = TAILQ_FIRST(&ifp->if_addrlist);
+       while (ifa != NULL) {
+               IFA_LOCK(ifa);
+               if (ifa->ifa_addr->sa_family != AF_INET6 ||
+                   !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->
+                   sin6_addr)) {
+                       IFA_UNLOCK(ifa);
+                       ifa = TAILQ_NEXT(ifa, ifa_list);
                        continue;
                }
 
                ia = (struct in6_ifaddr *)ifa;
 
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__)
-               /* leave from all multicast groups joined */
-               while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL)
-                       in6_delmulti(in6m);
-#endif
+               /* hold a reference for this routine */
+               IFA_ADDREF_LOCKED(ifa);
+               /* remove from the linked list */
+               if_detach_ifa(ifp, ifa);
+               IFA_UNLOCK(ifa);
 
-               /* remove from the routing table */
-               if ((ia->ia_flags & IFA_ROUTE)
-                && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0
-#if defined (__FreeBSD__) || defined (__APPLE__)
-                               , 0UL
-#endif
-                               ))) {
-                       rtflags = rt->rt_flags;
-                       rtfree(rt);
-                       rtrequest(RTM_DELETE,
-                               (struct sockaddr *)&ia->ia_addr,
-                               (struct sockaddr *)&ia->ia_addr,
-                               (struct sockaddr *)&ia->ia_prefixmask,
-                               rtflags, (struct rtentry **)0);
+               /*
+                * Leaving the multicast group(s) may involve freeing the
+                * link address multicast structure(s) for the interface,
+                * which is protected by ifnet lock.  To avoid violating
+                * lock ordering, we must drop ifnet lock before doing so.
+                * The ifa won't go away since we held a refcnt above.
+                */
+               ifnet_lock_done(ifp);
+
+               /*
+                * We have to do this work manually here instead of calling
+                * in6_purgeaddr() since in6_purgeaddr() uses the RTM_HOST flag.
+                */
+
+               /*
+                * leave from multicast groups we have joined for the interface
+                */
+               IFA_LOCK(ifa);
+               while ((imm = ia->ia6_memberships.lh_first) != NULL) {
+                       LIST_REMOVE(imm, i6mm_chain);
+                       IFA_UNLOCK(ifa);
+                       in6_leavegroup(imm);
+                       IFA_LOCK(ifa);
                }
 
-               /* remove from the linked list */
-#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
-               if (ifaprev)
-                       ifaprev->ifa_next = ifa->ifa_next;
-               else
-                       ifp->if_addrlist = ifa->ifa_next;
-#else
-               TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
-#endif
+               /* remove from the routing table */
+               if (ia->ia_flags & IFA_ROUTE) {
+                       IFA_UNLOCK(ifa);
+                       rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0);
+                       if (rt != NULL) {
+                               (void) rtrequest(RTM_DELETE,
+                                       (struct sockaddr *)&ia->ia_addr,
+                                       (struct sockaddr *)&ia->ia_addr,
+                                       (struct sockaddr *)&ia->ia_prefixmask,
+                                       rt->rt_flags, (struct rtentry **)0);
+                               rtfree(rt);
+                       }
+               } else {
+                       IFA_UNLOCK(ifa);
+               }
 
                /* also remove from the IPv6 address chain(itojun&jinmei) */
+               unlinked = 1;
                oia = ia;
-               if (oia == (ia = in6_ifaddr))
-                       in6_ifaddr = ia->ia_next;
-               else {
+               lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+               if (oia == (ia = in6_ifaddrs)) {
+                       in6_ifaddrs = ia->ia_next;
+               } else {
                        while (ia->ia_next && (ia->ia_next != oia))
                                ia = ia->ia_next;
-                       if (ia->ia_next)
+                       if (ia->ia_next) {
                                ia->ia_next = oia->ia_next;
-#if DEBUG
-                       else
-                               printf("%s: didn't unlink in6ifaddr from "
-                                   "list\n", if_name(ifp));
-#endif
+                       } else {
+                               nd6log((LOG_ERR,
+                                   "%s: didn't unlink in6ifaddr from "
+                                   "list\n", if_name(ifp)));
+                               unlinked = 0;
+                       }
                }
+               lck_rw_done(&in6_ifaddr_rwlock);
 
-               _FREE(ia, M_IFADDR);
-       }
+               ifa = &oia->ia_ifa;
+               /*
+                * release another refcnt for the link from in6_ifaddrs.
+                * Do this only if it's not already unlinked in the event
+                * that we lost the race, since in6_ifaddr_rwlock was
+                * momentarily dropped above.
+                */
+               if (unlinked)
+                       IFA_REMREF(ifa);
+               /* release reference held for this routine */
+               IFA_REMREF(ifa);
 
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
-       /* leave from all multicast groups joined */
-       for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) {
-               in6m_next = LIST_NEXT(in6m, in6m_entry);
-               if (in6m->in6m_ifp != ifp)
-                       continue;
-               in6_delmulti(in6m);
-               in6m = NULL;
+               /*
+                * This is suboptimal, but since we dropped ifnet lock above
+                * the list might have changed.  Repeat the search from the
+                * beginning until we find the first eligible IPv6 address.
+                */
+               ifnet_lock_exclusive(ifp);
+               ifa = TAILQ_FIRST(&ifp->if_addrlist);
        }
-#endif
+       ifnet_lock_done(ifp);
 
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
-       /* cleanup multicast address kludge table, if there is any */
-       in6_purgemkludge(ifp);
-#endif
+       /* invalidate route caches */
+       routegenid_inet6_update();
 
-       /* remove neighbor management table */
+       /*
+        * remove neighbor management table.  we call it twice just to make
+        * sure we nuke everything.  maybe we need just one call.
+        * XXX: since the first call did not release addresses, some prefixes
+        * might remain.  We should call nd6_purge() again to release the
+        * prefixes after removing all addresses above.
+        * (Or can we just delay calling nd6_purge until at this point?)
+        */
        nd6_purge(ifp);
 
        /* remove route to link-local allnodes multicast (ff02::1) */
-       bzero(&sin6, sizeof(sin6));
-       sin6.sin6_len = sizeof(struct sockaddr_in6);
+       bzero(&sin6, sizeof (sin6));
+       sin6.sin6_len = sizeof (struct sockaddr_in6);
        sin6.sin6_family = AF_INET6;
        sin6.sin6_addr = in6addr_linklocal_allnodes;
        sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-#if !defined(__FreeBSD__) && !defined (__APPLE__)
-       if ((rt = rtalloc1((struct sockaddr *)&sin6, 0)) != NULL)
-#else
-       if ((rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL)) != NULL)
-#endif
-       {
-               rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt),
-                       rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
+       rt = rtalloc1((struct sockaddr *)&sin6, 0, 0);
+       if (rt != NULL) {
+               RT_LOCK(rt);
+               if (rt->rt_ifp == ifp) {
+                       /*
+                        * Prevent another thread from modifying rt_key,
+                        * rt_gateway via rt_setgate() after the rt_lock
+                        * is dropped by marking the route as defunct.
+                        */
+                       rt->rt_flags |= RTF_CONDEMNED;
+                       RT_UNLOCK(rt);
+                       (void) rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
+                           rt_mask(rt), rt->rt_flags, 0);
+               } else {
+                       RT_UNLOCK(rt);
+               }
                rtfree(rt);
        }
 }
+
+void
+in6_iid_mktmp(struct ifnet *ifp, u_int8_t *retbuf, const u_int8_t *baseid,
+    int generate)
+{
+       u_int8_t nullbuf[8];
+       struct nd_ifinfo *ndi = ND_IFINFO(ifp);
+
+       VERIFY(ndi != NULL && ndi->initialized);
+       lck_mtx_lock(&ndi->lock);
+       bzero(nullbuf, sizeof (nullbuf));
+       if (bcmp(ndi->randomid, nullbuf, sizeof (nullbuf)) == 0) {
+               /* we've never created a random ID.  Create a new one. */
+               generate = 1;
+       }
+
+       if (generate) {
+               bcopy(baseid, ndi->randomseed1, sizeof (ndi->randomseed1));
+
+               /* in6_generate_tmp_iid will update seedn and buf */
+               (void) in6_generate_tmp_iid(ndi->randomseed0, ndi->randomseed1,
+                   ndi->randomid);
+       }
+
+       bcopy(ndi->randomid, retbuf, 8);
+       lck_mtx_unlock(&ndi->lock);
+}
+
+void
+in6_tmpaddrtimer(void *arg)
+{
+#pragma unused(arg)
+       struct ifnet *ifp = NULL;
+       struct nd_ifinfo *ndi = NULL;
+       u_int8_t nullbuf[8];
+
+       timeout(in6_tmpaddrtimer, (caddr_t)0, (ip6_temp_preferred_lifetime -
+           ip6_desync_factor - ip6_temp_regen_advance) * hz);
+
+       bzero(nullbuf, sizeof (nullbuf));
+       ifnet_head_lock_shared();
+       for (ifp = ifnet_head.tqh_first; ifp;
+           ifp = ifp->if_link.tqe_next) {
+               ndi = ND_IFINFO(ifp);
+               if ((NULL == ndi) || (FALSE == ndi->initialized)) {
+                       continue;
+               }
+               lck_mtx_lock(&ndi->lock);
+               if (bcmp(ndi->randomid, nullbuf, sizeof (nullbuf)) != 0) {
+                       /*
+                        * We've been generating a random ID on this interface.
+                        * Create a new one.
+                        */
+                       (void) in6_generate_tmp_iid(ndi->randomseed0,
+                           ndi->randomseed1, ndi->randomid);
+               }
+               lck_mtx_unlock(&ndi->lock);
+       }
+       ifnet_head_done();
+}