]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/nd6_rtr.c
xnu-3789.31.2.tar.gz
[apple/xnu.git] / bsd / netinet6 / nd6_rtr.c
index ceb0f7d9c7c029b8a22d682e3b90f9000a99f7af..0d6c9f0448c64954535cc30e0c9c4327843e0ad2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -77,6 +77,7 @@
 #include <machine/machine_routines.h>
 
 #include <net/if.h>
+#include <net/if_var.h>
 #include <net/if_types.h>
 #include <net/if_dl.h>
 #include <net/route.h>
@@ -101,7 +102,6 @@ static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
 
 static struct in6_ifaddr *in6_pfx_newpersistaddr(struct nd_prefix *, int,
     int *);
-static void defrtrlist_sync(struct ifnet *);
 
 static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *,
        struct nd_defrouter *);
@@ -130,8 +130,8 @@ static void ndpr_trace(struct nd_prefix *, int);
 
 extern int nd6_recalc_reachtm_interval;
 
-static struct ifnet *nd6_defifp;
-int nd6_defifindex;
+static struct ifnet *nd6_defifp = NULL;
+int nd6_defifindex = 0;
 static unsigned int nd6_defrouter_genid;
 
 int ip6_use_tempaddr = 1; /* use temp addr by default for testing now */
@@ -439,13 +439,8 @@ nd6_ra_input(
        if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
                mcast = 1;
 
-       lck_rw_lock_shared(nd_if_rwlock);
-       if (ifp->if_index >= nd_ifinfo_indexlim) {
-               lck_rw_done(nd_if_rwlock);
-               goto freeit;
-       }
-       ndi = &nd_ifinfo[ifp->if_index];
-       VERIFY(ndi->initialized);
+       ndi = ND_IFINFO(ifp);
+       VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
        lck_mtx_lock(&ndi->lock);
        bzero(&dr0, sizeof (dr0));
        dr0.rtaddr = saddr6;
@@ -465,11 +460,19 @@ nd6_ra_input(
        }
        if (nd_ra->nd_ra_retransmit)
                ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
-       if (nd_ra->nd_ra_curhoplimit)
-               ndi->chlim = nd_ra->nd_ra_curhoplimit;
+       if (nd_ra->nd_ra_curhoplimit) {
+               if (ndi->chlim < nd_ra->nd_ra_curhoplimit) {
+                       ndi->chlim = nd_ra->nd_ra_curhoplimit;
+               } else if (ndi->chlim != nd_ra->nd_ra_curhoplimit) {
+                       nd6log((LOG_ERR,
+                           "RA with a lower CurHopLimit sent from "
+                           "%s on %s (current = %d, received = %d). "
+                           "Ignored.\n", ip6_sprintf(&ip6->ip6_src),
+                           if_name(ifp), ndi->chlim,
+                           nd_ra->nd_ra_curhoplimit));
+               }
+       }
        lck_mtx_unlock(&ndi->lock);
-       lck_rw_done(nd_if_rwlock);
-       ndi = NULL;
        lck_mtx_lock(nd6_mutex);
        dr = defrtrlist_update(&dr0);
        lck_mtx_unlock(nd6_mutex);
@@ -486,6 +489,9 @@ nd6_ra_input(
                    pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
                    pt = (struct nd_opt_hdr *)((caddr_t)pt +
                    (pt->nd_opt_len << 3))) {
+                       struct in6_addr pi_mask;
+                       bzero(&pi_mask, sizeof(pi_mask));
+
                        if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
                                continue;
                        pi = (struct nd_opt_prefix_info *)pt;
@@ -506,7 +512,18 @@ nd6_ra_input(
                                continue;
                        }
 
-                       if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) ||
+                       /*
+                        * To ignore ::/64 make sure bits beyond prefixlen
+                        * are set to zero
+                        */
+                       in6_prefixlen2mask(&pi_mask, pi->nd_opt_pi_prefix_len);
+                       pi->nd_opt_pi_prefix.s6_addr32[0] &= pi_mask.s6_addr32[0];
+                       pi->nd_opt_pi_prefix.s6_addr32[1] &= pi_mask.s6_addr32[1];
+                       pi->nd_opt_pi_prefix.s6_addr32[2] &= pi_mask.s6_addr32[2];
+                       pi->nd_opt_pi_prefix.s6_addr32[3] &= pi_mask.s6_addr32[3];
+
+                       if (IN6_IS_ADDR_UNSPECIFIED(&pi->nd_opt_pi_prefix) ||
+                           IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) ||
                            IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
                                nd6log((LOG_INFO,
                                    "%s: invalid prefix %s, ignored\n",
@@ -615,13 +632,6 @@ nd6_ra_input(
                        goto skip;
                }
 
-               lck_rw_lock_shared(nd_if_rwlock);
-               if (ifp->if_index >= nd_ifinfo_indexlim) {
-                       lck_rw_done(nd_if_rwlock);
-                       goto freeit;
-               }
-               ndi = &nd_ifinfo[ifp->if_index];
-               VERIFY(ndi->initialized);
                lck_mtx_lock(&ndi->lock);
                /* upper bound */
                if (ndi->maxmtu) {
@@ -630,7 +640,6 @@ nd6_ra_input(
 
                                ndi->linkmtu = mtu;
                                lck_mtx_unlock(&ndi->lock);
-                               lck_rw_done(nd_if_rwlock);
                                if (change) /* in6_maxmtu may change */
                                        in6_setmaxmtu();
                        } else {
@@ -640,17 +649,14 @@ nd6_ra_input(
                                    mtu, ip6_sprintf(&ip6->ip6_src),
                                    ndi->maxmtu));
                                lck_mtx_unlock(&ndi->lock);
-                               lck_rw_done(nd_if_rwlock);
                        }
                } else {
                        lck_mtx_unlock(&ndi->lock);
-                       lck_rw_done(nd_if_rwlock);
                        nd6log((LOG_INFO, "nd6_ra_input: mtu option "
                            "mtu=%d sent from %s; maxmtu unknown, "
                            "ignoring\n",
                            mtu, ip6_sprintf(&ip6->ip6_src)));
                }
-               ndi = NULL;
        }
 
 skip:
@@ -671,6 +677,9 @@ skip:
                goto bad;
        }
 
+       if (dr && dr->stateflags & NDDRF_MAPPED)
+               saddr6 = dr->rtaddr_mapped;
+
        nd6_cache_lladdr(ifp, &saddr6, lladdr, (int)lladdrlen,
            ND_ROUTER_ADVERT, 0);
 
@@ -711,9 +720,7 @@ bad:
 
 /* tell the change to user processes watching the routing socket. */
 static void
-nd6_rtmsg(cmd, rt)
-       int cmd;
-       struct rtentry *rt;
+nd6_rtmsg(int cmd, struct rtentry *rt)
 {
        struct rt_addrinfo info;
        struct ifnet *ifp = rt->rt_ifp;
@@ -742,6 +749,7 @@ defrouter_addreq(struct nd_defrouter *new, boolean_t scoped)
        struct rtentry *newrt = NULL;
        unsigned int ifscope;
        int err;
+       struct nd_ifinfo *ndi = ND_IFINFO(new->ifp);
 
        lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED);
        NDDR_LOCK_ASSERT_NOTHELD(new);
@@ -773,25 +781,59 @@ defrouter_addreq(struct nd_defrouter *new, boolean_t scoped)
        def.sin6_len = mask.sin6_len = gate.sin6_len
                = sizeof (struct sockaddr_in6);
        def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
-       gate.sin6_addr = new->rtaddr;
+
+       if (new->stateflags & NDDRF_MAPPED)
+               gate.sin6_addr = new->rtaddr_mapped;
+       else
+               gate.sin6_addr = new->rtaddr;
 
        ifscope = scoped ? new->ifp->if_index : IFSCOPE_NONE;
        NDDR_UNLOCK(new);
 
+       /*
+        * Cellular networks may have buggy deployments
+        * with gateway IPv6 link local address with same
+        * interface identifier as the one that has been
+        * assigned for the cellular context.
+        * If gateway is same as locally configured link local
+        * interface on cellular interface, generated a different one
+        * and store it in the nd_defrouter entry and use it to work
+        * on routing table
+        */
+       if (new->ifp->if_type == IFT_CELLULAR &&
+           !(new->stateflags & NDDRF_STATIC) &&
+           !(new->stateflags & NDDRF_MAPPED) &&
+           IN6_IS_ADDR_LINKLOCAL(&gate.sin6_addr) &&
+           ndi && !(ndi->flags & ND6_IFF_PERFORMNUD)) {
+               struct in6_ifaddr *tmp_ia6 = in6ifa_ifpforlinklocal(new->ifp, 0);
+
+               if (tmp_ia6 != NULL &&
+                   !(tmp_ia6->ia6_flags & IN6_IFF_NOTMANUAL) &&
+                   IN6_ARE_ADDR_EQUAL(&tmp_ia6->ia_addr.sin6_addr,
+                       &gate.sin6_addr)) {
+                       gate.sin6_addr.s6_addr8[15] += 1;
+                       new->rtaddr_mapped = gate.sin6_addr;
+                       new->stateflags |= NDDRF_MAPPED;
+
+                       nd6log((LOG_INFO, "%s: Default router %s mapped "
+                           "to ", if_name(new->ifp), ip6_sprintf(&new->rtaddr)));
+                       nd6log((LOG_INFO, "%s\n", ip6_sprintf(&new->rtaddr_mapped)));
+               }
+       }
+
        err = rtrequest_scoped(RTM_ADD, (struct sockaddr *)&def,
            (struct sockaddr *)&gate, (struct sockaddr *)&mask,
            RTF_GATEWAY, &newrt, ifscope);
 
        if (newrt) {
                RT_LOCK(newrt);
-               nd6_rtmsg(RTM_ADD, newrt); /* tell user process */
+               nd6_rtmsg(RTM_ADD, newrt);      /* tell user process */
                RT_REMREF_LOCKED(newrt);
                RT_UNLOCK(newrt);
                NDDR_LOCK(new);
                new->stateflags |= NDDRF_INSTALLED;
                if (ifscope != IFSCOPE_NONE)
                        new->stateflags |= NDDRF_IFSCOPE;
-               new->genid = nd6_defrouter_genid;
        } else {
                nd6log((LOG_ERR, "%s: failed to add default router "
                    "%s on %s scoped %d (errno = %d)\n", __func__,
@@ -864,7 +906,18 @@ defrouter_delreq(struct nd_defrouter *dr)
        def.sin6_len = mask.sin6_len = gate.sin6_len
                = sizeof (struct sockaddr_in6);
        def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
-       gate.sin6_addr = dr->rtaddr;
+
+       /*
+        * The router entry may be mapped to a different address.
+        * If that is the case, use the mapped address as gateway
+        * to do operation on the routing table.
+        * To get more context, read the related comment in
+        * defrouter_addreq
+        */
+       if (dr->stateflags & NDDRF_MAPPED)
+               gate.sin6_addr = dr->rtaddr_mapped;
+       else
+               gate.sin6_addr = dr->rtaddr;
 
        if (dr->ifp != NULL) {
                ifscope = (dr->stateflags & NDDRF_IFSCOPE) ?
@@ -873,6 +926,7 @@ defrouter_delreq(struct nd_defrouter *dr)
                ifscope = IFSCOPE_NONE;
        }
        NDDR_UNLOCK(dr);
+
        err = rtrequest_scoped(RTM_DELETE,
            (struct sockaddr *)&def, (struct sockaddr *)&gate,
            (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt, ifscope);
@@ -929,15 +983,12 @@ defrouter_reset(void)
        }
 
        /* Nuke primary (non-scoped) default router */
-       if (ip6_doscopedroute) {
-               bzero(&drany, sizeof (drany));
-               lck_mtx_init(&drany.nddr_lock, ifa_mtx_grp, ifa_mtx_attr);
-               lck_mtx_unlock(nd6_mutex);
-               defrouter_delreq(&drany);
-               lck_mtx_destroy(&drany.nddr_lock, ifa_mtx_grp);
-               lck_mtx_lock(nd6_mutex);
-       }
-
+       bzero(&drany, sizeof (drany));
+       lck_mtx_init(&drany.nddr_lock, ifa_mtx_grp, ifa_mtx_attr);
+       lck_mtx_unlock(nd6_mutex);
+       defrouter_delreq(&drany);
+       lck_mtx_destroy(&drany.nddr_lock, ifa_mtx_grp);
+       lck_mtx_lock(nd6_mutex);
 }
 
 int
@@ -948,6 +999,7 @@ defrtrlist_ioctl(u_long cmd, caddr_t data)
        struct ifnet *dr_ifp;
        int error = 0, add = 0;
 
+       /* XXX Handle mapped default router entries */
        switch (cmd) {
        case SIOCDRADD_IN6_32:          /* struct in6_defrouter_32 */
        case SIOCDRADD_IN6_64:          /* struct in6_defrouter_64 */
@@ -1016,33 +1068,54 @@ defrtrlist_ioctl(u_long cmd, caddr_t data)
        return (error);
 }
 
+/*
+ * XXX Please make sure to remove dr from the
+ * global default router tailq list before this
+ * function call.
+ * Also ensure that you release the list reference
+ * only after calling this routine.
+ */
 void
 defrtrlist_del(struct nd_defrouter *dr)
 {
-       struct nd_defrouter *deldr = NULL;
+#if (DEVELOPMENT || DEBUG)
+       struct nd_defrouter *dr_itr = NULL;
+#endif
        struct nd_prefix *pr;
        struct ifnet *ifp = dr->ifp;
+       struct nd_ifinfo *ndi = NULL;
        boolean_t resetmtu;
 
        lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
 
-       if (!ip6_doscopedroute && dr == TAILQ_FIRST(&nd_defrouter))
-               deldr = dr;     /* The router is primary. */
-
-       TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+#if (DEVELOPMENT || DEBUG)
+       /*
+        * Verify that the router is not in the global default
+        * router list.
+        * Can't use defrouter_lookup here because that just works
+        * with address and ifp pointer.
+        * We have to compare the memory here.
+        * Also we can't use ASSERT here as that is not defined
+        * for development builds.
+        */
+       TAILQ_FOREACH(dr_itr, &nd_defrouter, dr_entry)
+               VERIFY(dr != dr_itr);
+#endif
        ++nd6_defrouter_genid;
        /*
         * Flush all the routing table entries that use the router
         * as a next hop.
         */
-       if (ip6_doscopedroute || !ip6_forwarding) {
-               /* above is a good condition? */
-               NDDR_ADDREF(dr);
-               lck_mtx_unlock(nd6_mutex);
+       /* above is a good condition? */
+       NDDR_ADDREF(dr);
+       lck_mtx_unlock(nd6_mutex);
+       if (dr->stateflags & NDDRF_MAPPED)
+               rt6_flush(&dr->rtaddr_mapped, ifp);
+       else
                rt6_flush(&dr->rtaddr, ifp);
-               lck_mtx_lock(nd6_mutex);
-               NDDR_REMREF(dr);
-       }
+
+       lck_mtx_lock(nd6_mutex);
+       NDDR_REMREF(dr);
        nd6log2((LOG_INFO, "%s: freeing defrouter %s\n", if_name(dr->ifp),
            ip6_sprintf(&dr->rtaddr)));
        /*
@@ -1068,33 +1141,26 @@ defrtrlist_del(struct nd_defrouter *dr)
 
        pfxlist_onlink_check();
 
-       /*
-        * If the router is the primary one, choose a new one.  If Scoped
-        * Routing is enabled, always try to pick another eligible router
-        * on this interface.
-        */
-       if (deldr || ip6_doscopedroute)
-               defrouter_select(ifp);
-
        resetmtu = FALSE;
-       lck_rw_lock_shared(nd_if_rwlock);
-       if (ifp->if_index < nd_ifinfo_indexlim) {
-               struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
-               VERIFY(ndi->initialized);
-               lck_mtx_lock(&ndi->lock);
-               VERIFY(ndi->ndefrouters >= 0);
-               if (ndi->ndefrouters > 0 && --ndi->ndefrouters == 0) {
-                       nd6_ifreset(ifp);
-                       resetmtu = TRUE;
-               }
-               lck_mtx_unlock(&ndi->lock);
+       ndi = ND_IFINFO(ifp);
+       VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
+       lck_mtx_lock(&ndi->lock);
+       VERIFY(ndi->ndefrouters >= 0);
+       if (ndi->ndefrouters > 0 && --ndi->ndefrouters == 0) {
+               nd6_ifreset(ifp);
+               resetmtu = TRUE;
        }
-       lck_rw_done(nd_if_rwlock);
+       lck_mtx_unlock(&ndi->lock);
+
+       /*
+        * If the router is the primary one, choose a new one.
+        * We always try to pick another eligible router
+        * on this interface as we do scoped routing
+        */
+       defrouter_select(ifp);
 
        if (resetmtu)
                nd6_setmtu(ifp);
-
-       NDDR_REMREF(dr);        /* remove list reference */
 }
 
 int
@@ -1141,7 +1207,9 @@ defrtrlist_del_static(struct nd_defrouter *new)
                        NDDR_REMREF(dr);
                dr = NULL;
        } else {
+               TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
                defrtrlist_del(dr);
+               NDDR_REMREF(dr);        /* remove list reference */
                NDDR_REMREF(dr);
        }
        lck_mtx_unlock(nd6_mutex);
@@ -1209,28 +1277,38 @@ rtpref(struct nd_defrouter *dr)
 void
 defrouter_select(struct ifnet *ifp)
 {
-#pragma unused(ifp)
-       struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL;
-       struct nd_defrouter *installed_dr0 = NULL;
-       struct rtentry *rt = NULL;
+       struct nd_defrouter *dr = NULL;
+       struct nd_defrouter *selected_dr = NULL;
+       struct nd_defrouter *installed_dr = NULL;
        struct llinfo_nd6 *ln = NULL;
-       int  update = 0;
-       boolean_t found_installedrt = FALSE;
+       struct rtentry *rt = NULL;
+       struct nd_ifinfo *ndi = NULL;
+       unsigned int genid = 0;
+       boolean_t is_installed_reachable = FALSE;
 
        lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
 
-       /*
-        * We no longer install (default) interface route; only prefix routes
-        * are installed as interface routes.  Therefore, there is no harm in
-        * going through this routine even if a default interface is specified,
-        * which happens when Scoped Routing is enabled.  But for efficiency,
-        * we fall back to the original KAME logic when Scoped Routing is
-        * not in effect.
-        */
-       if (ip6_forwarding && !ip6_doscopedroute) {
-               nd6log((LOG_WARNING,
-                   "defrouter_select: called unexpectedly (forwarding=%d)\n",
-                   ip6_forwarding));
+       if (ifp == NULL) {
+               nd6log2((LOG_INFO,
+                   "%s:%d: Return early. NULL interface",
+                   __func__, __LINE__));
+               return;
+       }
+
+       if (ifp == lo_ifp) {
+               nd6log2((LOG_INFO,
+                   "%s:%d: Return early. "
+                   "Default router select called for loopback.\n",
+                   __func__, __LINE__));
+               return;
+       }
+
+       if (ifp->if_eflags & IFEF_IPV6_ROUTER) {
+               nd6log2((LOG_INFO,
+                   "%s:%d: Return early. "
+                   "Default router select called for interface"
+                   " %s with IFEF_IPV6_ROUTER flag set\n",
+                   __func__, __LINE__, if_name(ifp)));
                return;
        }
 
@@ -1238,8 +1316,33 @@ defrouter_select(struct ifnet *ifp)
         * Let's handle easy case (3) first:
         * If default router list is empty, there's nothing to be done.
         */
-       if (!TAILQ_FIRST(&nd_defrouter))
+       if (!TAILQ_FIRST(&nd_defrouter)) {
+               nd6log2((LOG_INFO,
+                   "%s:%d: Return early. "
+                   "Default router is empty.\n", __func__, __LINE__));
+               return;
+       }
+
+       /*
+        * Take an early exit if number of routers in nd_ifinfo is
+        * 0 for the interface.
+        */
+       ndi = ND_IFINFO(ifp);
+       if (!ndi || !ndi->initialized) {
+               nd6log2((LOG_INFO,
+                   "%s:%d: Return early. "
+                   "Interface %s's nd_ifinfo not initialized.\n",
+                   __func__, __LINE__, if_name(ifp)));
                return;
+       }
+
+       if (ndi->ndefrouters == 0) {
+               nd6log2((LOG_INFO,
+                   "%s:%d: Return early. "
+                   "%s does not have any default routers.\n",
+                   __func__, __LINE__, if_name(ifp)));
+               return;
+       }
 
        /*
         * Due to the number of times we drop nd6_mutex, we need to
@@ -1262,50 +1365,84 @@ defrouter_select(struct ifnet *ifp)
         *      selected_dr     = candidate for primary router
         *      installed_dr    = currently installed primary router
         */
-       for (dr = TAILQ_FIRST(&nd_defrouter); dr;
-           dr = TAILQ_NEXT(dr, dr_entry)) {
-               boolean_t reachable, advrouter;
+       genid = nd6_defrouter_genid;
+       dr = TAILQ_FIRST(&nd_defrouter);
+
+       while (dr != NULL) {
                struct in6_addr rtaddr;
-               struct ifnet *drifp;
-               struct nd_defrouter *drrele;
+               struct ifnet *drifp = NULL;
+               struct nd_defrouter *drrele = NULL;
 
-               drrele = NULL;
-               reachable = FALSE;
                NDDR_LOCK(dr);
-               rtaddr = *(&dr->rtaddr);
                drifp = dr->ifp;
-               advrouter = (drifp != NULL &&
-                   (drifp->if_eflags & IFEF_IPV6_ROUTER));
+               if (drifp != ifp) {
+                       NDDR_UNLOCK(dr);
+                       dr = TAILQ_NEXT(dr, dr_entry);
+                       continue;
+               }
+
+               /*
+                * Optimize for the common case.
+                * When the interface has only one default router
+                * there's no point checking for reachability as
+                * there's nothing else to choose from.
+                */
+               if (ndi->ndefrouters == 1) {
+                       nd6log2((LOG_INFO,
+                           "%s:%d: Fast forward default router selection "
+                           "as interface %s has learned only one default "
+                           "router and there's nothing else to choose from.\n",
+                           __func__, __LINE__, if_name(ifp)));
+                       VERIFY(selected_dr == NULL && installed_dr == NULL);
+                       selected_dr = dr;
+                       if (dr->stateflags & NDDRF_INSTALLED)
+                               installed_dr = dr;
+                       NDDR_ADDREF_LOCKED(selected_dr);
+                       NDDR_UNLOCK(dr);
+                       goto install_route;
+               }
+
+               if (dr->stateflags & NDDRF_MAPPED)
+                       rtaddr = dr->rtaddr_mapped;
+               else
+                       rtaddr = dr->rtaddr;
+
                NDDR_ADDREF_LOCKED(dr); /* for this for loop */
                NDDR_UNLOCK(dr);
 
-               lck_mtx_unlock(nd6_mutex);
                /* Callee returns a locked route upon success */
-               if ((rt = nd6_lookup(&rtaddr, 0, drifp, 0)) != NULL) {
-                       RT_LOCK_ASSERT_HELD(rt);
-                       if ((ln = rt->rt_llinfo) != NULL &&
+               if (selected_dr == NULL) {
+                       lck_mtx_unlock(nd6_mutex);
+                       if ((rt = nd6_lookup(&rtaddr, 0, drifp, 0)) != NULL &&
+                           (ln = rt->rt_llinfo) != NULL &&
                            ND6_IS_LLINFO_PROBREACH(ln)) {
-                               reachable = TRUE;
-                               if (selected_dr == NULL &&
-                                   (!ip6_doscopedroute ||
-                                   (drifp == nd6_defifp && !advrouter))) {
-                                       selected_dr = dr;
-                                       NDDR_ADDREF(selected_dr);
-                               }
+                               RT_LOCK_ASSERT_HELD(rt);
+                               selected_dr = dr;
+                               NDDR_ADDREF(selected_dr);
                        }
+                       lck_mtx_lock(nd6_mutex);
+               }
+
+               if (rt) {
                        RT_REMREF_LOCKED(rt);
                        RT_UNLOCK(rt);
                        rt = NULL;
                }
-               lck_mtx_lock(nd6_mutex);
 
-               /* Handle case (b) */
+               /*
+                * Handle case (b)
+                * When there are more than one routers on the same link, the one with
+                * the highest router preference will be installed.
+                * Since the list is in decreasing order of preference:
+                * 1) If selected_dr is not NULL, only use dr if it is static and has
+                *    equal preference and selected_dr is not static.
+                * 2) Else if selected_dr is NULL, and dr is static make selected_dr = dr
+                */
                NDDR_LOCK(dr);
-               if (ip6_doscopedroute && drifp == nd6_defifp && !advrouter &&
-                   (selected_dr == NULL || rtpref(dr) > rtpref(selected_dr) ||
-                   (rtpref(dr) == rtpref(selected_dr) &&
-                   (dr->stateflags & NDDRF_STATIC) &&
-                   !(selected_dr->stateflags & NDDRF_STATIC)))) {
+               if (((selected_dr && (rtpref(dr) >= rtpref(selected_dr)) &&
+                    !(selected_dr->stateflags & NDDRF_STATIC)) ||
+                    (selected_dr == NULL)) &&
+                   (dr->stateflags & NDDRF_STATIC)) {
                        if (selected_dr) {
                                /* Release it later on */
                                VERIFY(drrele == NULL);
@@ -1315,382 +1452,191 @@ defrouter_select(struct ifnet *ifp)
                        NDDR_ADDREF_LOCKED(selected_dr);
                }
 
-               if (!(dr->stateflags & NDDRF_INSTALLED)) {
-                       /*
-                        * If the router hasn't been installed and it is
-                        * reachable, try to install it later on below.
-                        * If it's static, try to install it anyway.
-                        */
-                       if (!advrouter && (reachable ||
-                           (dr->stateflags & NDDRF_STATIC))) {
-                               dr->genid = -1;
-                               ++update;
-                               nd6log2((LOG_INFO, "%s: possible router %s, "
-                                   "scoped=%d, static=%d\n", if_name(drifp),
-                                   ip6_sprintf(&rtaddr),
-                                   (dr->stateflags & NDDRF_IFSCOPE) ? 1 : 0,
-                                   (dr->stateflags & NDDRF_STATIC) ? 1 : 0));
-                       }
-                       NDDR_UNLOCK(dr);
-                       NDDR_REMREF(dr);        /* for this for loop */
-                       if (drrele != NULL)
-                               NDDR_REMREF(drrele);
-                       continue;
-               }
-
-               /* Record the currently installed primary/non-scoped router */
-               if (!ip6_doscopedroute || !(dr->stateflags & NDDRF_IFSCOPE)) {
+               /* Record the currently installed router */
+               if (dr->stateflags & NDDRF_INSTALLED) {
                        if (installed_dr == NULL) {
                                installed_dr = dr;
                                NDDR_ADDREF_LOCKED(installed_dr);
+                               if (dr->stateflags & NDDRF_MAPPED)
+                                       rtaddr = installed_dr->rtaddr_mapped;
+                               else
+                                       rtaddr = installed_dr->rtaddr;
+                               NDDR_UNLOCK(dr);
+                               lck_mtx_unlock(nd6_mutex);
+                               /* Callee returns a locked route upon success */
+                               if ((rt = nd6_lookup(&rtaddr, 0, ifp, 0)) != NULL) {
+                                       RT_LOCK_ASSERT_HELD(rt);
+                                       if ((ln = rt->rt_llinfo) != NULL &&
+                                           ND6_IS_LLINFO_PROBREACH(ln))
+                                               is_installed_reachable = TRUE;
+
+                                       RT_REMREF_LOCKED(rt);
+                                       RT_UNLOCK(rt);
+                                       rt = NULL;
+                               }
+                               lck_mtx_lock(nd6_mutex);
                        } else {
                                /* this should not happen; warn for diagnosis */
-                               log(LOG_ERR, "defrouter_select: more than one "
-                                   "%s default router is installed\n",
-                                   ip6_doscopedroute ? "non-scoped" : "");
+                               nd6log((LOG_ERR, "defrouter_select: more than one "
+                                   "default router is installed for interface :%s.\n",
+                                   if_name(ifp)));
+                               NDDR_UNLOCK(dr);
                        }
-               }
-               NDDR_UNLOCK(dr);
+               } else
+                       NDDR_UNLOCK(dr);
+
                NDDR_REMREF(dr);        /* for this for loop */
                if (drrele != NULL)
                        NDDR_REMREF(drrele);
-       }
-
-       /* If none was selected, use the currently installed one */
-       if (ip6_doscopedroute && selected_dr == NULL && installed_dr != NULL) {
-               selected_dr = installed_dr;
-               NDDR_ADDREF(selected_dr);
-       }
-
-       /*
-        * Install the unreachable one(s) if necesssary.
-        */
-       for (dr = TAILQ_FIRST(&nd_defrouter); dr;
-           dr = TAILQ_NEXT(dr, dr_entry)) {
-               struct nd_defrouter *_dr;
-
-               if (!ip6_doscopedroute)
-                       break;
-
-               NDDR_LOCK(dr);
-
-               /* If already (or will be) installed, skip */
-               if ((dr->stateflags & NDDRF_INSTALLED) || dr->genid == -1) {
-                       NDDR_UNLOCK(dr);
-                       continue;
-               }
 
-               /* See if there is already a default router for the link */
-               for (_dr = TAILQ_FIRST(&nd_defrouter); _dr;
-                   _dr = TAILQ_NEXT(_dr, dr_entry)) {
-                       if (_dr != dr)
-                               NDDR_LOCK(_dr);
-                       if (_dr == dr || _dr->ifp != dr->ifp) {
-                               if (_dr != dr)
-                                       NDDR_UNLOCK(_dr);
-                               continue;
+               /*
+                * Check if the list changed when we gave up
+                * the nd6_mutex lock
+                */
+               if(genid != nd6_defrouter_genid) {
+                       if (selected_dr) {
+                               NDDR_REMREF(selected_dr);
+                               selected_dr = NULL;
                        }
 
-                       if ((_dr->stateflags & NDDRF_INSTALLED) ||
-                           _dr->genid == -1) {
-                               if (_dr != dr)
-                                       NDDR_UNLOCK(_dr);
-                               break;
+                       if (installed_dr) {
+                               NDDR_REMREF(installed_dr);
+                               installed_dr = NULL;
                        }
-                       if (_dr != dr)
-                               NDDR_UNLOCK(_dr);
-               }
 
-               /* If none so far, schedule it to be installed below */
-               if (_dr == NULL && dr->ifp != NULL &&
-                   !(dr->ifp->if_eflags & IFEF_IPV6_ROUTER)) {
-                       dr->genid = -1;
-                       ++update;
-                       nd6log2((LOG_INFO, "%s: possible router %s, "
-                           "static=%d (unreachable)\n", if_name(dr->ifp),
-                           ip6_sprintf(&dr->rtaddr),
-                           (dr->stateflags & NDDRF_STATIC) ? 1 : 0));
+                       if (ndi->ndefrouters == 0) {
+                               nd6log2((LOG_INFO,
+                                   "%s:%d: Interface %s no longer "
+                                   "has any default routers. Abort.\n",
+                                   __func__, __LINE__, if_name(ifp)));
+                               goto out;
+                       }
+                       nd6log2((LOG_INFO,
+                           "%s:%d: Iterate default router list again "
+                           "for interface %s, as the list seems to have "
+                           "changed during release-reaquire of global "
+                           "nd6_mutex lock.\n",
+                           __func__, __LINE__, if_name(ifp)));
+
+                       is_installed_reachable = FALSE;
+                       genid = nd6_defrouter_genid;
+                       dr = TAILQ_FIRST(&nd_defrouter);
+               } else {
+                       dr = TAILQ_NEXT(dr, dr_entry);
                }
-               NDDR_UNLOCK(dr);
-       }
-
-       dr = selected_dr;
-       if (dr != NULL) {
-               nd6log2((LOG_INFO, "%s: considering primary default router %s, "
-                   "static=%d [round 1]\n", if_name(dr->ifp),
-                   ip6_sprintf(&dr->rtaddr),
-                   (dr->stateflags & NDDRF_STATIC) ? 1 : 0));
        }
 
        /*
         * If none of the default routers was found to be reachable,
-        * round-robin the list regardless of preference, except when
-        * Scoped Routing is enabled per case (c).
-        *
-        * Otherwise, if we have an installed router, check if the selected
-        * (reachable) router should really be preferred to the installed one.
-        * We only prefer the new router when the old one is not reachable
-        * or when the new one has a really higher preference value.
+        * round-robin the list regardless of preference.
+        * Please note selected_dr equal to NULL implies that even
+        * installed default router is not reachable
         */
-       if (!ip6_doscopedroute && selected_dr == NULL) {
-               if (installed_dr == NULL ||
-                   !TAILQ_NEXT(installed_dr, dr_entry)) {
-                       selected_dr = TAILQ_FIRST(&nd_defrouter);
-                       if (selected_dr)
-                               NDDR_ADDREF(selected_dr);
-               } else {
-                       selected_dr = TAILQ_NEXT(installed_dr, dr_entry);
-                       if (selected_dr)
-                               NDDR_ADDREF(selected_dr);
-               }
-       } else if (selected_dr != NULL && installed_dr != NULL) {
-               lck_mtx_unlock(nd6_mutex);
-               rt = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp, 0);
-               if (rt) {
-                       RT_LOCK_ASSERT_HELD(rt);
-                       if ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
-                           ND6_IS_LLINFO_PROBREACH(ln) &&
-                           (!ip6_doscopedroute ||
-                               installed_dr->ifp == nd6_defifp) &&
-                           rtpref(selected_dr) <= rtpref(installed_dr)) {
-                               NDDR_REMREF(selected_dr);
-                               selected_dr = installed_dr;
-                               NDDR_ADDREF(selected_dr);
+       if (selected_dr == NULL) {
+               if (installed_dr) {
+                       for (dr = TAILQ_NEXT(installed_dr, dr_entry); dr;
+                           dr = TAILQ_NEXT(dr, dr_entry)) {
+                               if (installed_dr->ifp != dr->ifp)
+                                       continue;
+                               selected_dr = dr;
+                               break;
                        }
-                       RT_REMREF_LOCKED(rt);
-                       RT_UNLOCK(rt);
-                       rt = NULL;
-                       found_installedrt = TRUE;
                }
-               lck_mtx_lock(nd6_mutex);
-       }
 
-       if (ip6_doscopedroute) {
                /*
-                * If the installed primary router is not on the current
-                * IPv6 default interface, demote it to a scoped entry.
+                * If none was installed or the installed one if the last
+                * one on the list, select the first one from the list
                 */
-               if (installed_dr != NULL && installed_dr->ifp != nd6_defifp &&
-                   !(installed_dr->stateflags & NDDRF_IFSCOPE)) {
-                       if (selected_dr != NULL &&
-                           selected_dr->ifp != nd6_defifp) {
-                               NDDR_REMREF(selected_dr);
-                               selected_dr = NULL;
+               if ((installed_dr == NULL) || (selected_dr == NULL)) {
+                       for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+                           dr = TAILQ_NEXT(dr, dr_entry)) {
+                               if (dr->ifp == ifp) {
+                                       selected_dr = dr;
+                                       break;
+                               }
                        }
-                       ++update;
                }
 
-               /*
-                * If the selected router is currently scoped, make sure
-                * we update (it needs to be promoted to primary.)
-                */
-               if (selected_dr != NULL &&
-                   (selected_dr->stateflags & NDDRF_IFSCOPE))
-                       ++update;
-
-               /*
-                * If the installed router is no longer reachable, remove
-                * it and install the selected router instead.
-                */
-               if (installed_dr != NULL
-                   && selected_dr != NULL 
-                   && installed_dr != selected_dr 
-                   && found_installedrt == FALSE
-                   && installed_dr->ifp == selected_dr->ifp) {
-                       /* skip it below */
-                       installed_dr0 = installed_dr;
-                       /* NB: we previousled referenced installed_dr */
-                       installed_dr = NULL;
-                       selected_dr->genid = -1;
-                       ++update;
-               }
-       }
-
-       /*
-        * If Scoped Routing is enabled and there's nothing to update,
-        * just return.  Otherwise, if Scoped Routing is disabled and if
-        * the selected router is different than the installed one,
-        * remove the installed router and install the selected one.
-        */
-       dr = selected_dr;
-       VERIFY(dr != NULL || ip6_doscopedroute);
-       if (!ip6_doscopedroute || !update) {
-               if (dr == NULL)
+               if ((selected_dr == NULL) && (installed_dr == NULL)) {
+                       nd6log2((LOG_INFO,
+                           "%s:%d: Between release and reaquire of global "
+                           "nd6_mutex lock, the list seems to have changed "
+                           "and it does not have any default routers for "
+                           "interface %s.\n",
+                           __func__, __LINE__, if_name(ifp)));
                        goto out;
-
-               if (dr != installed_dr) {
-                       nd6log2((LOG_INFO, "%s: no update, selected router %s, "
-                           "installed router %s\n", if_name(dr->ifp),
-                           ip6_sprintf(&dr->rtaddr), installed_dr != NULL ?
-                           ip6_sprintf(&installed_dr->rtaddr) : "NONE"));
-               } else {
-                       nd6log2((LOG_INFO, "%s: no update, router is %s\n",
-                           if_name(dr->ifp), ip6_sprintf(&dr->rtaddr)));
                }
-               if (!ip6_doscopedroute && installed_dr != dr) {
+
+               if (selected_dr != installed_dr)
+                       NDDR_ADDREF(selected_dr);
+       } else if (installed_dr != NULL) {
+               if (installed_dr != selected_dr) {
                        /*
-                        * No need to ADDREF dr because at this point
-                        * dr points to selected_dr, which already holds
-                        * a reference.
+                        * This means that selected default router is reachable
+                        * while installed one may or may not be.
+                        * Static router should always be considered as reachable
+                        * for router selection process.
                         */
-                       lck_mtx_unlock(nd6_mutex);
-                       if (installed_dr) {
-                               defrouter_delreq(installed_dr);
+                       if ((installed_dr->stateflags & NDDRF_STATIC) &&
+                           rtpref(installed_dr) >= rtpref(selected_dr)) {
+                               NDDR_REMREF(selected_dr);
+                               selected_dr = installed_dr;
+                       } else if (is_installed_reachable) {
+                               if (rtpref(selected_dr) <= rtpref(installed_dr)) {
+                                       NDDR_REMREF(selected_dr);
+                                       selected_dr = installed_dr;
+                               }
                        }
-                       defrouter_addreq(dr, FALSE);
-                       lck_mtx_lock(nd6_mutex);
+               } else {
+                       NDDR_REMREF(selected_dr);
                }
-               goto out;
-       }
-
-       /*
-        * Scoped Routing is enabled and we need to update.  The selected
-        * router needs to be installed as primary/non-scoped entry.  If
-        * there is any existing entry that is non-scoped, remove it from
-        * the routing table and reinstall it as scoped entry.
-        */
-       if (dr != NULL) {
-               nd6log2((LOG_INFO, "%s: considering primary default router %s, "
-                   "static=%d [round 2]\n", if_name(dr->ifp),
-                   ip6_sprintf(&dr->rtaddr),
-                   (dr->stateflags & NDDRF_STATIC) ? 1 : 0));
        }
 
+install_route: 
        /*
-        * On the following while loops we use two flags:
-        *   dr->genid
-        *   NDDRF_PROCESSED
-        *
-        * genid is used to skip entries that are not to be added/removed on the
-        * second while loop.
-        * NDDRF_PROCESSED is used to skip entries that were already
-        * processed.
-        * This is necessary because we drop the nd6_mutex and start the while
-        * loop again.
+        * If the selected router is different than the installed one,
+        * remove the installed router and install the selected one.
+        * Note that the selected router is never NULL here.
+        * Else check if the route entry scope has to be changed.
         */
-       TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) {
-               NDDR_LOCK(dr);
-               VERIFY((dr->stateflags & NDDRF_PROCESSED) == 0);
-               NDDR_UNLOCK(dr);
-       }
-       /* Remove conflicting entries */
-       dr = TAILQ_FIRST(&nd_defrouter);
-       while (dr) {
-               NDDR_LOCK(dr);
-               if (!(dr->stateflags & NDDRF_INSTALLED) ||
-                   dr->stateflags & NDDRF_PROCESSED) {
-                       NDDR_UNLOCK(dr);
-                       dr = TAILQ_NEXT(dr, dr_entry);
-                       continue;
-               }
-               dr->stateflags |= NDDRF_PROCESSED;
-
-               /* A NULL selected_dr will remove primary default route */
-               if ((dr == selected_dr && (dr->stateflags & NDDRF_IFSCOPE)) ||
-                   (dr != selected_dr && !(dr->stateflags & NDDRF_IFSCOPE))) {
-                       NDDR_ADDREF_LOCKED(dr);
-                       NDDR_UNLOCK(dr);
-                       lck_mtx_unlock(nd6_mutex);
-                       defrouter_delreq(dr);
-                       lck_mtx_lock(nd6_mutex);
-                       NDDR_LOCK(dr);
-                       if (dr && dr != installed_dr0)
-                               dr->genid = -1;
-                       NDDR_UNLOCK(dr);
-                       NDDR_REMREF(dr);
-                       /*
-                        * Since we lost nd6_mutex, we have to start over.
-                        */
-                       dr = TAILQ_FIRST(&nd_defrouter);
-                       continue;
-               }
-               NDDR_UNLOCK(dr);
-               dr = TAILQ_NEXT(dr, dr_entry);
-       }
-
-       /* -1 is a special number, make sure we don't use it for genid */
-       if (++nd6_defrouter_genid == -1)
-               nd6_defrouter_genid = 1;
-
-       TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) {
-               NDDR_LOCK(dr);
-               dr->stateflags &= ~NDDRF_PROCESSED;
-               NDDR_UNLOCK(dr);
-       }
-       /* Add the entries back */
-       dr = TAILQ_FIRST(&nd_defrouter);
-       while (dr) {
-               struct nd_defrouter *_dr;
-
-               NDDR_LOCK(dr);
-               if (dr->stateflags & NDDRF_PROCESSED ||
-                   dr->genid != -1) {
-                       NDDR_UNLOCK(dr);
-                       dr = TAILQ_NEXT(dr, dr_entry);
-                       continue;
-               }
-               dr->stateflags |= NDDRF_PROCESSED;
-
-               /* Handle case (b) */
-               for (_dr = TAILQ_FIRST(&nd_defrouter); _dr;
-                   _dr = TAILQ_NEXT(_dr, dr_entry)) {
-                       if (_dr == dr)
-                               continue;
-                       /*
-                        * This is safe because we previously checked if
-                        * _dr == dr.
-                        */
-                       NDDR_LOCK(_dr);
-                       if (_dr->ifp == dr->ifp && rtpref(_dr) >= rtpref(dr) &&
-                           (_dr->stateflags & NDDRF_INSTALLED)) {
-                               NDDR_ADDREF_LOCKED(_dr);
-                               NDDR_UNLOCK(_dr);
-                               break;
-                       }
-                       NDDR_UNLOCK(_dr);
-               }
-
-               /* If same preference and i/f, static entry takes precedence */
-               if (_dr != NULL && rtpref(_dr) == rtpref(dr) &&
-                   !(_dr->stateflags & NDDRF_STATIC) &&
-                   (dr->stateflags & NDDRF_STATIC)) {
-                       lck_mtx_unlock(nd6_mutex);
-                       defrouter_delreq(_dr);
-                       lck_mtx_lock(nd6_mutex);
-                       NDDR_REMREF(_dr);
-                       _dr = NULL;
-               }
-
-               if (_dr == NULL && !(dr->stateflags & NDDRF_INSTALLED)) {
-                       NDDR_ADDREF_LOCKED(dr);
-                       NDDR_UNLOCK(dr);
-                       lck_mtx_unlock(nd6_mutex);
-                       defrouter_addreq(dr, (selected_dr == NULL ||
-                           dr->ifp != selected_dr->ifp));
-                       dr->genid = nd6_defrouter_genid;
-                       lck_mtx_lock(nd6_mutex);
-                       NDDR_REMREF(dr);
-                       /*
-                        * Since we lost nd6_mutex, we have to start over.
-                        */
-                       dr = TAILQ_FIRST(&nd_defrouter);
-                       continue;
+       lck_mtx_unlock(nd6_mutex);
+       if (installed_dr != selected_dr) {
+               nd6log((LOG_INFO,
+                   "%s:%d: Found a better router for interface "
+                   "%s. Installing new default route.\n",
+                   __func__, __LINE__, if_name(ifp)));
+               if (installed_dr != NULL) {
+                       defrouter_delreq(installed_dr);
                }
-               NDDR_UNLOCK(dr);
-               dr = TAILQ_NEXT(dr, dr_entry);
+               /*
+                * Install scoped route if the interface is not
+                * the default nd6 interface.
+                */
+               defrouter_addreq(selected_dr,
+                   (selected_dr->ifp != nd6_defifp));
+       } else if (((installed_dr->stateflags & NDDRF_IFSCOPE) &&
+                   (installed_dr->ifp == nd6_defifp)) ||
+                  (!(installed_dr->stateflags & NDDRF_IFSCOPE) &&
+                   (installed_dr->ifp != nd6_defifp))) {
+               nd6log((LOG_INFO,
+                   "%s:%d: Need to reinstall default route for interface "
+                   "%s as its scope has changed.\n",
+                   __func__, __LINE__, if_name(ifp)));
+               defrouter_delreq(installed_dr);
+               defrouter_addreq(installed_dr,
+                   (installed_dr->ifp != nd6_defifp));
+       } else {
+               nd6log2((LOG_INFO,
+                   "%s:%d: No need to change the default "
+                   "route for interface %s.\n",
+                   __func__, __LINE__, if_name(ifp)));
        }
+       lck_mtx_lock(nd6_mutex);
 out:
-       TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) {
-               NDDR_LOCK(dr);
-               dr->stateflags &= ~NDDRF_PROCESSED;
-               NDDR_UNLOCK(dr);
-       }
-       if (selected_dr)
+       if (selected_dr && (selected_dr != installed_dr))
                NDDR_REMREF(selected_dr);
        if (installed_dr)
                NDDR_REMREF(installed_dr);
-       if (installed_dr0)
-               NDDR_REMREF(installed_dr0);
        lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
        VERIFY(nd_defrouter_busy);
        nd_defrouter_busy = FALSE;
@@ -1713,12 +1659,14 @@ defrtrlist_update_common(struct nd_defrouter *new, boolean_t scoped)
        if ((dr = defrouter_lookup(&new->rtaddr, ifp)) != NULL) {
                /* entry exists */
                if (new->rtlifetime == 0) {
+                       TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
                        defrtrlist_del(dr);
+                       NDDR_REMREF(dr);        /* remove list reference */
                        NDDR_REMREF(dr);
                        dr = NULL;
                } else {
                        int oldpref = rtpref(dr);
-
+                       struct nd_defrouter *p = NULL;
                        /* override */
                        dr->flags = new->flags; /* xxx flag check */
                        dr->rtlifetime = new->rtlifetime;
@@ -1731,36 +1679,30 @@ defrtrlist_update_common(struct nd_defrouter *new, boolean_t scoped)
                         * list of routers in the same preference band, unless
                         * it's already at that position.
                         */
-                       if (ip6_doscopedroute) {
-                               struct nd_defrouter *p = NULL;
-
-                               /* same preference and scoped; just return */
-                               if (rtpref(new) == oldpref && scoped)
-                                       return (dr);
-
-                               n = TAILQ_FIRST(&nd_defrouter);
-                               while (n != NULL) {
-                                       /* preference changed; sort it */
-                                       if (rtpref(new) != oldpref)
-                                               break;
-
-                                       /* not at the top of band; sort it */
-                                       if (n != dr && rtpref(n) == oldpref &&
-                                           (!p || rtpref(p) > rtpref(n)))
-                                               break;
-
-                                       p = n;
-                                       n = TAILQ_NEXT(n, dr_entry);
-                               }
-
-                               /* nothing has changed, just return */
-                               if (n == NULL && (scoped ||
-                                   !(dr->stateflags & NDDRF_IFSCOPE)))
-                                       return (dr);
-                       } else if (rtpref(new) == oldpref) {
+                       /* same preference and scoped; just return */
+                       if (rtpref(new) == oldpref && scoped)
                                return (dr);
+
+                       n = TAILQ_FIRST(&nd_defrouter);
+                       while (n != NULL) {
+                               /* preference changed; sort it */
+                               if (rtpref(new) != oldpref)
+                                       break;
+
+                               /* not at the top of band; sort it */
+                               if (n != dr && rtpref(n) == oldpref &&
+                                               (!p || rtpref(p) > rtpref(n)))
+                                       break;
+
+                               p = n;
+                               n = TAILQ_NEXT(n, dr_entry);
                        }
 
+                       /* nothing has changed, just return */
+                       if (n == NULL && (scoped ||
+                           !(dr->stateflags & NDDRF_IFSCOPE)))
+                               return (dr);
+
                        /*
                         * preferred router may be changed, so relocate
                         * this router.
@@ -1772,7 +1714,6 @@ defrtrlist_update_common(struct nd_defrouter *new, boolean_t scoped)
                         */
                        TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
                        new->stateflags = dr->stateflags;
-                       new->stateflags &= ~NDDRF_PROCESSED;
 
                        n = dr;
                        goto insert;
@@ -1792,17 +1733,12 @@ defrtrlist_update_common(struct nd_defrouter *new, boolean_t scoped)
                return (NULL);
        }
 
-       lck_rw_lock_shared(nd_if_rwlock);
-       if (ifp->if_index >= nd_ifinfo_indexlim)
-               goto freeit;
-       ndi = &nd_ifinfo[ifp->if_index];
-       VERIFY(ndi->initialized);
+       ndi = ND_IFINFO(ifp);
+       VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
        lck_mtx_lock(&ndi->lock);
        if (ip6_maxifdefrouters >= 0 &&
            ndi->ndefrouters >= ip6_maxifdefrouters) {
                lck_mtx_unlock(&ndi->lock);
-freeit:
-               lck_rw_done(nd_if_rwlock);
                nddr_free(n);
                return (NULL);
        }
@@ -1814,7 +1750,6 @@ freeit:
        ndi->ndefrouters++;
        VERIFY(ndi->ndefrouters != 0);
        lck_mtx_unlock(&ndi->lock);
-       lck_rw_done(nd_if_rwlock);
 
        nd6log2((LOG_INFO, "%s: allocating defrouter %s\n", if_name(ifp),
            ip6_sprintf(&new->rtaddr)));
@@ -1824,13 +1759,11 @@ freeit:
        memcpy(&n->rtaddr, &new->rtaddr, sizeof (n->rtaddr));
        n->flags = new->flags;
        n->stateflags = new->stateflags;
-       n->stateflags &= ~NDDRF_PROCESSED;
        n->rtlifetime = new->rtlifetime;
        n->expire = new->expire;
        n->base_calendartime = caltime.tv_sec;
        n->base_uptime = net_uptime();
        n->ifp = new->ifp;
-       n->genid = new->genid;
        n->err = new->err;
        NDDR_UNLOCK(n);
 insert:
@@ -1851,7 +1784,7 @@ insert:
        for (dr = TAILQ_FIRST(&nd_defrouter); dr;
            dr = TAILQ_NEXT(dr, dr_entry)) {
                if (rtpref(n) > rtpref(dr) ||
-                   (ip6_doscopedroute && !scoped && rtpref(n) == rtpref(dr)))
+                   (!scoped && rtpref(n) == rtpref(dr)))
                        break;
        }
        if (dr)
@@ -1876,45 +1809,6 @@ defrtrlist_update(struct nd_defrouter *new)
        return (dr);
 }
 
-static void
-defrtrlist_sync(struct ifnet *ifp)
-{
-       struct nd_defrouter *dr, new;
-
-       lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
-
-       if (!ip6_doscopedroute) {
-               defrouter_select(ifp);
-               return;
-       }
-
-       for (dr = TAILQ_FIRST(&nd_defrouter); dr;
-           dr = TAILQ_NEXT(dr, dr_entry)) {
-               NDDR_LOCK(dr);
-               if (dr->ifp == ifp && (dr->stateflags & NDDRF_INSTALLED))
-                       break;
-               NDDR_UNLOCK(dr);
-       }
-
-       if (dr == NULL) {
-               defrouter_select(ifp);
-       } else {
-               memcpy(&new.rtaddr, &dr->rtaddr, sizeof (new.rtaddr));
-               new.flags = dr->flags;
-               new.stateflags = dr->stateflags;
-               new.stateflags &= ~NDDRF_PROCESSED;
-               new.rtlifetime = dr->rtlifetime;
-               new.expire = dr->expire;
-               new.ifp = dr->ifp;
-               new.genid = dr->genid;
-               new.err = dr->err;
-               NDDR_UNLOCK(dr);
-               dr = defrtrlist_update_common(&new, FALSE);
-               if (dr)
-                       NDDR_REMREF(dr);
-       }
-}
-
 static struct nd_pfxrouter *
 pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr)
 {
@@ -1964,8 +1858,20 @@ pfxrtr_del(struct nd_pfxrouter *pfr, struct nd_prefix *pr)
        zfree(ndprtr_zone, pfr);
 }
 
+/*
+ * The routine has been modified to atomically refresh expiry
+ * time for nd6 prefix as the part of lookup.
+ * There's a corner case where a system going
+ * in sleep gets rid of manual addresses configured in the system
+ * and then schedules the prefix for deletion.
+ * However before the prefix gets deleted, if system comes out
+ * from sleep and configures same address before prefix deletion
+ * , the later prefix deletion will remove the prefix route and
+ * the system will not be able to communicate with other IPv6
+ * neighbor nodes in the same subnet.
+ */
 struct nd_prefix *
-nd6_prefix_lookup(struct nd_prefix *pr)
+nd6_prefix_lookup(struct nd_prefix *pr, int nd6_prefix_expiry)
 {
        struct nd_prefix *search;
 
@@ -1976,6 +1882,9 @@ nd6_prefix_lookup(struct nd_prefix *pr)
                    pr->ndpr_plen == search->ndpr_plen &&
                    in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
                    &search->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
+                       if (nd6_prefix_expiry != ND6_PREFIX_EXPIRY_UNSPEC) {
+                               search->ndpr_expire = nd6_prefix_expiry;
+                       }
                        NDPR_ADDREF_LOCKED(search);
                        NDPR_UNLOCK(search);
                        break;
@@ -1987,84 +1896,6 @@ nd6_prefix_lookup(struct nd_prefix *pr)
        return (search);
 }
 
-static void
-purge_detached(struct ifnet *ifp)
-{
-       struct nd_prefix *pr, *pr_next;
-       struct in6_ifaddr *ia;
-       struct ifaddr *ifa, *ifa_next;
-       boolean_t removed = FALSE;
-
-       lck_mtx_lock(nd6_mutex);
-
-       pr = nd_prefix.lh_first;
-repeat:
-       while (pr) {
-               NDPR_LOCK(pr);
-               pr_next = pr->ndpr_next;
-               if (pr->ndpr_ifp != ifp ||
-                   IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
-                   ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
-                   !LIST_EMPTY(&pr->ndpr_advrtrs))) {
-                       NDPR_UNLOCK(pr);
-                       pr = pr_next;
-                       continue;
-               }
-               NDPR_ADDREF_LOCKED(pr);
-               NDPR_UNLOCK(pr);
-               ifnet_lock_shared(ifp);
-               for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa_next) {
-                       IFA_LOCK(ifa);
-                       ifa_next = ifa->ifa_list.tqe_next;
-                       if (ifa->ifa_addr->sa_family != AF_INET6) {
-                               IFA_UNLOCK(ifa);
-                               continue;
-                       }
-                       ia = (struct in6_ifaddr *)ifa;
-                       if ((ia->ia6_flags & IN6_IFF_AUTOCONF) ==
-                           IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) {
-                               IFA_ADDREF_LOCKED(ifa); /* for us */
-                               IFA_UNLOCK(ifa);
-                               /*
-                                * Purging the address requires writer access
-                                * to the address list, so drop the ifnet lock
-                                * now and repeat from beginning.
-                                */
-                               ifnet_lock_done(ifp);
-                               lck_mtx_unlock(nd6_mutex);
-                               in6_purgeaddr(ifa);
-                               IFA_REMREF(ifa); /* drop ours */
-                               lck_mtx_lock(nd6_mutex);
-                               NDPR_REMREF(pr);
-                               pr = nd_prefix.lh_first;
-                               goto repeat;
-                       }
-                       IFA_UNLOCK(ifa);
-               }
-               ifnet_lock_done(ifp);
-               NDPR_LOCK(pr);
-               if (pr->ndpr_addrcnt == 0 &&
-                   !(pr->ndpr_stateflags & NDPRF_DEFUNCT)) {
-                       prelist_remove(pr);
-                       NDPR_UNLOCK(pr);
-                       removed = TRUE;
-                       /*
-                        * Reset the search from the beginning because
-                        * nd6_mutex may have been dropped in
-                        * prelist_remove().
-                        */
-                       pr_next = nd_prefix.lh_first;
-               } else {
-                       NDPR_UNLOCK(pr);
-               }
-               NDPR_REMREF(pr);
-               pr = pr_next;
-       }
-       if (removed)
-               pfxlist_onlink_check();
-       lck_mtx_unlock(nd6_mutex);
-}
-
 int
 nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr,
     struct nd_prefix **newp, boolean_t force_scoped)
@@ -2075,34 +1906,14 @@ nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr,
        int i, error;
 
        if (ip6_maxifprefixes >= 0) {
-               lck_rw_lock_shared(nd_if_rwlock);
-               if (ifp->if_index >= nd_ifinfo_indexlim) {
-                       lck_rw_done(nd_if_rwlock);
-                       return (EINVAL);
-               }
-               ndi = &nd_ifinfo[ifp->if_index];
-               VERIFY(ndi->initialized);
+               ndi = ND_IFINFO(ifp);
+               VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
                lck_mtx_lock(&ndi->lock);
-               if (ndi->nprefixes >= ip6_maxifprefixes / 2) {
-                       lck_mtx_unlock(&ndi->lock);
-                       lck_rw_done(nd_if_rwlock);
-                       purge_detached(ifp);
-                       lck_rw_lock_shared(nd_if_rwlock);
-                       /*
-                        * Refresh pointer since nd_ifinfo[] may have grown;
-                        * repeating the bounds check against nd_ifinfo_indexlim
-                        * isn't necessary since the array never shrinks.
-                        */
-                       ndi = &nd_ifinfo[ifp->if_index];
-                       lck_mtx_lock(&ndi->lock);
-               }
                if (ndi->nprefixes >= ip6_maxifprefixes) {
                        lck_mtx_unlock(&ndi->lock);
-                       lck_rw_done(nd_if_rwlock);
                        return (ENOMEM);
                }
                lck_mtx_unlock(&ndi->lock);
-               lck_rw_done(nd_if_rwlock);
        }
 
        new = ndpr_alloc(M_WAITOK);
@@ -2149,6 +1960,11 @@ nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr,
        new->ndpr_debug |= IFD_ATTACHED;
        NDPR_ADDREF(new);       /* for nd_prefix list */
 
+       lck_mtx_lock(&ndi->lock);
+       ndi->nprefixes++;
+       VERIFY(ndi->nprefixes != 0);
+       lck_mtx_unlock(&ndi->lock);
+
        /* ND_OPT_PI_FLAG_ONLINK processing */
        if (new->ndpr_raf_onlink) {
                int e;
@@ -2168,20 +1984,6 @@ nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr,
                pfxrtr_add(new, dr);
        }
 
-       lck_rw_lock_shared(nd_if_rwlock);
-       /*
-        * Refresh pointer since nd_ifinfo[] may have grown;
-        * repeating the bounds check against nd_ifinfo_indexlim
-        * isn't necessary since the array never shrinks.
-        */
-       ndi = &nd_ifinfo[ifp->if_index];
-       VERIFY(ndi->initialized);
-       lck_mtx_lock(&ndi->lock);
-       ndi->nprefixes++;
-       VERIFY(ndi->nprefixes != 0);
-       lck_mtx_unlock(&ndi->lock);
-       lck_rw_done(nd_if_rwlock);
-
        lck_mtx_unlock(nd6_mutex);
 
        return (0);
@@ -2196,6 +1998,7 @@ prelist_remove(struct nd_prefix *pr)
        struct nd_pfxrouter *pfr, *next;
        struct ifnet *ifp = pr->ndpr_ifp;
        int e;
+       struct nd_ifinfo *ndi = NULL;
 
        lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
        NDPR_LOCK_ASSERT_HELD(pr);
@@ -2257,16 +2060,12 @@ prelist_remove(struct nd_prefix *pr)
                pfxrtr_del(pfr, pr);
        }
 
-       lck_rw_lock_shared(nd_if_rwlock);
-       if (ifp->if_index < nd_ifinfo_indexlim) {
-               struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
-               VERIFY(ndi->initialized);
-               lck_mtx_lock(&ndi->lock);
-               VERIFY(ndi->nprefixes > 0);
-               ndi->nprefixes--;
-               lck_mtx_unlock(&ndi->lock);
-       }
-       lck_rw_done(nd_if_rwlock);
+       ndi = ND_IFINFO(ifp);
+       VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
+       lck_mtx_lock(&ndi->lock);
+       VERIFY(ndi->nprefixes > 0);
+       ndi->nprefixes--;
+       lck_mtx_unlock(&ndi->lock);
 
        /* This must not be the last reference to the nd_prefix */
        if (NDPR_REMREF_LOCKED(pr) == NULL) {
@@ -2312,7 +2111,7 @@ prelist_update(
 #endif
        }
 
-       if ((pr = nd6_prefix_lookup(new)) != NULL) {
+       if ((pr = nd6_prefix_lookup(new, ND6_PREFIX_EXPIRY_UNSPEC)) != NULL) {
                /*
                 * nd6_prefix_lookup() ensures that pr and new have the same
                 * prefix on a same interface.
@@ -2363,8 +2162,6 @@ prelist_update(
                NDPR_REMREF(pr);
                lck_mtx_unlock(nd6_mutex);
        } else {
-               struct nd_prefix *newpr = NULL;
-
                newprefix = 1;
 
                if (new->ndpr_vltime == 0)
@@ -2374,33 +2171,16 @@ prelist_update(
 
                bzero(&new->ndpr_addr, sizeof (struct in6_addr));
 
-               error = nd6_prelist_add(new, dr, &newpr, FALSE);
-               if (error != 0 || newpr == NULL) {
+               error = nd6_prelist_add(new, dr, &pr, FALSE);
+               if (error != 0 || pr == NULL) {
                        nd6log((LOG_NOTICE, "prelist_update: "
                            "nd6_prelist_add failed for %s/%d on %s "
                            "errno=%d, returnpr=0x%llx\n",
                            ip6_sprintf(&new->ndpr_prefix.sin6_addr),
                            new->ndpr_plen, if_name(new->ndpr_ifp),
-                           error, (uint64_t)VM_KERNEL_ADDRPERM(newpr)));
+                           error, (uint64_t)VM_KERNEL_ADDRPERM(pr)));
                        goto end; /* we should just give up in this case. */
                }
-
-               /*
-                * XXX: from the ND point of view, we can ignore a prefix
-                * with the on-link bit being zero.  However, we need a
-                * prefix structure for references from autoconfigured
-                * addresses.  Thus, we explicitly make sure that the prefix
-                * itself expires now.
-                */
-               NDPR_LOCK(newpr);
-               if (newpr->ndpr_raf_onlink == 0) {
-                       newpr->ndpr_vltime = 0;
-                       newpr->ndpr_pltime = 0;
-                       in6_init_prefix_ltimes(newpr);
-               }
-
-               pr = newpr;
-               NDPR_UNLOCK(newpr);
        }
 
        /*
@@ -2410,7 +2190,7 @@ prelist_update(
 
        /* 5.5.3 (a). Ignore the prefix without the A bit set. */
        if (!new->ndpr_raf_auto)
-               goto afteraddrconf;
+               goto end;
 
        /*
         * 5.5.3 (b). the link-local prefix should have been ignored in
@@ -2623,8 +2403,6 @@ prelist_update(
                }
        }
 
-afteraddrconf:
-
 end:
        if (pr != NULL)
                NDPR_REMREF(pr);
@@ -2940,7 +2718,11 @@ find_pfxlist_reachable_router(struct nd_prefix *pr)
        pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs);
        while (pfxrtr) {
                ifp = pfxrtr->router->ifp;
-               rtaddr = pfxrtr->router->rtaddr;
+               if (pfxrtr->router->stateflags & NDDRF_MAPPED)
+                       rtaddr = pfxrtr->router->rtaddr_mapped;
+               else
+                       rtaddr = pfxrtr->router->rtaddr;
+
                NDPR_UNLOCK(pr);
                lck_mtx_unlock(nd6_mutex);
                /* Callee returns a locked route upon success */
@@ -3272,7 +3054,7 @@ pfxlist_onlink_check(void)
                                IFA_LOCK(&ifa->ia_ifa);
                                if (ifa->ia6_flags & IN6_IFF_DETACHED) {
                                        ifa->ia6_flags &= ~IN6_IFF_DETACHED;
-                                       ifa->ia6_flags |= IN6_IFF_TENTATIVE;
+                                       in6_ifaddr_set_dadprogress((struct in6_ifaddr *)ifa);
                                        IFA_UNLOCK(&ifa->ia_ifa);
                                        nd6_dad_start((struct ifaddr *)ifa, 0);
                                } else {
@@ -3296,7 +3078,7 @@ pfxlist_onlink_check(void)
                        }
                        if (ifa->ia6_flags & IN6_IFF_DETACHED) {
                                ifa->ia6_flags &= ~IN6_IFF_DETACHED;
-                               ifa->ia6_flags |= IN6_IFF_TENTATIVE;
+                               in6_ifaddr_set_dadprogress((struct in6_ifaddr *)ifa);
                                IFA_UNLOCK(&ifa->ia_ifa);
                                /* Do we need a delay in this case? */
                                nd6_dad_start((struct ifaddr *)ifa, 0);
@@ -3351,7 +3133,7 @@ nd6_prefix_sync(struct ifnet *ifp)
 
        lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
 
-       if (!ip6_doscopedroute || ifp == NULL)
+       if (ifp == NULL)
                return;
 
        for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
@@ -3476,12 +3258,7 @@ nd6_prefix_onlink_common(struct nd_prefix *pr, boolean_t force_scoped,
        if (opr != NULL)
                NDPR_REMREF(opr);
 
-       if (!ip6_doscopedroute) {
-               /* if an interface route already exists, just return */
-               if (opr != NULL)
-                       return (0);
-               ifscope = IFSCOPE_NONE;
-       } else if (!force_scoped) {
+       if (!force_scoped) {
                /*
                 * If a primary/non-scoped interface route already exists,
                 * install the new one as a scoped entry.  If the existing
@@ -3596,14 +3373,13 @@ nd6_prefix_onlink_common(struct nd_prefix *pr, boolean_t force_scoped,
         */
        if (!(pr->ndpr_stateflags & NDPRF_DEFUNCT) &&
            (rt != NULL || error == EEXIST)) {
-               struct nd_ifinfo *ndi;
+               struct nd_ifinfo *ndi = NULL;
 
                VERIFY(pr->ndpr_prproxy_sols_cnt == 0);
                VERIFY(RB_EMPTY(&pr->ndpr_prproxy_sols));
 
-               lck_rw_lock_shared(nd_if_rwlock);
                ndi = ND_IFINFO(ifp);
-               VERIFY(ndi != NULL && ndi->initialized);
+               VERIFY((NULL != ndi)  && (TRUE == ndi->initialized));
                lck_mtx_lock(&ndi->lock);
 
                pr->ndpr_rt = rt;       /* keep reference from rtrequest */
@@ -3625,7 +3401,6 @@ nd6_prefix_onlink_common(struct nd_prefix *pr, boolean_t force_scoped,
                }
 
                lck_mtx_unlock(&ndi->lock);
-               lck_rw_done(nd_if_rwlock);
        } else if (rt != NULL && pr->ndpr_stateflags & NDPRF_DEFUNCT)
                rtfree(rt);
 
@@ -3668,7 +3443,6 @@ nd6_prefix_offlink(struct nd_prefix *pr)
 {
        int plen, error = 0, prproxy;
        struct ifnet *ifp = pr->ndpr_ifp;
-       struct nd_prefix *opr;
        struct sockaddr_in6 sa6, mask6, prefix;
        struct rtentry *rt = NULL, *ndpr_rt = NULL;
        unsigned int ifscope;
@@ -3716,66 +3490,6 @@ nd6_prefix_offlink(struct nd_prefix *pr)
                RT_UNLOCK(rt);
                rtfree(rt);
 
-               /*
-                * The following check takes place only when Scoped Routing
-                * is not enabled.  There might be the same prefix on another
-                * interface, the prefix which could not be on-link just
-                * because we have the interface route (see comments in
-                * nd6_prefix_onlink).  If there's one, try to make the prefix
-                * on-link on the interface.
-                */
-               lck_mtx_lock(nd6_mutex);
-               opr = nd_prefix.lh_first;
-               while (opr) {
-                       /* does not apply in the Scoped Routing case */
-                       if (ip6_doscopedroute)
-                               break;
-
-                       if (opr == pr) {
-                               opr = opr->ndpr_next;
-                               continue;
-                       }
-
-                       NDPR_LOCK(opr);
-                       if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
-                               NDPR_UNLOCK(opr);
-                               opr = opr->ndpr_next;
-                               continue;
-                       }
-                       /*
-                        * KAME specific: detached prefixes should not be
-                        * on-link.
-                        */
-                       if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) {
-                               NDPR_UNLOCK(opr);
-                               opr = opr->ndpr_next;
-                               continue;
-                       }
-                       if (opr->ndpr_plen == plen &&
-                           in6_are_prefix_equal(&prefix.sin6_addr,
-                           &opr->ndpr_prefix.sin6_addr, plen)) {
-                               int e;
-
-                               NDPR_ADDREF_LOCKED(opr);
-                               NDPR_UNLOCK(opr);
-                               if ((e = nd6_prefix_onlink(opr)) != 0) {
-                                       nd6log((LOG_ERR,
-                                           "nd6_prefix_offlink: failed to "
-                                           "recover a prefix %s/%d from %s "
-                                           "to %s (errno = %d)\n",
-                                           ip6_sprintf(
-                                           &opr->ndpr_prefix.sin6_addr),
-                                           opr->ndpr_plen, if_name(ifp),
-                                           if_name(opr->ndpr_ifp), e));
-                               }
-                               NDPR_REMREF(opr);
-                               opr = nd_prefix.lh_first;
-                       } else {
-                               NDPR_UNLOCK(opr);
-                               opr = opr->ndpr_next;
-                       }
-               }
-               lck_mtx_unlock(nd6_mutex);
        } else {
                nd6log((LOG_ERR,
                    "nd6_prefix_offlink: failed to delete route: "
@@ -3818,9 +3532,9 @@ nd6_prefix_offlink(struct nd_prefix *pr)
 static struct in6_ifaddr *
 in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp)
 {
-       struct in6_ifaddr *ia6;
-       struct ifnet *ifp;
-       struct nd_ifinfo *ndi;
+       struct in6_ifaddr *ia6 = NULL;
+       struct ifnet *ifp = NULL;
+       struct nd_ifinfo *ndi = NULL;
        struct in6_addr mask;
        struct in6_aliasreq ifra;
        int error, ifaupdate, iidlen, notcga;
@@ -3867,15 +3581,6 @@ in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp)
                goto unlock1;
        }
 
-       lck_rw_lock_shared(nd_if_rwlock);
-       if (ifp->if_index >= nd_ifinfo_indexlim) {
-               error = EADDRNOTAVAIL;
-               nd6log((LOG_INFO,
-                   "%s: invalid prefix length %d for %s, ignored\n",
-                   __func__, pr->ndpr_plen, if_name(ifp)));
-               goto unlock2;
-       }
-
        bzero(&ifra, sizeof (ifra));
        strlcpy(ifra.ifra_name, if_name(ifp), sizeof (ifra.ifra_name));
        ifra.ifra_addr.sin6_family = AF_INET6;
@@ -3890,7 +3595,7 @@ in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp)
        ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
        ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
 
-       ndi = &nd_ifinfo[ifp->if_index];
+       ndi = ND_IFINFO(ifp);
        VERIFY(ndi->initialized);
        lck_mtx_lock(&ndi->lock);
 
@@ -3898,7 +3603,6 @@ in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp)
            (ndi->flags & ND6_IFF_INSECURE) != 0;
 
        lck_mtx_unlock(&ndi->lock);
-       lck_rw_done(nd_if_rwlock);
        NDPR_UNLOCK(pr);
 
        if (notcga) {
@@ -3924,7 +3628,18 @@ in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp)
                ia6 = NULL;
        } else {
                in6_cga_node_lock();
-               error = in6_cga_generate(NULL, 0, &ifra.ifra_addr.sin6_addr);
+               struct in6_cga_prepare local_cga_prepare;
+
+               if (ndi->cga_initialized) {
+                       bcopy(&(ndi->local_cga_modifier),
+                           &(local_cga_prepare.cga_modifier),
+                           sizeof(local_cga_prepare.cga_modifier));
+                       error = in6_cga_generate(&local_cga_prepare, 0,
+                           &ifra.ifra_addr.sin6_addr);
+               } else {
+                       error = in6_cga_generate(NULL, 0,
+                           &ifra.ifra_addr.sin6_addr);
+               }
                in6_cga_node_unlock();
                if (error == 0)
                        ifra.ifra_flags |= IN6_IFF_SECURED;
@@ -3990,9 +3705,6 @@ in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp)
        in6_post_msg(ifp, KEV_INET6_NEW_RTADV_ADDR, ia6, NULL);
        goto done;
 
-unlock2:
-       lck_rw_done(nd_if_rwlock);
-
 unlock1:
        NDPR_UNLOCK(pr);
 
@@ -4327,8 +4039,12 @@ nd6_setdefaultiface(
                 * we do this here to avoid re-install the default route
                 * if the list is NOT empty.
                 */
-               if (ip6_doscopedroute || TAILQ_FIRST(&nd_defrouter) == NULL) {
-                       defrtrlist_sync(nd6_defifp);
+               if (odef_ifp != NULL) {
+                       defrouter_select(odef_ifp);
+               }
+
+               if (nd6_defifp != NULL) {
+                       defrouter_select(nd6_defifp);
                        nd6_prefix_sync(nd6_defifp);
                }
 
@@ -4340,6 +4056,5 @@ nd6_setdefaultiface(
                scope6_setdefault(nd6_defifp);
        }
        lck_mtx_unlock(nd6_mutex);
-
        return (error);
 }