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"