]> 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 8fe4ea5eb5a73b1d2d819d68bf080626eaddb57a..5995b212d0952bab0b0f5b32c956b6299bb77e17 100644 (file)
@@ -1,3 +1,30 @@
+/*
+ * 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 $  */
 
 #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>
@@ -65,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;
@@ -73,16 +105,16 @@ int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL;
 int ip6_auto_linklocal = 1;    /* enable by default */
 #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 *, struct in6_aliasreq *));
-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
@@ -103,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) */
@@ -137,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];
@@ -226,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;
@@ -238,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)
@@ -252,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 */
@@ -265,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)
@@ -359,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;
 
@@ -381,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)
@@ -396,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) {
@@ -423,22 +465,22 @@ success:
 }
 
 static int
-in6_ifattach_linklocal(ifp, altifp, ifra_passed)
-       struct ifnet *ifp;
-       struct ifnet *altifp;   /* secondary EUI64 source */
-       struct in6_aliasreq *ifra_passed;
+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;
-       int i, dl_tag, error;
+       struct nd_prefix pr0, *pr;
+       int i, error;
 
        /*
         * configure link-local address.
         */
        bzero(&ifra, sizeof(ifra));
 
-       dlil_plumb_protocol(PF_INET6, ifp, &dl_tag);
+       proto_plumb(PF_INET6, ifp);
 
        /*
         * in6_update_ifa() does not use ifra_name, but we accurately set it
@@ -446,7 +488,8 @@ in6_ifattach_linklocal(ifp, altifp, ifra_passed)
         */
        strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
 
-       if (ifp->if_type == IFT_PPP && ifra_passed != NULL)  /* PPP provided both addresses for us */
+       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;
@@ -497,7 +540,7 @@ in6_ifattach_linklocal(ifp, altifp, ifra_passed)
         * 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
@@ -562,18 +605,29 @@ in6_ifattach_linklocal(ifp, altifp, ifra_passed)
         * 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);
+               }
+       }
+
+       if (ia != NULL) {
+               in6_post_msg(ifp, KEV_INET6_NEW_LL_ADDR, ia);
+               ifafree(&ia->ia_ifa);
        }
 
-       in6_post_msg(ifp, KEV_INET6_NEW_LL_ADDR, ia);
+       /* 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;
@@ -616,7 +670,7 @@ in6_ifattach_loopback(ifp)
         * 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);
@@ -633,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;
@@ -657,7 +711,7 @@ in6_nigroup(ifp, name, namelen, in6)
        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';
        }
@@ -680,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;
@@ -695,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), 
@@ -708,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;
@@ -725,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();
 }
 
 /*
@@ -739,16 +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, ifra)
-       struct ifnet *ifp;
-       struct ifnet *altifp;   /* secondary EUI64 source */
-       struct  in6_aliasreq *ifra;
+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;
-
+       int error;
 
        /*
         * We have some arrays that should be indexed by if_index.
@@ -758,44 +817,85 @@ in6_ifattach(ifp, altifp, ifra)
         */
        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 */
-       nd6_ifattach(ifp);
+       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
@@ -822,7 +922,7 @@ in6_ifattach(ifp, altifp, ifra)
                log(LOG_INFO, "in6_ifattach: "
                    "%s is not multicast capable, IPv6 not enabled\n",
                    if_name(ifp));
-               return;
+               return EINVAL;
        }
 
        /*
@@ -830,11 +930,8 @@ in6_ifattach(ifp, altifp, ifra)
         * 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");
        }
 
        /*
@@ -846,11 +943,13 @@ in6_ifattach(ifp, altifp, ifra)
                        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));
+                               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);
                }
        }
 
@@ -862,16 +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;
 }
 
 /*
@@ -880,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);
@@ -898,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)
@@ -920,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;
@@ -950,27 +1040,11 @@ in6_ifdetach(ifp)
                                    "list\n", if_name(ifp)));
                        }
                }
+               lck_mtx_unlock(nd6_mutex);
 
-               IFAFREE(&oia->ia_ifa);
+               ifafree(&oia->ia_ifa);
        }
-
-#ifndef __APPLE__
-
-/*             This is a cause for reentrency, as those multicast addresses are
- *             freed both from the interface detaching and triggered by the closing of the socket
- *             Let the socket do the cleanup and not force it from the interface level
- */
-       /* 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;
-       }
-#endif /* __APPLE__ */
+       ifnet_lock_done(ifp);
 
        /*
         * remove neighbor management table.  we call it twice just to make
@@ -988,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. */
@@ -1020,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);
 }