Module Name: src
Committed By: ozaki-r
Date: Mon Dec 19 07:51:34 UTC 2016
Modified Files:
src/sys/netinet6: in6.c nd6.c nd6.h nd6_nbr.c nd6_rtr.c
Log Message:
Protect IPv6 default router and prefix lists with coarse-grained rwlock
in6_purgeaddr (in6_unlink_ifa) itself unrefernces a prefix entry and calls
nd6_prelist_remove if the counter becomes 0, so callers doesn't need to
handle the reference counting.
Performance-sensitive paths (sending/forwarding packets) call just one
reader lock. This is a trade-off between performance impact vs. the amount
of efforts; if we want to remove the reader lock, we need huge amount of
works including destroying objects with psz/psref in softint, for example.
To generate a diff of this commit:
cvs rdiff -u -r1.224 -r1.225 src/sys/netinet6/in6.c
cvs rdiff -u -r1.219 -r1.220 src/sys/netinet6/nd6.c
cvs rdiff -u -r1.80 -r1.81 src/sys/netinet6/nd6.h
cvs rdiff -u -r1.133 -r1.134 src/sys/netinet6/nd6_nbr.c
cvs rdiff -u -r1.127 -r1.128 src/sys/netinet6/nd6_rtr.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/netinet6/in6.c
diff -u src/sys/netinet6/in6.c:1.224 src/sys/netinet6/in6.c:1.225
--- src/sys/netinet6/in6.c:1.224 Mon Dec 12 03:55:57 2016
+++ src/sys/netinet6/in6.c Mon Dec 19 07:51:34 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: in6.c,v 1.224 2016/12/12 03:55:57 ozaki-r Exp $ */
+/* $NetBSD: in6.c,v 1.225 2016/12/19 07:51:34 ozaki-r Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.224 2016/12/12 03:55:57 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.225 2016/12/19 07:51:34 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -709,23 +709,11 @@ in6_control1(struct socket *so, u_long c
}
case SIOCDIFADDR_IN6:
- {
- struct nd_prefix *pr;
-
- /*
- * If the address being deleted is the only one that owns
- * the corresponding prefix, expire the prefix as well.
- * Note that in6_purgeaddr() will decrement ndpr_refcnt.
- */
- pr = ia->ia6_ndpr;
ia6_release(ia, &psref);
in6_purgeaddr(&ia->ia_ifa);
ia = NULL;
- if (pr && pr->ndpr_refcnt == 0)
- nd6_prelist_remove(pr);
run_hooks = true;
break;
- }
default:
error = ENOTTY;
@@ -1388,7 +1376,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, st
* Release the reference to the ND prefix.
*/
if (ia->ia6_ndpr != NULL) {
- ia->ia6_ndpr->ndpr_refcnt--;
+ nd6_prefix_unref(ia->ia6_ndpr);
ia->ia6_ndpr = NULL;
}
@@ -1397,8 +1385,11 @@ in6_unlink_ifa(struct in6_ifaddr *ia, st
* nd6_pfxlist_onlink_check() since the release might affect the status of
* other (detached) addresses.
*/
- if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0)
+ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0) {
+ ND6_WLOCK();
nd6_pfxlist_onlink_check();
+ ND6_UNLOCK();
+ }
IN6_ADDRLIST_ENTRY_DESTROY(ia);
@@ -2153,7 +2144,9 @@ in6_if_link_up(struct ifnet *ifp)
curlwp_bindx(bound);
/* Restore any detached prefixes */
+ ND6_WLOCK();
nd6_pfxlist_onlink_check();
+ ND6_UNLOCK();
}
void
@@ -2180,7 +2173,9 @@ in6_if_link_down(struct ifnet *ifp)
int s, bound;
/* Any prefixes on this interface should be detached as well */
+ ND6_WLOCK();
nd6_pfxlist_onlink_check();
+ ND6_UNLOCK();
bound = curlwp_bind();
s = pserialize_read_enter();
Index: src/sys/netinet6/nd6.c
diff -u src/sys/netinet6/nd6.c:1.219 src/sys/netinet6/nd6.c:1.220
--- src/sys/netinet6/nd6.c:1.219 Mon Dec 19 04:52:17 2016
+++ src/sys/netinet6/nd6.c Mon Dec 19 07:51:34 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6.c,v 1.219 2016/12/19 04:52:17 ozaki-r Exp $ */
+/* $NetBSD: nd6.c,v 1.220 2016/12/19 07:51:34 ozaki-r Exp $ */
/* $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.219 2016/12/19 04:52:17 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.220 2016/12/19 07:51:34 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_net_mpsafe.h"
@@ -103,6 +103,8 @@ int nd6_debug = 1;
int nd6_debug = 0;
#endif
+krwlock_t nd6_lock __cacheline_aligned;
+
struct nd_drhead nd_defrouter;
struct nd_prhead nd_prefix = { 0 };
@@ -138,6 +140,8 @@ nd6_init(void)
{
int error;
+ rw_init(&nd6_lock);
+
/* initialization of the default router list */
ND_DEFROUTER_LIST_INIT();
@@ -595,12 +599,14 @@ nd6_timer_work(struct work *wk, void *ar
#endif
/* expire default router list */
-
+
+ ND6_WLOCK();
ND_DEFROUTER_LIST_FOREACH_SAFE(dr, next_dr) {
if (dr->expire && dr->expire < time_uptime) {
nd6_defrtrlist_del(dr, NULL);
}
}
+ ND6_UNLOCK();
/*
* expire interface addresses.
@@ -695,6 +701,7 @@ nd6_timer_work(struct work *wk, void *ar
curlwp_bindx(bound);
/* expire prefix list */
+ ND6_WLOCK();
ND_PREFIX_LIST_FOREACH_SAFE(pr, next_pr) {
/*
* check prefix lifetime.
@@ -712,6 +719,7 @@ nd6_timer_work(struct work *wk, void *ar
nd6_prelist_remove(pr);
}
}
+ ND6_UNLOCK();
#ifndef NET_MPSAFE
KERNEL_UNLOCK_ONE(NULL);
@@ -786,7 +794,10 @@ regen_tmpaddr(const struct in6_ifaddr *i
* Random factor is introduced in the preferred lifetime, so
* we do not need additional delay (3rd arg to in6_tmpifadd).
*/
- if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) {
+ ND6_WLOCK();
+ e = in6_tmpifadd(public_ifa6, 0, 0);
+ ND6_UNLOCK();
+ if (e != 0) {
ia6_release(public_ifa6, &psref);
log(LOG_NOTICE, "regen_tmpaddr: failed to create a new"
" tmp addr, errno=%d\n", e);
@@ -835,6 +846,7 @@ nd6_purge(struct ifnet *ifp, struct in6_
if (ext == NULL)
return;
+ ND6_WLOCK();
/*
* Nuke default router list entries toward ifp.
* We defer removal of default router list entries that is installed
@@ -884,6 +896,7 @@ nd6_purge(struct ifnet *ifp, struct in6_
nd6_defrouter_select();
}
}
+ ND6_UNLOCK();
/*
* We may not need to nuke the neighbor cache entries here
@@ -903,6 +916,7 @@ nd6_assert_purged(struct ifnet *ifp)
struct nd_defrouter *dr;
struct nd_prefix *pr;
+ ND6_RLOCK();
ND_DEFROUTER_LIST_FOREACH(dr) {
KASSERTMSG(dr->ifp != ifp,
"defrouter %s remains on %s",
@@ -915,6 +929,7 @@ nd6_assert_purged(struct ifnet *ifp)
ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
pr->ndpr_plen, ifp->if_xname);
}
+ ND6_UNLOCK();
}
struct llentry *
@@ -993,6 +1008,7 @@ nd6_is_new_addr_neighbor(const struct so
* If the address matches one of our on-link prefixes, it should be a
* neighbor.
*/
+ ND6_RLOCK();
ND_PREFIX_LIST_FOREACH(pr) {
if (pr->ndpr_ifp != ifp)
continue;
@@ -1022,9 +1038,12 @@ nd6_is_new_addr_neighbor(const struct so
}
if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
- &addr->sin6_addr, &pr->ndpr_mask))
+ &addr->sin6_addr, &pr->ndpr_mask)) {
+ ND6_UNLOCK();
return 1;
+ }
}
+ ND6_UNLOCK();
/*
* If the address is assigned on the node of the other side of
@@ -1044,11 +1063,13 @@ nd6_is_new_addr_neighbor(const struct so
* If the default router list is empty, all addresses are regarded
* as on-link, and thus, as a neighbor.
*/
+ ND6_RLOCK();
if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV &&
- ND_DEFROUTER_LIST_EMPTY() &&
- nd6_defifindex == ifp->if_index) {
+ ND_DEFROUTER_LIST_EMPTY() && nd6_defifindex == ifp->if_index) {
+ ND6_UNLOCK();
return 1;
}
+ ND6_UNLOCK();
return 0;
}
@@ -1091,6 +1112,7 @@ nd6_is_addr_neighbor(const struct sockad
* If the address matches one of our on-link prefixes, it should be a
* neighbor.
*/
+ ND6_RLOCK();
ND_PREFIX_LIST_FOREACH(pr) {
if (pr->ndpr_ifp != ifp)
continue;
@@ -1099,8 +1121,10 @@ nd6_is_addr_neighbor(const struct sockad
continue;
if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
- &addr->sin6_addr, &pr->ndpr_mask))
+ &addr->sin6_addr, &pr->ndpr_mask)) {
+ ND6_UNLOCK();
return 1;
+ }
}
/*
@@ -1111,8 +1135,10 @@ nd6_is_addr_neighbor(const struct sockad
*/
if (!ip6_forwarding && ND_DEFROUTER_LIST_EMPTY() &&
nd6_defifindex == ifp->if_index) {
+ ND6_UNLOCK();
return 1;
}
+ ND6_UNLOCK();
IF_AFDATA_UNLOCK_ASSERT(ifp);
if (nd6_is_new_addr_neighbor(addr, ifp))
@@ -1178,8 +1204,7 @@ nd6_free(struct llentry *ln, int gc)
nd6_llinfo_settimer(ln, -1);
if (!ip6_forwarding) {
- int s;
- s = splsoftnet();
+ ND6_WLOCK();
dr = nd6_defrouter_lookup(in6, ifp);
if (dr != NULL && dr->expire &&
@@ -1201,7 +1226,7 @@ nd6_free(struct llentry *ln, int gc)
(dr->expire - time_uptime) * hz);
else
nd6_llinfo_settimer(ln, nd6_gctimer * hz);
- splx(s);
+ ND6_UNLOCK();
LLE_WUNLOCK(ln);
return;
}
@@ -1262,11 +1287,10 @@ nd6_free(struct llentry *ln, int gc)
if (ln->la_flags & LLE_REDIRECT)
nd6_free_redirect(ln);
#endif
+ ND6_UNLOCK();
if (ln->ln_router || dr)
LLE_WLOCK(ln);
-
- splx(s);
}
/*
@@ -1620,7 +1644,6 @@ nd6_ioctl(u_long cmd, void *data, struct
struct nd_defrouter *dr;
struct nd_prefix *pr;
int i = 0, error = 0;
- int s;
switch (cmd) {
case SIOCGDRLST_IN6:
@@ -1628,7 +1651,7 @@ nd6_ioctl(u_long cmd, void *data, struct
* obsolete API, use sysctl under net.inet6.icmp6
*/
memset(drl, 0, sizeof(*drl));
- s = splsoftnet();
+ ND6_RLOCK();
ND_DEFROUTER_LIST_FOREACH(dr) {
if (i >= DRLSTSIZ)
break;
@@ -1642,7 +1665,7 @@ nd6_ioctl(u_long cmd, void *data, struct
drl->defrouter[i].if_index = dr->ifp->if_index;
i++;
}
- splx(s);
+ ND6_UNLOCK();
break;
case SIOCGPRLST_IN6:
/*
@@ -1658,7 +1681,7 @@ nd6_ioctl(u_long cmd, void *data, struct
* how about separating ioctls into two?
*/
memset(oprl, 0, sizeof(*oprl));
- s = splsoftnet();
+ ND6_RLOCK();
ND_PREFIX_LIST_FOREACH(pr) {
struct nd_pfxrouter *pfr;
int j;
@@ -1706,7 +1729,7 @@ nd6_ioctl(u_long cmd, void *data, struct
i++;
}
- splx(s);
+ ND6_UNLOCK();
break;
case OSIOCGIFINFO_IN6:
@@ -1757,6 +1780,7 @@ nd6_ioctl(u_long cmd, void *data, struct
{
struct ifaddr *ifa;
struct in6_ifaddr *ia;
+ int s;
if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
!(ND.flags & ND6_IFF_IFDISABLED))
@@ -1857,15 +1881,18 @@ nd6_ioctl(u_long cmd, void *data, struct
#undef ND
case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */
/* sync kernel routing table with the default router list */
+ ND6_WLOCK();
nd6_defrouter_reset();
nd6_defrouter_select();
+ ND6_UNLOCK();
break;
case SIOCSPFXFLUSH_IN6:
{
/* flush all the prefix advertised by routers */
struct nd_prefix *pfx, *next;
- s = splsoftnet();
+ restart:
+ ND6_WLOCK();
ND_PREFIX_LIST_FOREACH_SAFE(pfx, next) {
struct in6_ifaddr *ia, *ia_next;
int _s;
@@ -1874,7 +1901,6 @@ nd6_ioctl(u_long cmd, void *data, struct
continue; /* XXX */
/* do we really have to remove addresses as well? */
- restart:
_s = pserialize_read_enter();
for (ia = IN6_ADDRLIST_READER_FIRST(); ia;
ia = ia_next) {
@@ -1886,15 +1912,19 @@ nd6_ioctl(u_long cmd, void *data, struct
if (ia->ia6_ndpr == pfx) {
pserialize_read_exit(_s);
+ ND6_UNLOCK();
/* XXX NOMPSAFE? */
+ /* in6_purgeaddr may destroy pfx. */
in6_purgeaddr(&ia->ia_ifa);
goto restart;
}
}
pserialize_read_exit(_s);
+
+ KASSERT(pfx->ndpr_refcnt == 0);
nd6_prelist_remove(pfx);
}
- splx(s);
+ ND6_UNLOCK();
break;
}
case SIOCSRTRFLUSH_IN6:
@@ -1902,13 +1932,13 @@ nd6_ioctl(u_long cmd, void *data, struct
/* flush all the default routers */
struct nd_defrouter *drtr, *next;
- s = splsoftnet();
+ ND6_WLOCK();
nd6_defrouter_reset();
ND_DEFROUTER_LIST_FOREACH_SAFE(drtr, next) {
nd6_defrtrlist_del(drtr, NULL);
}
nd6_defrouter_select();
- splx(s);
+ ND6_UNLOCK();
break;
}
case SIOCGNBRINFO_IN6:
@@ -2193,8 +2223,11 @@ nd6_cache_lladdr(
* cases for safety.
*/
if (do_update && router && !ip6_forwarding &&
- nd6_accepts_rtadv(ndi))
+ nd6_accepts_rtadv(ndi)) {
+ ND6_WLOCK();
nd6_defrouter_select();
+ ND6_UNLOCK();
+ }
}
static void
@@ -2643,19 +2676,18 @@ nd6_sysctl(
static int
fill_drlist(void *oldp, size_t *oldlenp, size_t ol)
{
- int error = 0, s;
+ int error = 0;
struct in6_defrouter *d = NULL, *de = NULL;
struct nd_defrouter *dr;
size_t l;
- s = splsoftnet();
-
if (oldp) {
d = (struct in6_defrouter *)oldp;
de = (struct in6_defrouter *)((char *)oldp + *oldlenp);
}
l = 0;
+ ND6_RLOCK();
ND_DEFROUTER_LIST_FOREACH(dr) {
if (oldp && d + 1 <= de) {
@@ -2678,6 +2710,7 @@ fill_drlist(void *oldp, size_t *oldlenp,
if (d)
d++;
}
+ ND6_UNLOCK();
if (oldp) {
if (l > ol)
@@ -2686,28 +2719,25 @@ fill_drlist(void *oldp, size_t *oldlenp,
if (oldlenp)
*oldlenp = l; /* (void *)d - (void *)oldp */
- splx(s);
-
return error;
}
static int
fill_prlist(void *oldp, size_t *oldlenp, size_t ol)
{
- int error = 0, s;
+ int error = 0;
struct nd_prefix *pr;
uint8_t *p = NULL, *ps = NULL;
uint8_t *pe = NULL;
size_t l;
- s = splsoftnet();
-
if (oldp) {
ps = p = (uint8_t*)oldp;
pe = (uint8_t*)oldp + *oldlenp;
}
l = 0;
+ ND6_RLOCK();
ND_PREFIX_LIST_FOREACH(pr) {
u_short advrtrs;
struct sockaddr_in6 sin6;
@@ -2785,6 +2815,7 @@ fill_prlist(void *oldp, size_t *oldlenp,
}
}
}
+ ND6_UNLOCK();
if (oldp) {
*oldlenp = l; /* (void *)d - (void *)oldp */
@@ -2793,8 +2824,6 @@ fill_prlist(void *oldp, size_t *oldlenp,
} else
*oldlenp = l;
- splx(s);
-
return error;
}
Index: src/sys/netinet6/nd6.h
diff -u src/sys/netinet6/nd6.h:1.80 src/sys/netinet6/nd6.h:1.81
--- src/sys/netinet6/nd6.h:1.80 Mon Dec 19 03:32:54 2016
+++ src/sys/netinet6/nd6.h Mon Dec 19 07:51:34 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6.h,v 1.80 2016/12/19 03:32:54 ozaki-r Exp $ */
+/* $NetBSD: nd6.h,v 1.81 2016/12/19 07:51:34 ozaki-r Exp $ */
/* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */
/*
@@ -381,6 +381,14 @@ extern int nd6_debug;
#define nd6log(level, fmt, args...) \
do { if (nd6_debug) log(level, "%s: " fmt, __func__, ##args);} while (0)
+extern krwlock_t nd6_lock;
+
+#define ND6_RLOCK() rw_enter(&nd6_lock, RW_READER)
+#define ND6_WLOCK() rw_enter(&nd6_lock, RW_WRITER)
+#define ND6_UNLOCK() rw_exit(&nd6_lock)
+#define ND6_ASSERT_WLOCK() KASSERT(rw_write_held(&nd6_lock))
+#define ND6_ASSERT_LOCK() KASSERT(rw_lock_held(&nd6_lock))
+
/* nd6_rtr.c */
extern int ip6_desync_factor; /* seconds */
extern u_int32_t ip6_temp_preferred_lifetime; /* seconds */
@@ -463,6 +471,7 @@ void nd6_ra_input(struct mbuf *, int, in
void nd6_defrouter_reset(void);
void nd6_defrouter_select(void);
void nd6_defrtrlist_del(struct nd_defrouter *, struct in6_ifextra *);
+void nd6_prefix_unref(struct nd_prefix *);
void nd6_prelist_remove(struct nd_prefix *);
void nd6_pfxlist_onlink_check(void);
struct nd_defrouter *nd6_defrouter_lookup(const struct in6_addr *, struct ifnet *);
Index: src/sys/netinet6/nd6_nbr.c
diff -u src/sys/netinet6/nd6_nbr.c:1.133 src/sys/netinet6/nd6_nbr.c:1.134
--- src/sys/netinet6/nd6_nbr.c:1.133 Wed Dec 14 04:05:11 2016
+++ src/sys/netinet6/nd6_nbr.c Mon Dec 19 07:51:34 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6_nbr.c,v 1.133 2016/12/14 04:05:11 ozaki-r Exp $ */
+/* $NetBSD: nd6_nbr.c,v 1.134 2016/12/19 07:51:34 ozaki-r Exp $ */
/* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.133 2016/12/14 04:05:11 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.134 2016/12/19 07:51:34 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -809,17 +809,10 @@ nd6_na_input(struct mbuf *m, int off, in
*/
struct nd_defrouter *dr;
const struct in6_addr *in6;
- int s;
in6 = &ln->r_l3addr.addr6;
- /*
- * Lock to protect the default router list.
- * XXX: this might be unnecessary, since this function
- * is only called under the network software interrupt
- * context. However, we keep it just for safety.
- */
- s = splsoftnet();
+ ND6_WLOCK();
dr = nd6_defrouter_lookup(in6, ln->lle_tbl->llt_ifp);
if (dr)
nd6_defrtrlist_del(dr, NULL);
@@ -833,7 +826,7 @@ nd6_na_input(struct mbuf *m, int off, in
*/
nd6_rt_flush(&ip6->ip6_src, ln->lle_tbl->llt_ifp);
}
- splx(s);
+ ND6_UNLOCK();
}
ln->ln_router = is_router;
}
@@ -853,8 +846,11 @@ nd6_na_input(struct mbuf *m, int off, in
if (ln != NULL)
LLE_WUNLOCK(ln);
- if (checklink)
+ if (checklink) {
+ ND6_WLOCK();
nd6_pfxlist_onlink_check();
+ ND6_UNLOCK();
+ }
m_put_rcvif_psref(ifp, &psref);
m_freem(m);
Index: src/sys/netinet6/nd6_rtr.c
diff -u src/sys/netinet6/nd6_rtr.c:1.127 src/sys/netinet6/nd6_rtr.c:1.128
--- src/sys/netinet6/nd6_rtr.c:1.127 Wed Dec 14 06:33:01 2016
+++ src/sys/netinet6/nd6_rtr.c Mon Dec 19 07:51:34 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6_rtr.c,v 1.127 2016/12/14 06:33:01 ozaki-r Exp $ */
+/* $NetBSD: nd6_rtr.c,v 1.128 2016/12/19 07:51:34 ozaki-r Exp $ */
/* $KAME: nd6_rtr.c,v 1.95 2001/02/07 08:09:47 itojun Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.127 2016/12/14 06:33:01 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.128 2016/12/19 07:51:34 ozaki-r Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -312,6 +312,7 @@ nd6_ra_input(struct mbuf *m, int off, in
ip6_sprintf(&ip6->ip6_src),
if_name(ifp), ndi->chlim, nd_ra->nd_ra_curhoplimit);
}
+ ND6_WLOCK();
dr = defrtrlist_update(&drtr);
}
@@ -369,6 +370,7 @@ nd6_ra_input(struct mbuf *m, int off, in
(void)prelist_update(&prc, dr, m, mcast);
}
}
+ ND6_UNLOCK();
/*
* MTU
@@ -432,7 +434,9 @@ nd6_ra_input(struct mbuf *m, int off, in
* router's neighbor cache, which might also affect our on-link
* detection of adveritsed prefixes.
*/
+ ND6_WLOCK();
nd6_pfxlist_onlink_check();
+ ND6_UNLOCK();
}
freeit:
@@ -487,6 +491,8 @@ nd6_defrouter_lookup(const struct in6_ad
{
struct nd_defrouter *dr;
+ ND6_ASSERT_LOCK();
+
ND_DEFROUTER_LIST_FOREACH(dr) {
if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
break;
@@ -502,6 +508,8 @@ nd6_defrtrlist_del(struct nd_defrouter *
struct nd_prefix *pr;
struct nd_ifinfo *ndi;
+ ND6_ASSERT_WLOCK();
+
if (ext == NULL)
ext = dr->ifp->if_afdata[AF_INET6];
@@ -599,6 +607,8 @@ nd6_defrouter_reset(void)
{
struct nd_defrouter *dr;
+ ND6_ASSERT_WLOCK();
+
ND_DEFROUTER_LIST_FOREACH(dr)
defrouter_delreq(dr);
@@ -633,9 +643,10 @@ void
nd6_defrouter_select(void)
{
struct nd_ifinfo *ndi;
- int s = splsoftnet();
struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL;
+ ND6_ASSERT_WLOCK();
+
/*
* This function should be called only when acting as an autoconfigured
* host. Although the remaining part of this function is not effective
@@ -645,7 +656,6 @@ nd6_defrouter_select(void)
if (ip6_forwarding) {
nd6log(LOG_WARNING, "called unexpectedly (forwarding=%d, "
"accept_rtadv=%d)\n", ip6_forwarding, ip6_accept_rtadv);
- splx(s);
return;
}
@@ -653,10 +663,8 @@ nd6_defrouter_select(void)
* Let's handle easy case (3) first:
* If default router list is empty, there's nothing to be done.
*/
- if (ND_DEFROUTER_LIST_EMPTY()) {
- splx(s);
+ if (ND_DEFROUTER_LIST_EMPTY())
return;
- }
/*
* Search for a (probably) reachable router from the list.
@@ -711,7 +719,6 @@ nd6_defrouter_select(void)
defrouter_addreq(selected_dr);
}
- splx(s);
return;
}
@@ -747,7 +754,8 @@ defrtrlist_update(struct nd_defrouter *n
{
struct nd_defrouter *dr, *n, *ret = NULL;
struct in6_ifextra *ext = newdr->ifp->if_afdata[AF_INET6];
- int s = splsoftnet();
+
+ ND6_ASSERT_WLOCK();
if ((dr = nd6_defrouter_lookup(&newdr->rtaddr, newdr->ifp)) != NULL) {
/* entry exists */
@@ -830,7 +838,6 @@ insert:
ret = n;
out:
- splx(s);
return ret;
}
@@ -839,6 +846,8 @@ pfxrtr_lookup(struct nd_prefix *pr, stru
{
struct nd_pfxrouter *search;
+ ND6_ASSERT_LOCK();
+
LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) {
if (search->router == dr)
break;
@@ -852,6 +861,8 @@ pfxrtr_add(struct nd_prefix *pr, struct
{
struct nd_pfxrouter *newpfr;
+ ND6_ASSERT_WLOCK();
+
newpfr = malloc(sizeof(*newpfr), M_IP6NDP, M_NOWAIT|M_ZERO);
if (newpfr == NULL)
return;
@@ -893,6 +904,9 @@ purge_detached(struct ifnet *ifp)
struct in6_ifaddr *ia;
struct ifaddr *ifa, *ifa_next;
+restart:
+ ND6_ASSERT_WLOCK();
+
ND_PREFIX_LIST_FOREACH_SAFE(pr, pr_next) {
int s;
@@ -909,7 +923,6 @@ purge_detached(struct ifnet *ifp)
!LIST_EMPTY(&pr->ndpr_advrtrs)))
continue;
- restart:
s = pserialize_read_enter();
for (ifa = IFADDR_READER_FIRST(ifp); ifa; ifa = ifa_next) {
ifa_next = IFADDR_READER_NEXT(ifa);
@@ -919,14 +932,19 @@ purge_detached(struct ifnet *ifp)
if ((ia->ia6_flags & IN6_IFF_AUTOCONF) ==
IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) {
pserialize_read_exit(s);
+ ND6_UNLOCK();
+
+ /* in6_purgeaddr may destroy pr. */
in6_purgeaddr(ifa);
+
+ ND6_WLOCK();
goto restart;
}
}
pserialize_read_exit(s);
- if (pr->ndpr_refcnt == 0)
- nd6_prelist_remove(pr);
+ KASSERT(pr->ndpr_refcnt == 0);
+ nd6_prelist_remove(pr);
}
}
@@ -939,6 +957,8 @@ nd6_prelist_add(struct nd_prefixctl *prc
int error;
struct in6_ifextra *ext = prc->ndprc_ifp->if_afdata[AF_INET6];
+ ND6_ASSERT_WLOCK();
+
if (ip6_maxifprefixes >= 0) {
if (ext->nprefixes >= ip6_maxifprefixes / 2)
purge_detached(prc->ndprc_ifp);
@@ -1000,12 +1020,26 @@ nd6_prelist_add(struct nd_prefixctl *prc
}
void
+nd6_prefix_unref(struct nd_prefix *pr)
+{
+
+ ND6_WLOCK();
+ pr->ndpr_refcnt--;
+ if (pr->ndpr_refcnt == 0)
+ nd6_prelist_remove(pr);
+ ND6_UNLOCK();
+}
+
+void
nd6_prelist_remove(struct nd_prefix *pr)
{
struct nd_pfxrouter *pfr, *next;
int e, s;
struct in6_ifextra *ext = pr->ndpr_ifp->if_afdata[AF_INET6];
+ ND6_ASSERT_WLOCK();
+ KASSERT(pr->ndpr_refcnt == 0);
+
/* make sure to invalidate the prefix until it is really freed. */
pr->ndpr_vltime = 0;
pr->ndpr_pltime = 0;
@@ -1026,9 +1060,6 @@ nd6_prelist_remove(struct nd_prefix *pr)
/* what should we do? */
}
- if (pr->ndpr_refcnt > 0)
- return; /* notice here? */
-
s = splsoftnet();
/* unlink ndpr_entry from nd_prefix list */
ND_PREFIX_LIST_REMOVE(pr);
@@ -1064,12 +1095,13 @@ prelist_update(struct nd_prefixctl *newp
struct ifaddr *ifa;
struct ifnet *ifp = newprc->ndprc_ifp;
struct nd_prefix *pr;
- int s = splsoftnet();
int error = 0;
int auth;
struct in6_addrlifetime lt6_tmp;
int ss;
+ ND6_ASSERT_WLOCK();
+
auth = 0;
if (m) {
/*
@@ -1395,7 +1427,6 @@ prelist_update(struct nd_prefixctl *newp
}
end:
- splx(s);
return error;
}
@@ -1442,6 +1473,8 @@ nd6_pfxlist_onlink_check(void)
struct nd_pfxrouter *pfxrtr = NULL;
int s;
+ ND6_ASSERT_WLOCK();
+
/*
* Check if there is a prefix that has a reachable advertising
* router.
@@ -1657,6 +1690,8 @@ nd6_prefix_onlink(struct nd_prefix *pr)
struct psref psref;
int bound;
+ ND6_ASSERT_WLOCK();
+
/* sanity check */
if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
nd6log(LOG_ERR, "%s/%d is already on-link\n",
@@ -1764,6 +1799,8 @@ nd6_prefix_offlink(struct nd_prefix *pr)
struct nd_prefix *opr;
struct sockaddr_in6 sa6, mask6;
+ ND6_ASSERT_WLOCK();
+
/* sanity check */
if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
nd6log(LOG_ERR, "%s/%d is already off-link\n",
@@ -1839,6 +1876,8 @@ in6_ifadd(struct nd_prefixctl *prc, int
int updateflags;
int s;
+ ND6_ASSERT_WLOCK();
+
in6_prefixlen2mask(&mask, prefixlen);
/*
@@ -1977,6 +2016,8 @@ in6_tmpifadd(
u_int32_t vltime0, pltime0;
int s;
+ ND6_ASSERT_WLOCK();
+
memset(&ifra, 0, sizeof(ifra));
strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name));
ifra.ifra_addr = ia0->ia_addr;
@@ -2100,6 +2141,8 @@ static int
in6_init_prefix_ltimes(struct nd_prefix *ndpr)
{
+ ND6_ASSERT_WLOCK();
+
/* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */
if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
nd6log(LOG_INFO, "preferred lifetime"