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"

Reply via email to