summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/nd6.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.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.c')
-rw-r--r--freebsd/sys/netinet6/nd6.c145
1 files changed, 100 insertions, 45 deletions
diff --git a/freebsd/sys/netinet6/nd6.c b/freebsd/sys/netinet6/nd6.c
index d1c7036d..3e019a62 100644
--- a/freebsd/sys/netinet6/nd6.c
+++ b/freebsd/sys/netinet6/nd6.c
@@ -40,8 +40,10 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
+#include <rtems/bsd/sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
+#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/time.h>
@@ -49,7 +51,6 @@ __FBSDID("$FreeBSD$");
#include <sys/protosw.h>
#include <rtems/bsd/sys/errno.h>
#include <sys/syslog.h>
-#include <rtems/bsd/sys/lock.h>
#include <sys/rwlock.h>
#include <sys/queue.h>
#include <sys/sdt.h>
@@ -120,6 +121,8 @@ static eventhandler_tag lle_event_eh, iflladdr_event_eh;
VNET_DEFINE(struct nd_drhead, nd_defrouter);
VNET_DEFINE(struct nd_prhead, nd_prefix);
VNET_DEFINE(struct rwlock, nd6_lock);
+VNET_DEFINE(uint64_t, nd6_list_genid);
+VNET_DEFINE(struct mtx, nd6_onlink_mtx);
VNET_DEFINE(int, nd6_recalc_reachtm_interval) = ND6_RECALC_REACHTM_INTERVAL;
#define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval)
@@ -211,11 +214,10 @@ void
nd6_init(void)
{
- rw_init(&V_nd6_lock, "nd6");
+ mtx_init(&V_nd6_onlink_mtx, "nd6 onlink", NULL, MTX_DEF);
+ rw_init(&V_nd6_lock, "nd6 list");
LIST_INIT(&V_nd_prefix);
-
- /* initialization of the default router list */
TAILQ_INIT(&V_nd_defrouter);
/* Start timers. */
@@ -247,6 +249,7 @@ nd6_destroy()
EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_event_eh);
}
rw_destroy(&V_nd6_lock);
+ mtx_destroy(&V_nd6_onlink_mtx);
}
#endif
@@ -905,13 +908,15 @@ nd6_timer(void *arg)
{
CURVNET_SET((struct vnet *) arg);
struct nd_drhead drq;
+ struct nd_prhead prl;
struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
struct in6_ifaddr *ia6, *nia6;
+ bool onlink_locked;
TAILQ_INIT(&drq);
+ LIST_INIT(&prl);
- /* expire default router list */
ND6_WLOCK();
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr)
if (dr->expire && dr->expire < time_uptime)
@@ -1018,23 +1023,51 @@ nd6_timer(void *arg)
}
}
- /* expire prefix list */
+ ND6_WLOCK();
+ onlink_locked = false;
+restart:
LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) {
/*
- * check prefix lifetime.
- * since pltime is just for autoconf, pltime processing for
- * prefix is not necessary.
+ * Expire prefixes. Since the pltime is only used for
+ * autoconfigured addresses, pltime processing for prefixes is
+ * not necessary.
+ *
+ * Only unlink after all derived addresses have expired. This
+ * may not occur until two hours after the prefix has expired
+ * per RFC 4862. If the prefix expires before its derived
+ * addresses, mark it off-link. This will be done automatically
+ * after unlinking if no address references remain.
*/
- if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME &&
- time_uptime - pr->ndpr_lastupdate > pr->ndpr_vltime) {
+ if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME ||
+ time_uptime - pr->ndpr_lastupdate <= pr->ndpr_vltime)
+ continue;
- /*
- * address expiration and prefix expiration are
- * separate. NEVER perform in6_purgeaddr here.
- */
- prelist_remove(pr);
+ if (pr->ndpr_addrcnt == 0) {
+ nd6_prefix_unlink(pr, &prl);
+ continue;
+ }
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
+ if (!onlink_locked) {
+ onlink_locked = ND6_ONLINK_TRYLOCK();
+ if (!onlink_locked) {
+ ND6_WUNLOCK();
+ ND6_ONLINK_LOCK();
+ onlink_locked = true;
+ ND6_WLOCK();
+ goto restart;
+ }
+ }
+ (void)nd6_prefix_offlink(pr);
}
}
+ ND6_WUNLOCK();
+ if (onlink_locked)
+ ND6_ONLINK_UNLOCK();
+
+ while ((pr = LIST_FIRST(&prl)) != NULL) {
+ LIST_REMOVE(pr, ndpr_entry);
+ nd6_prefix_del(pr);
+ }
callout_reset(&V_nd6_timer_ch, V_nd6_prune * hz,
nd6_timer, curvnet);
@@ -1120,10 +1153,12 @@ void
nd6_purge(struct ifnet *ifp)
{
struct nd_drhead drq;
+ struct nd_prhead prl;
struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
TAILQ_INIT(&drq);
+ LIST_INIT(&prl);
/*
* Nuke default router list entries toward ifp.
@@ -1138,33 +1173,31 @@ nd6_purge(struct ifnet *ifp)
if (dr->ifp == ifp)
defrouter_unlink(dr, &drq);
}
-
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) {
if (!dr->installed)
continue;
if (dr->ifp == ifp)
defrouter_unlink(dr, &drq);
}
+
+ /*
+ * Remove prefixes on ifp. We should have already removed addresses on
+ * this interface, so no addresses should be referencing these prefixes.
+ */
+ LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) {
+ if (pr->ndpr_ifp == ifp)
+ nd6_prefix_unlink(pr, &prl);
+ }
ND6_WUNLOCK();
+ /* Delete the unlinked router and prefix objects. */
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
TAILQ_REMOVE(&drq, dr, dr_entry);
defrouter_del(dr);
}
-
- /* Nuke prefix list entries toward ifp */
- LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) {
- if (pr->ndpr_ifp == ifp) {
- /*
- * Because if_detach() does *not* release prefixes
- * while purging addresses the reference count will
- * still be above zero. We therefore reset it to
- * make sure that the prefix really gets purged.
- */
- pr->ndpr_refcnt = 0;
-
- prelist_remove(pr);
- }
+ while ((pr = LIST_FIRST(&prl)) != NULL) {
+ LIST_REMOVE(pr, ndpr_entry);
+ nd6_prefix_del(pr);
}
/* cancel default outgoing interface setting */
@@ -1229,8 +1262,9 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
struct ifaddr *dstaddr;
struct rt_addrinfo info;
struct sockaddr_in6 rt_key;
- struct sockaddr *dst6;
- int fibnum;
+ const struct sockaddr *dst6;
+ uint64_t genid;
+ int error, fibnum;
/*
* A link-local address is always a neighbor.
@@ -1268,19 +1302,29 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
* If the address matches one of our on-link prefixes, it should be a
* neighbor.
*/
+ ND6_RLOCK();
+restart:
LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
if (pr->ndpr_ifp != ifp)
continue;
- if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) {
-
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
/* Always use the default FIB here. */
- dst6 = (struct sockaddr *)&pr->ndpr_prefix;
+ dst6 = (const struct sockaddr *)&pr->ndpr_prefix;
+
+ genid = V_nd6_list_genid;
+ ND6_RUNLOCK();
/* Restore length field before retrying lookup */
rt_key.sin6_len = sizeof(rt_key);
- if (rib_lookup_info(fibnum, dst6, 0, 0, &info) != 0)
+ error = rib_lookup_info(fibnum, dst6, 0, 0, &info);
+
+ ND6_RLOCK();
+ if (genid != V_nd6_list_genid)
+ goto restart;
+ if (error != 0)
continue;
+
/*
* This is the case where multiple interfaces
* have the same prefix, but only one is installed
@@ -1292,14 +1336,17 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
* differ.
*/
if (!IN6_ARE_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
- &rt_key.sin6_addr))
+ &rt_key.sin6_addr))
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_RUNLOCK();
return (1);
+ }
}
+ ND6_RUNLOCK();
/*
* If the address is assigned on the node of the other side of
@@ -1730,15 +1777,22 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
case SIOCSPFXFLUSH_IN6:
{
/* flush all the prefix advertised by routers */
+ struct in6_ifaddr *ia, *ia_next;
struct nd_prefix *pr, *next;
+ struct nd_prhead prl;
- LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, next) {
- struct in6_ifaddr *ia, *ia_next;
+ LIST_INIT(&prl);
+ ND6_WLOCK();
+ LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, next) {
if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
continue; /* XXX */
+ nd6_prefix_unlink(pr, &prl);
+ }
+ ND6_WUNLOCK();
- /* do we really have to remove addresses as well? */
+ while ((pr = LIST_FIRST(&prl)) != NULL) {
+ LIST_REMOVE(pr, ndpr_entry);
/* XXXRW: in6_ifaddrhead locking. */
TAILQ_FOREACH_SAFE(ia, &V_in6_ifaddrhead, ia_link,
ia_next) {
@@ -1748,7 +1802,7 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
if (ia->ia6_ndpr == pr)
in6_purgeaddr(&ia->ia_ifa);
}
- prelist_remove(pr);
+ nd6_prefix_del(pr);
}
break;
}
@@ -2676,7 +2730,7 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
else
p.expire = maxexpire;
}
- p.refcnt = pr->ndpr_refcnt;
+ p.refcnt = pr->ndpr_addrcnt;
p.flags = pr->ndpr_stateflags;
p.advrtrs = 0;
LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry)
@@ -2692,9 +2746,10 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
ip6_sprintf(ip6buf, &pfr->router->rtaddr));
error = SYSCTL_OUT(req, &s6, sizeof(s6));
if (error != 0)
- break;
+ goto out;
}
}
+out:
ND6_RUNLOCK();
return (error);
}