Page MenuHomeFreeBSD

D2254.id4933.diff
No OneTemporary

D2254.id4933.diff

Index: sys/netinet6/in6.c
===================================================================
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -1334,21 +1334,35 @@
in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
{
char ip6buf[INET6_ADDRSTRLEN];
+ struct in6_ifaddr *iatmp;
- IF_ADDR_WLOCK(ifp);
- TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
- IF_ADDR_WUNLOCK(ifp);
- ifa_free(&ia->ia_ifa); /* if_addrhead */
+ IN6_IFADDR_WLOCK();
+ /*
+ * Now that we have the exclusive lock, make sure we're not racing with
+ * a different thread that's attempting to unlink the same address.
+ */
+ LIST_FOREACH(iatmp, IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia6_hash) {
+ if (iatmp == ia)
+ break;
+ }
+ if (iatmp == NULL) {
+ IN6_IFADDR_WUNLOCK();
+ return;
+ }
+ TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link);
+ LIST_REMOVE(ia, ia6_hash);
+ IN6_IFADDR_WUNLOCK();
/*
* Defer the release of what might be the last reference to the
* in6_ifaddr so that it can't be freed before the remainder of the
* cleanup.
*/
- IN6_IFADDR_WLOCK();
- TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link);
- LIST_REMOVE(ia, ia6_hash);
- IN6_IFADDR_WUNLOCK();
+
+ IF_ADDR_WLOCK(ifp);
+ TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
+ IF_ADDR_WUNLOCK(ifp);
+ ifa_free(&ia->ia_ifa); /* if_addrhead */
/*
* Release the reference to the base prefix. There should be a
Index: sys/netinet6/in6_var.h
===================================================================
--- sys/netinet6/in6_var.h
+++ sys/netinet6/in6_var.h
@@ -119,7 +119,6 @@
struct sockaddr_in6 ia_net; /* network number of interface */
struct sockaddr_in6 ia_dstaddr; /* space for destination addr */
struct sockaddr_in6 ia_prefixmask; /* prefix mask */
- u_int32_t ia_plen; /* prefix length */
TAILQ_ENTRY(in6_ifaddr) ia_link; /* list of IPv6 addresses */
int ia6_flags;
@@ -136,6 +135,8 @@
LIST_HEAD(, in6_multi_mship) ia6_memberships;
/* entry in bucket of inet6 addresses */
LIST_ENTRY(in6_ifaddr) ia6_hash;
+ /* expiring address list, only to be used by nd6_timer() */
+ SLIST_ENTRY(in6_ifaddr) ia6_expiring;
};
/* List of in6_ifaddr's. */
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -112,11 +112,6 @@
VNET_DEFINE(int, nd6_debug) = 0;
#endif
-/* for debugging? */
-#if 0
-static int nd6_inuse, nd6_allocated;
-#endif
-
VNET_DEFINE(struct nd_drhead, nd_defrouter);
VNET_DEFINE(struct nd_prhead, nd_prefix);
@@ -606,7 +601,7 @@
/*
- * ND6 timer routine to expire default route list and prefix list
+ * ND6 timer routine to expire default router, address, and prefix list entries.
*/
void
nd6_timer(void *arg)
@@ -615,6 +610,7 @@
struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
struct in6_ifaddr *ia6, *nia6;
+ SLIST_HEAD(, in6_ifaddr) topurge;
callout_reset(&V_nd6_timer_ch, V_nd6_prune * hz,
nd6_timer, curvnet);
@@ -631,15 +627,27 @@
* However, from a stricter speci-confrmance standpoint, we should
* rather separate address lifetimes and prefix lifetimes.
*
- * XXXRW: in6_ifaddrhead locking.
+ * Expired address are placed in the topurge list and are freed only
+ * after we have scanned the full list to avoid acquiring the address
+ * list write lock more often than is necessary.
*/
+
+ SLIST_INIT(&topurge);
+
addrloop:
- TAILQ_FOREACH_SAFE(ia6, &V_in6_ifaddrhead, ia_link, nia6) {
+ IN6_IFADDR_RLOCK();
+ TAILQ_FOREACH(ia6, &V_in6_ifaddrhead, ia_link) {
/* check address lifetime */
if (IFA6_IS_INVALID(ia6)) {
int regen = 0;
/*
+ * regen_tmpaddr() may release the address list lock, so
+ * take a reference before calling it.
+ */
+ ifa_ref(&ia6->ia_ifa);
+
+ /*
* If the expiring address is temporary, try
* regenerating a new one. This would be useful when
* we suspended a laptop PC, then turned it on after a
@@ -655,7 +663,11 @@
regen = 1;
}
- in6_purgeaddr(&ia6->ia_ifa);
+ /*
+ * The ia6_expiring field is reserved for use by
+ * nd6_timer(), and so is protected by the callout lock.
+ */
+ SLIST_INSERT_HEAD(&topurge, ia6, ia6_expiring);
if (regen)
goto addrloop; /* XXX: see below */
@@ -696,6 +708,12 @@
ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
}
}
+ IN6_IFADDR_RUNLOCK();
+
+ SLIST_FOREACH_SAFE(ia6, &topurge, ia6_expiring, nia6) {
+ in6_purgeaddr(&ia6->ia_ifa);
+ ifa_free(&ia6->ia_ifa);
+ }
/* expire prefix list */
LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) {
@@ -719,6 +737,9 @@
/*
* ia6 - deprecated/invalidated temporary address
+ *
+ * Must be called with the in6_ifaddr read lock held. The lock is released if we
+ * add a new address, in which case 0 is returned.
*/
static int
regen_tmpaddr(struct in6_ifaddr *ia6)
@@ -727,6 +748,8 @@
struct ifnet *ifp;
struct in6_ifaddr *public_ifa6 = NULL;
+ IN6_IFADDR_RLOCK_ASSERT();
+
ifp = ia6->ia_ifa.ifa_ifp;
IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -774,12 +797,10 @@
if (public_ifa6 != NULL) {
int e;
- if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) {
- ifa_free(&public_ifa6->ia_ifa);
- log(LOG_NOTICE, "regen_tmpaddr: failed to create a new"
- " tmp addr,errno=%d\n", e);
- return (-1);
- }
+ IN6_IFADDR_RUNLOCK();
+ if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0)
+ log(LOG_NOTICE,
+ "regen_tmpaddr: failed to create a new tmp addr, errno=%d\n", e);
ifa_free(&public_ifa6->ia_ifa);
return (0);
}

File Metadata

Mime Type
text/plain
Expires
Tue, Mar 31, 4:17 AM (17 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30585026
Default Alt Text
D2254.id4933.diff (5 KB)

Event Timeline