summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/nd6_rtr.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2016-12-09 14:19:03 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-01-10 09:53:34 +0100
commit75b706fde4cbf82bcd41a1cec319778aa0f8eb2d (patch)
treeea39a351a1f6337b5a5dd6036314693adef5ffe6 /freebsd/sys/netinet6/nd6_rtr.c
parentVMSTAT(8): Port to RTEMS (diff)
downloadrtems-libbsd-75b706fde4cbf82bcd41a1cec319778aa0f8eb2d.tar.bz2
Update to FreeBSD head 2016-12-10
Git mirror commit 80c55f08a05ab3b26a73b226ccb56adc3122a55c.
Diffstat (limited to 'freebsd/sys/netinet6/nd6_rtr.c')
-rw-r--r--freebsd/sys/netinet6/nd6_rtr.c358
1 files changed, 229 insertions, 129 deletions
diff --git a/freebsd/sys/netinet6/nd6_rtr.c b/freebsd/sys/netinet6/nd6_rtr.c
index c8d7c0ef..33d8fa2a 100644
--- a/freebsd/sys/netinet6/nd6_rtr.c
+++ b/freebsd/sys/netinet6/nd6_rtr.c
@@ -78,20 +78,16 @@ static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *,
struct mbuf *, int);
static struct in6_ifaddr *in6_ifadd(struct nd_prefixctl *, int);
static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *,
- struct nd_defrouter *);
+ struct nd_defrouter *);
static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *);
static void pfxrtr_del(struct nd_pfxrouter *);
-static struct nd_pfxrouter *find_pfxlist_reachable_router
-(struct nd_prefix *);
+static struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *);
static void defrouter_delreq(struct nd_defrouter *);
static void nd6_rtmsg(int, struct rtentry *);
static int in6_init_prefix_ltimes(struct nd_prefix *);
static void in6_init_address_ltimes(struct nd_prefix *,
- struct in6_addrlifetime *);
-
-static int nd6_prefix_onlink(struct nd_prefix *);
-static int nd6_prefix_offlink(struct nd_prefix *);
+ struct in6_addrlifetime *);
static int rt6_deleteroute(const struct rtentry *, void *);
@@ -664,6 +660,7 @@ defrouter_unlink(struct nd_defrouter *dr, struct nd_drhead *drq)
ND6_WLOCK_ASSERT();
TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry);
+ V_nd6_list_genid++;
if (drq != NULL)
TAILQ_INSERT_TAIL(drq, dr, dr_entry);
}
@@ -673,6 +670,7 @@ defrouter_del(struct nd_defrouter *dr)
{
struct nd_defrouter *deldr = NULL;
struct nd_prefix *pr;
+ struct nd_pfxrouter *pfxrtr;
ND6_UNLOCK_ASSERT();
@@ -691,11 +689,13 @@ defrouter_del(struct nd_defrouter *dr)
/*
* Also delete all the pointers to the router in each prefix lists.
*/
+ ND6_WLOCK();
LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
- struct nd_pfxrouter *pfxrtr;
if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
pfxrtr_del(pfxrtr);
}
+ ND6_WUNLOCK();
+
pfxlist_onlink_check();
/*
@@ -855,14 +855,18 @@ static struct nd_defrouter *
defrtrlist_update(struct nd_defrouter *new)
{
struct nd_defrouter *dr, *n;
+ uint64_t genid;
int oldpref;
+ bool writelocked;
if (new->rtlifetime == 0) {
defrouter_remove(&new->rtaddr, new->ifp);
return (NULL);
}
- ND6_WLOCK();
+ ND6_RLOCK();
+ writelocked = false;
+restart:
dr = defrouter_lookup_locked(&new->rtaddr, new->ifp);
if (dr != NULL) {
oldpref = rtpref(dr);
@@ -878,10 +882,32 @@ defrtrlist_update(struct nd_defrouter *new)
* router is still installed in the kernel.
*/
if (dr->installed && rtpref(new) == oldpref) {
- ND6_WUNLOCK();
+ if (writelocked)
+ ND6_WUNLOCK();
+ else
+ ND6_RUNLOCK();
return (dr);
}
+ }
+
+ /*
+ * The router needs to be reinserted into the default router
+ * list, so upgrade to a write lock. If that fails and the list
+ * has potentially changed while the lock was dropped, we'll
+ * redo the lookup with the write lock held.
+ */
+ if (!writelocked) {
+ writelocked = true;
+ if (!ND6_TRY_UPGRADE()) {
+ genid = V_nd6_list_genid;
+ ND6_RUNLOCK();
+ ND6_WLOCK();
+ if (genid != V_nd6_list_genid)
+ goto restart;
+ }
+ }
+ if (dr != NULL) {
/*
* The preferred router may have changed, so relocate this
* router.
@@ -915,6 +941,7 @@ defrtrlist_update(struct nd_defrouter *new)
TAILQ_INSERT_BEFORE(dr, n, dr_entry);
else
TAILQ_INSERT_TAIL(&V_nd_defrouter, n, dr_entry);
+ V_nd6_list_genid++;
ND6_WUNLOCK();
defrouter_select();
@@ -927,11 +954,12 @@ pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr)
{
struct nd_pfxrouter *search;
+ ND6_LOCK_ASSERT();
+
LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) {
if (search->router == dr)
break;
}
-
return (search);
}
@@ -939,55 +967,110 @@ static void
pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr)
{
struct nd_pfxrouter *new;
+ bool update;
+
+ ND6_UNLOCK_ASSERT();
+
+ ND6_RLOCK();
+ if (pfxrtr_lookup(pr, dr) != NULL) {
+ ND6_RUNLOCK();
+ return;
+ }
+ ND6_RUNLOCK();
new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO);
if (new == NULL)
return;
- new->router = dr;
defrouter_ref(dr);
+ new->router = dr;
- LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
+ ND6_WLOCK();
+ if (pfxrtr_lookup(pr, dr) == NULL) {
+ LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
+ update = true;
+ } else {
+ /* We lost a race to add the reference. */
+ defrouter_rele(dr);
+ free(new, M_IP6NDP);
+ update = false;
+ }
+ ND6_WUNLOCK();
- pfxlist_onlink_check();
+ if (update)
+ pfxlist_onlink_check();
}
static void
pfxrtr_del(struct nd_pfxrouter *pfr)
{
+ ND6_WLOCK_ASSERT();
+
LIST_REMOVE(pfr, pfr_entry);
defrouter_rele(pfr->router);
free(pfr, M_IP6NDP);
}
-struct nd_prefix *
-nd6_prefix_lookup(struct nd_prefixctl *key)
+static struct nd_prefix *
+nd6_prefix_lookup_locked(struct nd_prefixctl *key)
{
struct nd_prefix *search;
+ ND6_LOCK_ASSERT();
+
LIST_FOREACH(search, &V_nd_prefix, ndpr_entry) {
if (key->ndpr_ifp == search->ndpr_ifp &&
key->ndpr_plen == search->ndpr_plen &&
in6_are_prefix_equal(&key->ndpr_prefix.sin6_addr,
&search->ndpr_prefix.sin6_addr, key->ndpr_plen)) {
+ nd6_prefix_ref(search);
break;
}
}
+ return (search);
+}
+
+struct nd_prefix *
+nd6_prefix_lookup(struct nd_prefixctl *key)
+{
+ struct nd_prefix *search;
+ ND6_RLOCK();
+ search = nd6_prefix_lookup_locked(key);
+ ND6_RUNLOCK();
return (search);
}
+void
+nd6_prefix_ref(struct nd_prefix *pr)
+{
+
+ refcount_acquire(&pr->ndpr_refcnt);
+}
+
+void
+nd6_prefix_rele(struct nd_prefix *pr)
+{
+
+ if (refcount_release(&pr->ndpr_refcnt)) {
+ KASSERT(LIST_EMPTY(&pr->ndpr_advrtrs),
+ ("prefix %p has advertising routers", pr));
+ free(pr, M_IP6NDP);
+ }
+}
+
int
nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr,
struct nd_prefix **newp)
{
- struct nd_prefix *new = NULL;
- int error = 0;
+ struct nd_prefix *new;
char ip6buf[INET6_ADDRSTRLEN];
+ int error;
new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO);
if (new == NULL)
return (ENOMEM);
+ refcount_init(&new->ndpr_refcnt, newp != NULL ? 2 : 1);
new->ndpr_ifp = pr->ndpr_ifp;
new->ndpr_prefix = pr->ndpr_prefix;
new->ndpr_plen = pr->ndpr_plen;
@@ -1006,20 +1089,22 @@ nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr,
/* make prefix in the canonical form */
IN6_MASK_ADDR(&new->ndpr_prefix.sin6_addr, &new->ndpr_mask);
- /* link ndpr_entry to nd_prefix list */
+ ND6_WLOCK();
LIST_INSERT_HEAD(&V_nd_prefix, new, ndpr_entry);
+ V_nd6_list_genid++;
+ ND6_WUNLOCK();
/* ND_OPT_PI_FLAG_ONLINK processing */
if (new->ndpr_raf_onlink) {
- int e;
-
- if ((e = nd6_prefix_onlink(new)) != 0) {
+ ND6_ONLINK_LOCK();
+ if ((error = nd6_prefix_onlink(new)) != 0) {
nd6log((LOG_ERR, "nd6_prelist_add: failed to make "
"the prefix %s/%d on-link on %s (errno=%d)\n",
ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
- pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
+ pr->ndpr_plen, if_name(pr->ndpr_ifp), error));
/* proceed anyway. XXX: is it correct? */
}
+ ND6_ONLINK_UNLOCK();
}
if (dr != NULL)
@@ -1029,51 +1114,69 @@ nd6_prelist_add(struct nd_prefixctl *pr, struct nd_defrouter *dr,
return (0);
}
+/*
+ * Remove a prefix from the prefix list and optionally stash it in a
+ * caller-provided list.
+ *
+ * The ND6 lock must be held.
+ */
void
-prelist_remove(struct nd_prefix *pr)
+nd6_prefix_unlink(struct nd_prefix *pr, struct nd_prhead *list)
+{
+
+ KASSERT(pr->ndpr_addrcnt == 0,
+ ("prefix %p has referencing addresses", pr));
+ ND6_WLOCK_ASSERT();
+
+ LIST_REMOVE(pr, ndpr_entry);
+ V_nd6_list_genid++;
+ if (list != NULL)
+ LIST_INSERT_HEAD(list, pr, ndpr_entry);
+}
+
+/*
+ * Free an unlinked prefix, first marking it off-link if necessary.
+ */
+void
+nd6_prefix_del(struct nd_prefix *pr)
{
struct nd_pfxrouter *pfr, *next;
int e;
char ip6buf[INET6_ADDRSTRLEN];
- /* make sure to invalidate the prefix until it is really freed. */
- pr->ndpr_vltime = 0;
- pr->ndpr_pltime = 0;
+ KASSERT(pr->ndpr_addrcnt == 0,
+ ("prefix %p has referencing addresses", pr));
+ ND6_UNLOCK_ASSERT();
/*
* Though these flags are now meaningless, we'd rather keep the value
* of pr->ndpr_raf_onlink and pr->ndpr_raf_auto not to confuse users
* when executing "ndp -p".
*/
-
- if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 &&
- (e = nd6_prefix_offlink(pr)) != 0) {
- nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink "
- "on %s, errno=%d\n",
- ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
- pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
- /* what should we do? */
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
+ ND6_ONLINK_LOCK();
+ if ((e = nd6_prefix_offlink(pr)) != 0) {
+ nd6log((LOG_ERR,
+ "nd6_prefix_del: failed to make %s/%d offlink "
+ "on %s, errno=%d\n",
+ ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
+ /* what should we do? */
+ }
+ ND6_ONLINK_UNLOCK();
}
- if (pr->ndpr_refcnt > 0)
- return; /* notice here? */
-
- /* unlink ndpr_entry from nd_prefix list */
- LIST_REMOVE(pr, ndpr_entry);
-
- /* free list of routers that advertised the prefix */
- LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next) {
+ /* Release references to routers that have advertised this prefix. */
+ ND6_WLOCK();
+ LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next)
pfxrtr_del(pfr);
- }
- free(pr, M_IP6NDP);
+ ND6_WUNLOCK();
+
+ nd6_prefix_rele(pr);
pfxlist_onlink_check();
}
-/*
- * dr - may be NULL
- */
-
static int
prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
struct mbuf *m, int mcast)
@@ -1123,21 +1226,22 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
if (new->ndpr_raf_onlink &&
(pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
- int e;
-
- if ((e = nd6_prefix_onlink(pr)) != 0) {
+ ND6_ONLINK_LOCK();
+ if ((error = nd6_prefix_onlink(pr)) != 0) {
nd6log((LOG_ERR,
"prelist_update: failed to make "
"the prefix %s/%d on-link on %s "
"(errno=%d)\n",
ip6_sprintf(ip6buf,
- &pr->ndpr_prefix.sin6_addr),
- pr->ndpr_plen, if_name(pr->ndpr_ifp), e));
+ &pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, if_name(pr->ndpr_ifp),
+ error));
/* proceed anyway. XXX: is it correct? */
}
+ ND6_ONLINK_UNLOCK();
}
- if (dr && pfxrtr_lookup(pr, dr) == NULL)
+ if (dr != NULL)
pfxrtr_add(pr, dr);
} else {
if (new->ndpr_vltime == 0)
@@ -1358,7 +1462,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
/*
* note that we should use pr (not new) for reference.
*/
- pr->ndpr_refcnt++;
+ pr->ndpr_addrcnt++;
ia6->ia6_ndpr = pr;
/*
@@ -1396,8 +1500,10 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
}
}
- end:
- return error;
+end:
+ if (pr != NULL)
+ nd6_prefix_rele(pr);
+ return (error);
}
/*
@@ -1412,6 +1518,8 @@ find_pfxlist_reachable_router(struct nd_prefix *pr)
struct llentry *ln;
int canreach;
+ ND6_LOCK_ASSERT();
+
LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) {
IF_AFDATA_RLOCK(pfxrtr->router->ifp);
ln = nd6_lookup(&pfxrtr->router->rtaddr, 0, pfxrtr->router->ifp);
@@ -1434,7 +1542,7 @@ find_pfxlist_reachable_router(struct nd_prefix *pr)
* we have moved from the network but the lifetime of the prefix has not
* expired yet. So we should not use the prefix if there is another prefix
* that has an available router.
- * But, if there is no prefix that has an available router, we still regards
+ * But, if there is no prefix that has an available router, we still regard
* all the prefixes as on-link. This is because we can't tell if all the
* routers are simply dead or if we really moved from the network and there
* is no router around us.
@@ -1447,6 +1555,11 @@ pfxlist_onlink_check(void)
struct nd_defrouter *dr;
struct nd_pfxrouter *pfxrtr = NULL;
struct rm_priotracker in6_ifa_tracker;
+ uint64_t genid;
+ uint32_t flags;
+
+ ND6_ONLINK_LOCK();
+ ND6_RLOCK();
/*
* Check if there is a prefix that has a reachable advertising
@@ -1462,7 +1575,6 @@ pfxlist_onlink_check(void)
* that does not advertise any prefixes.
*/
if (pr == NULL) {
- ND6_RLOCK();
TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
struct nd_prefix *pr0;
@@ -1473,7 +1585,6 @@ pfxlist_onlink_check(void)
if (pfxrtr != NULL)
break;
}
- ND6_RUNLOCK();
}
if (pr != NULL || (!TAILQ_EMPTY(&V_nd_defrouter) && pfxrtr == NULL)) {
/*
@@ -1487,40 +1598,26 @@ pfxlist_onlink_check(void)
*/
LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
/* XXX: a link-local prefix should never be detached */
- if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
- continue;
-
- /*
- * we aren't interested in prefixes without the L bit
- * set.
- */
- if (pr->ndpr_raf_onlink == 0)
- continue;
-
- if (pr->ndpr_raf_auto == 0)
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
+ pr->ndpr_raf_onlink == 0 ||
+ pr->ndpr_raf_auto == 0)
continue;
if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
find_pfxlist_reachable_router(pr) == NULL)
pr->ndpr_stateflags |= NDPRF_DETACHED;
- if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
+ else if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
find_pfxlist_reachable_router(pr) != NULL)
pr->ndpr_stateflags &= ~NDPRF_DETACHED;
}
} else {
/* there is no prefix that has a reachable router */
LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
- if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
- continue;
-
- if (pr->ndpr_raf_onlink == 0)
- continue;
-
- if (pr->ndpr_raf_auto == 0)
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
+ pr->ndpr_raf_onlink == 0 ||
+ pr->ndpr_raf_auto == 0)
continue;
-
- if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0)
- pr->ndpr_stateflags &= ~NDPRF_DETACHED;
+ pr->ndpr_stateflags &= ~NDPRF_DETACHED;
}
}
@@ -1532,34 +1629,30 @@ pfxlist_onlink_check(void)
* interfaces. Such cases will be handled in nd6_prefix_onlink,
* so we don't have to care about them.
*/
+restart:
LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
- int e;
char ip6buf[INET6_ADDRSTRLEN];
+ int e;
- if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
- continue;
-
- if (pr->ndpr_raf_onlink == 0)
- continue;
-
- if (pr->ndpr_raf_auto == 0)
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
+ pr->ndpr_raf_onlink == 0 ||
+ pr->ndpr_raf_auto == 0)
continue;
- if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
- (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
- if ((e = nd6_prefix_offlink(pr)) != 0) {
+ flags = pr->ndpr_stateflags & (NDPRF_DETACHED | NDPRF_ONLINK);
+ if (flags == 0 || flags == (NDPRF_DETACHED | NDPRF_ONLINK)) {
+ genid = V_nd6_list_genid;
+ ND6_RUNLOCK();
+ if ((flags & NDPRF_ONLINK) != 0 &&
+ (e = nd6_prefix_offlink(pr)) != 0) {
nd6log((LOG_ERR,
"pfxlist_onlink_check: failed to "
"make %s/%d offlink, errno=%d\n",
ip6_sprintf(ip6buf,
&pr->ndpr_prefix.sin6_addr),
pr->ndpr_plen, e));
- }
- }
- if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
- (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 &&
- pr->ndpr_raf_onlink) {
- if ((e = nd6_prefix_onlink(pr)) != 0) {
+ } else if ((flags & NDPRF_ONLINK) == 0 &&
+ (e = nd6_prefix_onlink(pr)) != 0) {
nd6log((LOG_ERR,
"pfxlist_onlink_check: failed to "
"make %s/%d onlink, errno=%d\n",
@@ -1567,6 +1660,9 @@ pfxlist_onlink_check(void)
&pr->ndpr_prefix.sin6_addr),
pr->ndpr_plen, e));
}
+ ND6_RLOCK();
+ if (genid != V_nd6_list_genid)
+ goto restart;
}
}
@@ -1627,6 +1723,8 @@ pfxlist_onlink_check(void)
}
}
IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
+ ND6_RUNLOCK();
+ ND6_ONLINK_UNLOCK();
}
static int
@@ -1707,23 +1805,20 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
return (a_failure);
}
-static int
+int
nd6_prefix_onlink(struct nd_prefix *pr)
{
struct ifaddr *ifa;
struct ifnet *ifp = pr->ndpr_ifp;
struct nd_prefix *opr;
- int error = 0;
char ip6buf[INET6_ADDRSTRLEN];
+ int error;
- /* sanity check */
- if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
- nd6log((LOG_ERR,
- "nd6_prefix_onlink: %s/%d is already on-link\n",
- ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
- pr->ndpr_plen));
+ ND6_ONLINK_LOCK_ASSERT();
+ ND6_UNLOCK_ASSERT();
+
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0)
return (EEXIST);
- }
/*
* Add the interface route associated with the prefix. Before
@@ -1732,6 +1827,7 @@ nd6_prefix_onlink(struct nd_prefix *pr)
* Although such a configuration is expected to be rare, we explicitly
* allow it.
*/
+ ND6_RLOCK();
LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) {
if (opr == pr)
continue;
@@ -1741,9 +1837,12 @@ nd6_prefix_onlink(struct nd_prefix *pr)
if (opr->ndpr_plen == pr->ndpr_plen &&
in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
- &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen))
+ &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
+ ND6_RUNLOCK();
return (0);
+ }
}
+ ND6_RUNLOCK();
/*
* We prefer link-local addresses as the associated interface address.
@@ -1755,11 +1854,11 @@ nd6_prefix_onlink(struct nd_prefix *pr)
/* XXX: freebsd does not have ifa_ifwithaf */
IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
- if (ifa->ifa_addr->sa_family == AF_INET6)
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ ifa_ref(ifa);
break;
+ }
}
- if (ifa != NULL)
- ifa_ref(ifa);
IF_ADDR_RUNLOCK(ifp);
/* should we care about ia6_flags? */
}
@@ -1786,7 +1885,7 @@ nd6_prefix_onlink(struct nd_prefix *pr)
return (error);
}
-static int
+int
nd6_prefix_offlink(struct nd_prefix *pr)
{
int error = 0;
@@ -1795,16 +1894,14 @@ nd6_prefix_offlink(struct nd_prefix *pr)
struct sockaddr_in6 sa6, mask6;
struct rtentry *rt;
char ip6buf[INET6_ADDRSTRLEN];
+ uint64_t genid;
int fibnum, a_failure;
- /* sanity check */
- if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
- nd6log((LOG_ERR,
- "nd6_prefix_offlink: %s/%d is already off-link\n",
- ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr),
- pr->ndpr_plen));
+ ND6_ONLINK_LOCK_ASSERT();
+ ND6_UNLOCK_ASSERT();
+
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0)
return (EEXIST);
- }
bzero(&sa6, sizeof(sa6));
sa6.sin6_family = AF_INET6;
@@ -1845,18 +1942,15 @@ nd6_prefix_offlink(struct nd_prefix *pr)
* If there's one, try to make the prefix on-link on the
* interface.
*/
+ ND6_RLOCK();
+restart:
LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) {
- if (opr == pr)
- continue;
-
- if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0)
- continue;
-
/*
* KAME specific: detached prefixes should not be
* on-link.
*/
- if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0)
+ if (opr == pr || (opr->ndpr_stateflags &
+ (NDPRF_ONLINK | NDPRF_DETACHED)) != 0)
continue;
if (opr->ndpr_plen == pr->ndpr_plen &&
@@ -1864,6 +1958,8 @@ nd6_prefix_offlink(struct nd_prefix *pr)
&opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
int e;
+ genid = V_nd6_list_genid;
+ ND6_RUNLOCK();
if ((e = nd6_prefix_onlink(opr)) != 0) {
nd6log((LOG_ERR,
"nd6_prefix_offlink: failed to "
@@ -1875,8 +1971,12 @@ nd6_prefix_offlink(struct nd_prefix *pr)
if_name(opr->ndpr_ifp), e));
} else
a_failure = 0;
+ ND6_RLOCK();
+ if (genid != V_nd6_list_genid)
+ goto restart;
}
}
+ ND6_RUNLOCK();
} else {
/* XXX: can we still set the NDPRF_ONLINK flag? */
nd6log((LOG_ERR,
@@ -2116,7 +2216,7 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay)
return (EINVAL); /* XXX */
}
newia->ia6_ndpr = ia0->ia6_ndpr;
- newia->ia6_ndpr->ndpr_refcnt++;
+ newia->ia6_ndpr->ndpr_addrcnt++;
ifa_free(&newia->ia_ifa);
/*