]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/in6_ifattach.c
xnu-1504.9.37.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_ifattach.c
index 63c5d571061dcad4cc0ca230bf0b3fbb6a83045e..5995b212d0952bab0b0f5b32c956b6299bb77e17 100644 (file)
@@ -1,4 +1,32 @@
-/*     $KAME: in6_ifattach.c,v 1.41 2000/03/16 07:05:34 jinmei Exp $   */
+/*
+ * Copyright (c) 2003-2008 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@
+ */
+/*     $FreeBSD: src/sys/netinet6/in6_ifattach.c,v 1.8 2002/04/19 04:46:22 suz Exp $   */
+/*     $KAME: in6_ifattach.c,v 1.118 2001/05/24 07:44:00 itojun Exp $  */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 #include <sys/sockio.h>
 #include <sys/kernel.h>
 #include <sys/syslog.h>
-#include <sys/md5.h>
+#include <libkern/crypto/md5.h>
+#include <libkern/OSAtomic.h>
+#include <kern/lock.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>
@@ -64,7 +95,9 @@ 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;
+extern lck_mtx_t *inet6_domain_mutex;
 
 #if IP6_AUTO_LINKLOCAL
 int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL;
@@ -72,19 +105,16 @@ int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL;
 int ip6_auto_linklocal = 1;    /* enable by default */
 #endif
 
-#ifndef __APPLE__
-struct callout in6_tmpaddrtimer_ch;
-#endif
-
 extern struct inpcbinfo udbinfo;
 extern struct inpcbinfo ripcbinfo;
+extern lck_mtx_t *ip6_mutex;
 
-static int get_rand_ifid __P((struct ifnet *, struct in6_addr *));
-static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *));
-static int get_hw_ifid __P((struct ifnet *, struct in6_addr *));
-static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *));
-static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *));
-static int in6_ifattach_loopback __P((struct ifnet *));
+static int get_rand_ifid(struct ifnet *, struct in6_addr *);
+static int generate_tmp_ifid(u_int8_t *, const u_int8_t *, u_int8_t *);
+static int get_hw_ifid(struct ifnet *, struct in6_addr *);
+static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
+static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *, struct in6_aliasreq *);
+static int in6_ifattach_loopback(struct ifnet *);
 
 #define EUI64_GBIT     0x01
 #define EUI64_UBIT     0x02
@@ -105,24 +135,24 @@ static int in6_ifattach_loopback __P((struct ifnet *));
  * We currently use MD5(hostname) for it.
  */
 static int
-get_rand_ifid(ifp, in6)
-       struct ifnet *ifp;
-       struct in6_addr *in6;   /*upper 64bits are preserved */
+get_rand_ifid(
+       __unused struct ifnet *ifp,
+       struct in6_addr *in6)   /* upper 64bits are preserved */
 {
        MD5_CTX ctxt;
        u_int8_t digest[16];
-       int hostnamelen = strlen(hostname);
+       int len = strlen(hostname);
 
 #if 0
        /* we need at least several letters as seed for ifid */
-       if (hostnamelen < 3)
+       if (len < 3)
                return -1;
 #endif
 
        /* generate 8 bytes of pseudo-random value. */
        bzero(&ctxt, sizeof(ctxt));
        MD5Init(&ctxt);
-       MD5Update(&ctxt, hostname, hostnamelen);
+       MD5Update(&ctxt, hostname, len);
        MD5Final(digest, &ctxt);
 
        /* assumes sizeof(digest) > sizeof(ifid) */
@@ -139,9 +169,10 @@ get_rand_ifid(ifp, in6)
 }
 
 static int
-generate_tmp_ifid(seed0, seed1, ret)
-       u_int8_t *seed0, *ret;
-       const u_int8_t *seed1;
+generate_tmp_ifid(
+       u_int8_t *seed0,
+       const u_int8_t *seed1,
+       u_int8_t *ret)
 {
        MD5_CTX ctxt;
        u_int8_t seed[16], digest[16], nullbuf[8];
@@ -158,8 +189,9 @@ generate_tmp_ifid(seed0, seed1, ret)
                        val32 = random() ^ tv.tv_usec;
                        bcopy(&val32, seed + sizeof(val32) * i, sizeof(val32));
                }
-       } else
+       } else {
                bcopy(seed0, seed, 8);
+       }
 
        /* copy the right-most 64-bits of the given address */
        /* XXX assumption on the size of IFID */
@@ -227,9 +259,9 @@ generate_tmp_ifid(seed0, seed1, ret)
  * XXX assumes single sockaddr_dl (AF_LINK address) per an interface
  */
 static int
-get_hw_ifid(ifp, in6)
-       struct ifnet *ifp;
-       struct in6_addr *in6;   /*upper 64bits are preserved */
+get_hw_ifid(
+       struct ifnet *ifp,
+       struct in6_addr *in6)   /* upper 64bits are preserved */
 {
        struct ifaddr *ifa;
        struct sockaddr_dl *sdl;
@@ -239,6 +271,8 @@ get_hw_ifid(ifp, in6)
        static u_int8_t allone[8] =
                { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
+       /* Why doesn't this code use ifnet_addrs? */
+       ifnet_lock_shared(ifp);
        for (ifa = ifp->if_addrlist.tqh_first;
             ifa;
             ifa = ifa->ifa_list.tqe_next)
@@ -253,11 +287,13 @@ get_hw_ifid(ifp, in6)
 
                goto found;
        }
+       ifnet_lock_done(ifp);
 
        return -1;
 
 found:
-       addr = LLADDR(sdl);
+       ifnet_lock_done(ifp);
+       addr = (u_int8_t *) LLADDR(sdl);
        addrlen = sdl->sdl_alen;
 
        /* get EUI64 */
@@ -266,9 +302,12 @@ found:
        case IFT_FDDI:
        case IFT_ATM:
        case IFT_IEEE1394:
+       case IFT_L2VLAN:
+       case IFT_IEEE8023ADLAG:
 #if IFT_IEEE80211
        case IFT_IEEE80211:
 #endif
+       case IFT_BRIDGE:
                /* IEEE802/EUI64 cases - what others? */
                /* IEEE1394 uses 16byte length address starting with EUI64 */
                if (addrlen > 8)
@@ -360,10 +399,10 @@ found:
  * sources.
  */
 static int
-get_ifid(ifp0, altifp, in6)
-       struct ifnet *ifp0;
-       struct ifnet *altifp;   /*secondary EUI64 source*/
-       struct in6_addr *in6;
+get_ifid(
+       struct ifnet *ifp0,
+       struct ifnet *altifp,   /* secondary EUI64 source */
+       struct in6_addr *in6)
 {
        struct ifnet *ifp;
 
@@ -382,8 +421,8 @@ get_ifid(ifp0, altifp, in6)
        }
 
        /* next, try to get it from some other hardware interface */
-       for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
-       {
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
                if (ifp == ifp0)
                        continue;
                if (get_hw_ifid(ifp, in6) != 0)
@@ -397,9 +436,11 @@ get_ifid(ifp0, altifp, in6)
                        nd6log((LOG_DEBUG,
                            "%s: borrow interface identifier from %s\n",
                            if_name(ifp0), if_name(ifp)));
+                       ifnet_head_done();
                        goto success;
                }
        }
+       ifnet_head_done();
 
        /* last resort: get from random number source */
        if (get_rand_ifid(ifp, in6) == 0) {
@@ -424,13 +465,14 @@ success:
 }
 
 static int
-in6_ifattach_linklocal(ifp, altifp)
-       struct ifnet *ifp;
-       struct ifnet *altifp;   /* secondary EUI64 source */
+in6_ifattach_linklocal(
+       struct ifnet *ifp,
+       struct ifnet *altifp,   /* secondary EUI64 source */
+       struct in6_aliasreq *ifra_passed)
 {
        struct in6_ifaddr *ia;
        struct in6_aliasreq ifra;
-       struct nd_prefix pr0;
+       struct nd_prefix pr0, *pr;
        int i, error;
 
        /*
@@ -438,36 +480,42 @@ in6_ifattach_linklocal(ifp, altifp)
         */
        bzero(&ifra, sizeof(ifra));
 
+       proto_plumb(PF_INET6, ifp);
+
        /*
         * in6_update_ifa() does not use ifra_name, but we accurately set it
         * for safety.
         */
        strncpy(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);
+       if (((ifp->if_type == IFT_PPP) ||  ((ifp->if_eflags & IFEF_NOAUTOIPV6LL) != 0)) &&
+                        ifra_passed != NULL)  /* PPP provided both addresses for us */
+               bcopy(&ifra_passed->ifra_addr, &(ifra.ifra_addr), sizeof(struct sockaddr_in6));
+       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);
 #if SCOPEDROUTING
-       ifra.ifra_addr.sin6_addr.s6_addr16[1] = 0
+               ifra.ifra_addr.sin6_addr.s6_addr16[1] = 0
 #else
-       ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */
+               ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */
 #endif
-       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 (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) {
-                       nd6log((LOG_ERR,
-                           "%s: no ifid available\n", if_name(ifp)));
-                       return -1;
+               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 (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) {
+                               nd6log((LOG_ERR,
+                           "   %s: no ifid available\n", if_name(ifp)));
+                               return -1;
+                       }
                }
-       }
 #if SCOPEDROUTING
-       ifra.ifra_addr.sin6_scope_id =
-               in6_addr2scopeid(ifp,  &ifra.ifra_addr.sin6_addr);
+               ifra.ifra_addr.sin6_scope_id =
+                       in6_addr2scopeid(ifp,  &ifra.ifra_addr.sin6_addr);
 #endif
-
+       }
        ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
        ifra.ifra_prefixmask.sin6_family = AF_INET6;
        ifra.ifra_prefixmask.sin6_addr = in6mask64;
@@ -481,7 +529,7 @@ in6_ifattach_linklocal(ifp, altifp)
 
        /*
         * Do not let in6_update_ifa() do DAD, since we need a random delay
-        * before sending an NS at the first time the inteface becomes up.
+        * before sending an NS at the first time the interface becomes up.
         * Instead, in6_if_up() will start DAD with a proper random delay.
         */
        ifra.ifra_flags |= IN6_IFF_NODAD;
@@ -489,9 +537,10 @@ in6_ifattach_linklocal(ifp, altifp)
        /*
         * Now call in6_update_ifa() to do a bunch of procedures to configure
         * a link-local address. We can set NULL to the 3rd argument, because
-        * we know there's no other link-local address on the interface.
+        * we know there's no other link-local address on the interface
+        * and therefore we are adding one (instead of updating one).
         */
-       if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
+       if ((error = in6_update_ifa(ifp, &ifra, NULL, M_WAITOK)) != 0) {
                /*
                 * XXX: When the interface does not support IPv6, this call
                 * would fail in the SIOCSIFADDR ioctl.  I believe the
@@ -556,18 +605,29 @@ in6_ifattach_linklocal(ifp, altifp)
         * address, and then reconfigure another one, the prefix is still
         * valid with referring to the old link-local address.
         */
-       if (nd6_prefix_lookup(&pr0) == NULL) {
-               if ((error = nd6_prelist_add(&pr0, NULL, NULL)) != 0)
+       if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
+               if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) {
+                       printf("in6_ifattach_linklocal: nd6_prelist_add failed %d\n", error);
+                       ifafree(&ia->ia_ifa);
                        return(error);
+               }
        }
 
-       in6_post_msg(ifp, KEV_INET6_NEW_LL_ADDR, ia);
+       if (ia != NULL) {
+               in6_post_msg(ifp, KEV_INET6_NEW_LL_ADDR, ia);
+               ifafree(&ia->ia_ifa);
+       }
+
+       /* Drop use count held above during lookup/add */
+       if (pr != NULL)
+               ndpr_rele(pr, FALSE);
+
        return 0;
 }
 
 static int
-in6_ifattach_loopback(ifp)
-       struct ifnet *ifp;      /* must be IFT_LOOP */
+in6_ifattach_loopback(
+       struct ifnet *ifp)      /* must be IFT_LOOP */
 {
        struct in6_aliasreq ifra;
        int error;
@@ -600,17 +660,17 @@ in6_ifattach_loopback(ifp)
        ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
        ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
 
-       /* we don't need to perfrom DAD on loopback interfaces. */
+       /* we don't need to perform DAD on loopback interfaces. */
        ifra.ifra_flags |= IN6_IFF_NODAD;
 
        /* skip registration to the prefix list. XXX should be temporary. */
        ifra.ifra_flags |= IN6_IFF_NOPFX;
 
        /*
-        * We can set NULL to the 3rd arg. See comments in
-        * in6_ifattach_linklocal().
+        * We are sure that this is a newly assigned address, so we can set
+        * NULL to the 3rd arg.
         */
-       if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
+       if ((error = in6_update_ifa(ifp, &ifra, NULL, M_WAITOK)) != 0) {
                log(LOG_ERR, "in6_ifattach_loopback: failed to configure "
                    "the loopback address on %s (errno=%d)\n",
                    if_name(ifp), error);
@@ -627,11 +687,11 @@ in6_ifattach_loopback(ifp)
  * when ifp == NULL, the caller is responsible for filling scopeid.
  */
 int
-in6_nigroup(ifp, name, namelen, in6)
-       struct ifnet *ifp;
-       const char *name;
-       int namelen;
-       struct in6_addr *in6;
+in6_nigroup(
+       struct ifnet *ifp,
+       const char *name,
+       int namelen,
+       struct in6_addr *in6)
 {
        const char *p;
        u_char *q;
@@ -647,11 +707,11 @@ in6_nigroup(ifp, name, namelen, in6)
        while (p && *p && *p != '.' && p - name < namelen)
                p++;
        if (p - name > sizeof(n) - 1)
-               return -1;      /*label too long*/
+               return -1;      /* label too long */
        l = p - name;
        strncpy(n, name, l);
        n[(int)l] = '\0';
-       for (q = n; *q; q++) {
+       for (q = (u_char *) n; *q; q++) {
                if ('A' <= *q && *q <= 'Z')
                        *q = *q - 'A' + 'a';
        }
@@ -674,9 +734,9 @@ in6_nigroup(ifp, name, namelen, in6)
 }
 
 void
-in6_nigroup_attach(name, namelen)
-       const char *name;
-       int namelen;
+in6_nigroup_attach(
+       const char *name,
+       int namelen)
 {
        struct ifnet *ifp;
        struct sockaddr_in6 mltaddr;
@@ -689,12 +749,14 @@ in6_nigroup_attach(name, namelen)
        if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0)
                return;
 
-       for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
-       {
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
                mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+               ifnet_lock_shared(ifp);
                IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+               ifnet_lock_done(ifp);
                if (!in6m) {
-                       if (!in6_addmulti(&mltaddr.sin6_addr, ifp, &error)) {
+                       if (!in6_addmulti(&mltaddr.sin6_addr, ifp, &error, 0)) {
                                nd6log((LOG_ERR, "%s: failed to join %s "
                                    "(errno=%d)\n", if_name(ifp),
                                    ip6_sprintf(&mltaddr.sin6_addr), 
@@ -702,12 +764,13 @@ in6_nigroup_attach(name, namelen)
                        }
                }
        }
+       ifnet_head_done();
 }
 
 void
-in6_nigroup_detach(name, namelen)
-       const char *name;
-       int namelen;
+in6_nigroup_detach(
+       const char *name,
+       int namelen)
 {
        struct ifnet *ifp;
        struct sockaddr_in6 mltaddr;
@@ -719,13 +782,16 @@ in6_nigroup_detach(name, namelen)
        if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0)
                return;
 
-       for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
-       {
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
                mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+               ifnet_lock_shared(ifp);
                IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+               ifnet_lock_done(ifp);
                if (in6m)
-                       in6_delmulti(in6m);
+                       in6_delmulti(in6m, 0);
        }
+       ifnet_head_done();
 }
 
 /*
@@ -733,41 +799,15 @@ in6_nigroup_detach(name, namelen)
  * nodelocal address needs to be configured onto only one of them.
  * XXX multiple link-local address case
  */
-void
-in6_ifattach(ifp, altifp)
-       struct ifnet *ifp;
-       struct ifnet *altifp;   /* secondary EUI64 source */
+int
+in6_ifattach(
+       struct ifnet *ifp,
+       struct ifnet *altifp,   /* secondary EUI64 source */
+       struct  in6_aliasreq *ifra)
 {
        static size_t if_indexlim = 8;
        struct in6_ifaddr *ia;
-       struct in6_addr in6;
-       u_long dl_tag;
-
-       switch (ifp->if_type) {
-#if IFT_BRIDGE /*OpenBSD 2.8*/
-       /* some of the interfaces are inherently not IPv6 capable */
-               case IFT_BRIDGE:
-               return;
-#endif
-#ifdef __APPLE__
-       case IFT_ETHER:
-               dl_tag = ether_attach_inet6(ifp);
-               break;
-
-       case IFT_LOOP:
-               dl_tag = lo_attach_inet6(ifp);
-#if NGIF > 0
-       case IFT_GIF:
-               dl_tag = gif_attach_proto_family(ifp, PF_INET6);
-               break;
-#endif
-#if NSTF > 0
-       case IFT_STF:
-               dl_tag = stf_attach_inet6(ifp);
-               break;
-#endif
-#endif
-       }
+       int error;
 
        /*
         * We have some arrays that should be indexed by if_index.
@@ -777,41 +817,85 @@ in6_ifattach(ifp, altifp)
         */
        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 */
+       }
+    
+       lck_mtx_lock(ip6_mutex);
+       /* grow in6_ifstat */
+       if (in6_ifstatmax < if_indexlim) {
+               size_t n;
+               caddr_t q;
+        
                n = if_indexlim * sizeof(struct in6_ifstat *);
                q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
+               if (q == NULL) {
+                       lck_mtx_unlock(ip6_mutex);
+                       return ENOBUFS;
+               }
                bzero(q, n);
                if (in6_ifstat) {
                        bcopy((caddr_t)in6_ifstat, q,
-                               olim * sizeof(struct in6_ifstat *));
+                               in6_ifstatmax * sizeof(struct in6_ifstat *));
                        FREE((caddr_t)in6_ifstat, M_IFADDR);
                }
                in6_ifstat = (struct in6_ifstat **)q;
                in6_ifstatmax = if_indexlim;
+       }
+    
+       if (in6_ifstat[ifp->if_index] == NULL) {
+               in6_ifstat[ifp->if_index] = (struct in6_ifstat *)
+                       _MALLOC(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK);
+               if (in6_ifstat[ifp->if_index] == NULL) {
+                       lck_mtx_unlock(ip6_mutex);
+                       return ENOBUFS;
+               }
+               bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat));
+       }
+       lck_mtx_unlock(ip6_mutex);
 
-               /* grow icmp6_ifstat */
+       /* grow icmp6_ifstat, use inet6_domain_mutex as that is used in 
+         * icmp6 routines 
+         */
+       lck_mtx_lock(inet6_domain_mutex);
+       if (icmp6_ifstatmax < if_indexlim) {
+               size_t n;
+               caddr_t q;
+        
                n = if_indexlim * sizeof(struct icmp6_ifstat *);
                q = (caddr_t)_MALLOC(n, M_IFADDR, M_WAITOK);
+               if (q == NULL) {
+                       lck_mtx_unlock(inet6_domain_mutex);
+                       return ENOBUFS;
+               }
                bzero(q, n);
                if (icmp6_ifstat) {
                        bcopy((caddr_t)icmp6_ifstat, q,
-                               olim * sizeof(struct icmp6_ifstat *));
+                               icmp6_ifstatmax * sizeof(struct icmp6_ifstat *));
                        FREE((caddr_t)icmp6_ifstat, M_IFADDR);
                }
                icmp6_ifstat = (struct icmp6_ifstat **)q;
                icmp6_ifstatmax = if_indexlim;
        }
 
+       if (icmp6_ifstat[ifp->if_index] == NULL) {
+               icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *)
+                       _MALLOC(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK);
+               if (icmp6_ifstat[ifp->if_index] == NULL) {
+                       lck_mtx_unlock(inet6_domain_mutex);
+                       return ENOBUFS;
+               }
+               bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat));
+       }
+       lck_mtx_unlock(inet6_domain_mutex);
+
+       /* initialize NDP variables */
+       if ((error = nd6_ifattach(ifp)) != 0)
+               return error;
+
        /* initialize scope identifiers */
-       scope6_ifattach(ifp);
+       if ((error = scope6_ifattach(ifp)) != 0)
+               return error;
 
        /*
         * quirks based on interface type
@@ -820,8 +904,10 @@ in6_ifattach(ifp, altifp)
 #if IFT_STF
        case IFT_STF:
                /*
-                * 6to4 interface is a very speical kind of beast.
-                * no multicast, no linklocal (based on 03 draft).
+                * 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 statinit;
 #endif
@@ -836,22 +922,16 @@ in6_ifattach(ifp, altifp)
                log(LOG_INFO, "in6_ifattach: "
                    "%s is not multicast capable, IPv6 not enabled\n",
                    if_name(ifp));
-               return;
+               return EINVAL;
        }
 
-       /* initialize NDP variables */
-       nd6_ifattach(ifp);
-
        /*
         * assign loopback address for loopback interface.
         * XXX multiple loopback interface case.
         */
        if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
-               in6 = in6addr_loopback;
-               if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) {
-                       if (in6_ifattach_loopback(ifp) != 0)
-                               return;
-               }
+               if (in6_ifattach_loopback(ifp) != 0)
+                       printf("in6_ifattach: in6_ifattach_loopback failed\n");
        }
 
        /*
@@ -860,11 +940,16 @@ in6_ifattach(ifp, altifp)
        if (ip6_auto_linklocal) {
                ia = in6ifa_ifpforlinklocal(ifp, 0);
                if (ia == NULL) {
-                       if (in6_ifattach_linklocal(ifp, altifp) == 0) {
+                       if (in6_ifattach_linklocal(ifp, altifp, ifra) == 0) {
                                /* linklocal address assigned */
                        } else {
+                               log(LOG_INFO, "in6_ifattach: %s failed to "
+                                   "attach a linklocal address.\n",
+                                   if_name(ifp));
                                /* failed to assign linklocal address. bark? */
                        }
+               } else {
+                       ifafree(&ia->ia_ifa);
                }
        }
 
@@ -876,17 +961,7 @@ statinit:
        if (in6_maxmtu < ifp->if_mtu)
                in6_maxmtu = ifp->if_mtu;
 
-       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));
-       }
-       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));
-       }
-
+    return 0;
 }
 
 /*
@@ -895,16 +970,13 @@ statinit:
  * from the ifnet list in bsdi.
  */
 void
-in6_ifdetach(ifp)
-       struct ifnet *ifp;
+in6_ifdetach(
+       struct ifnet *ifp)
 {
-       struct in6_ifaddr *ia, *oia;
+       struct in6_ifaddr *ia, *oia, *nia;
        struct ifaddr *ifa, *next;
        struct rtentry *rt;
-       short rtflags;
        struct sockaddr_in6 sin6;
-       struct in6_multi *in6m;
-       struct in6_multi *in6m_next;
 
        /* nuke prefix list.  this may try to remove some of ifaddrs as well */
        in6_purgeprefix(ifp);
@@ -913,13 +985,17 @@ in6_ifdetach(ifp)
        nd6_purge(ifp);
 
        /* nuke any of IPv6 addresses we have */
-       for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next)
-       {
-               next = ifa->ifa_list.tqe_next;
-               if (ifa->ifa_addr->sa_family != AF_INET6)
+       
+       lck_mtx_lock(nd6_mutex);
+       for (ia = in6_ifaddrs; ia != NULL; ia = nia) {
+               nia = ia->ia_next;
+               if (ia->ia_ifa.ifa_ifp != ifp)
                        continue;
-               in6_purgeaddr(ifa);
+               in6_purgeaddr(&ia->ia_ifa, 1);
        }
+       lck_mtx_unlock(nd6_mutex);
+
+       ifnet_lock_exclusive(ifp);
 
        /* undo everything done by in6_ifattach(), just in case */
        for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next)
@@ -935,25 +1011,24 @@ in6_ifdetach(ifp)
                ia = (struct in6_ifaddr *)ifa;
 
                /* remove from the routing table */
-               if ((ia->ia_flags & IFA_ROUTE)
-                && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) {
-                       rtflags = rt->rt_flags;
-                       rtfree(rt);
-                       rtrequest(RTM_DELETE,
+               if ((ia->ia_flags & IFA_ROUTE) &&
+                   (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0))) {
+                       (void) rtrequest(RTM_DELETE,
                                (struct sockaddr *)&ia->ia_addr,
                                (struct sockaddr *)&ia->ia_addr,
                                (struct sockaddr *)&ia->ia_prefixmask,
-                               rtflags, (struct rtentry **)0);
+                               rt->rt_flags, (struct rtentry **)0);
+                       rtfree(rt);
                }
 
                /* remove from the linked list */
-               TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
-               ifafree(&ia->ia_ifa);
+               if_detach_ifa(ifp, &ia->ia_ifa);
 
                /* also remove from the IPv6 address chain(itojun&jinmei) */
                oia = ia;
-               if (oia == (ia = in6_ifaddr))
-                       in6_ifaddr = ia->ia_next;
+               lck_mtx_lock(nd6_mutex);
+               if (oia == (ia = in6_ifaddrs))
+                       in6_ifaddrs = ia->ia_next;
                else {
                        while (ia->ia_next && (ia->ia_next != oia))
                                ia = ia->ia_next;
@@ -965,20 +1040,11 @@ in6_ifdetach(ifp)
                                    "list\n", if_name(ifp)));
                        }
                }
+               lck_mtx_unlock(nd6_mutex);
 
-               IFAFREE(&oia->ia_ifa);
-       }
-
-       /* leave from all multicast groups joined */
-       in6_pcbpurgeif0(LIST_FIRST(udbinfo.listhead), ifp);
-       in6_pcbpurgeif0(LIST_FIRST(ripcbinfo.listhead), ifp);
-       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;
+               ifafree(&oia->ia_ifa);
        }
+       ifnet_lock_done(ifp);
 
        /*
         * remove neighbor management table.  we call it twice just to make
@@ -996,24 +1062,38 @@ in6_ifdetach(ifp)
        sin6.sin6_family = AF_INET6;
        sin6.sin6_addr = in6addr_linklocal_allnodes;
        sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-       rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
-       if (rt && rt->rt_ifp == ifp) {
-               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_get_tmpifid(ifp, retbuf, baseid, generate)
-       struct ifnet *ifp;
-       u_int8_t *retbuf;
-       const u_int8_t *baseid;
-       int generate;
+in6_get_tmpifid(
+       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->if_index];
+       struct nd_ifinfo *ndi;
 
+       lck_rw_lock_shared(nd_if_rwlock);
+       ndi = &nd_ifinfo[ifp->if_index];
        bzero(nullbuf, sizeof(nullbuf));
        if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) == 0) {
                /* we've never created a random ID.  Create a new one. */
@@ -1028,47 +1108,38 @@ in6_get_tmpifid(ifp, retbuf, baseid, generate)
                                        ndi->randomid);
        }
        bcopy(ndi->randomid, retbuf, 8);
-}
-
-void 
-in6_tmpaddrtimer_funneled(void *ignored_arg)
-{
-#ifdef __APPLE__
-        boolean_t   funnel_state;
-        funnel_state = thread_funnel_set(network_flock, TRUE);
-#endif
-        in6_tmpaddrtimer(ignored_arg); 
-#ifdef __APPLE__
-        (void) thread_funnel_set(network_flock, FALSE);
-#endif 
+       lck_rw_done(nd_if_rwlock);
 }
 
 void
-in6_tmpaddrtimer(ignored_arg)
-       void *ignored_arg;
+in6_tmpaddrtimer(
+       __unused void *ignored_arg)
 {
        int i;
        struct nd_ifinfo *ndi;
        u_int8_t nullbuf[8];
-       int s = splnet();
 
-       timeout(in6_tmpaddrtimer_funneled, (caddr_t)0,
+       timeout(in6_tmpaddrtimer, (caddr_t)0,
                      (ip6_temp_preferred_lifetime - ip6_desync_factor -
                       ip6_temp_regen_advance) * hz);
 
-       bzero(nullbuf, sizeof(nullbuf));
-       for (i = 1; i < if_index + 1; i++) {
-               ndi = &nd_ifinfo[i];
-               if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) {
-                       /*
-                        * We've been generating a random ID on this interface.
-                        * Create a new one.
-                        */
-                       (void)generate_tmp_ifid(ndi->randomseed0,
-                                               ndi->randomseed1,
-                                               ndi->randomid);
+       if (ip6_use_tempaddr) {
+               lck_rw_lock_shared(nd_if_rwlock);
+               bzero(nullbuf, sizeof(nullbuf));
+               for (i = 1; i < nd_ifinfo_indexlim + 1; i++) {
+                       ndi = &nd_ifinfo[i];
+                       if (ndi->flags != ND6_IFF_PERFORMNUD)
+                               continue;
+                       if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) {
+                               /*
+                                * We've been generating a random ID on this interface.
+                                * Create a new one.
+                                */
+                               (void)generate_tmp_ifid(ndi->randomseed0,
+                                                       ndi->randomseed1,
+                                                       ndi->randomid);
+                       }
                }
+               lck_rw_done(nd_if_rwlock);
        }
-
-       splx(s);
 }