/*
- * Copyright (c) 2003-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#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>
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 *);
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 */
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;
}
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);
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;
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",
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) {
ndi->linkmtu = mtu;
lck_mtx_unlock(&ndi->lock);
- lck_rw_done(nd_if_rwlock);
if (change) /* in6_maxmtu may change */
in6_setmaxmtu();
} else {
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:
goto bad;
}
+ if (dr && dr->stateflags & NDDRF_MAPPED)
+ saddr6 = dr->rtaddr_mapped;
+
nd6_cache_lladdr(ifp, &saddr6, lladdr, (int)lladdrlen,
ND_ROUTER_ADVERT, 0);
/* 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;
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);
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__,
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) ?
ifscope = IFSCOPE_NONE;
}
NDDR_UNLOCK(dr);
+
err = rtrequest_scoped(RTM_DELETE,
(struct sockaddr *)&def, (struct sockaddr *)&gate,
(struct sockaddr *)&mask, RTF_GATEWAY, &oldrt, ifscope);
}
/* 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
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 */
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)));
/*
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
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);
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;
}
* 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
* 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);
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;
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;
* 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.
*/
TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
new->stateflags = dr->stateflags;
- new->stateflags &= ~NDDRF_PROCESSED;
n = dr;
goto insert;
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);
}
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)));
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:
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)
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)
{
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;
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;
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)
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);
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;
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);
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);
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) {
#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.
NDPR_REMREF(pr);
lck_mtx_unlock(nd6_mutex);
} else {
- struct nd_prefix *newpr = NULL;
-
newprefix = 1;
if (new->ndpr_vltime == 0)
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);
}
/*
/* 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
}
}
-afteraddrconf:
-
end:
if (pr != NULL)
NDPR_REMREF(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 */
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 {
}
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);
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) {
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
*/
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 */
}
lck_mtx_unlock(&ndi->lock);
- lck_rw_done(nd_if_rwlock);
} else if (rt != NULL && pr->ndpr_stateflags & NDPRF_DEFUNCT)
rtfree(rt);
{
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;
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: "
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;
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;
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);
(ndi->flags & ND6_IFF_INSECURE) != 0;
lck_mtx_unlock(&ndi->lock);
- lck_rw_done(nd_if_rwlock);
NDPR_UNLOCK(pr);
if (notcga) {
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;
in6_post_msg(ifp, KEV_INET6_NEW_RTADV_ADDR, ia6, NULL);
goto done;
-unlock2:
- lck_rw_done(nd_if_rwlock);
-
unlock1:
NDPR_UNLOCK(pr);
* 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);
}
scope6_setdefault(nd6_defifp);
}
lck_mtx_unlock(nd6_mutex);
-
return (error);
}