Add address list locking for in6_ifaddrhead/ia_link: as with locking

for in_ifaddrhead, we stick with an rwlock for the time being, which
we will revisit in the future with a possible move to rmlocks.

Some pieces of code require significant further reworking to be
safe from all classes of writer-writer races.

Reviewed by:	bz
MFC after:	6 weeks
This commit is contained in:
rwatson 2009-06-25 16:35:28 +00:00
parent 07492aedb8
commit bd6eb7be79
9 changed files with 65 additions and 9 deletions

View File

@ -1680,6 +1680,7 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
/* we have to do it by hands to check we won't match on us */
ia_if = NULL; own = 0;
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
int i;
@ -1702,14 +1703,20 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
}
}
if (!ia_if)
if (!ia_if) {
IN6_IFADDR_RUNLOCK();
return (EADDRNOTAVAIL);
}
ia = ia_if;
ifa_ref(&ia->ia_ifa);
IN6_IFADDR_RUNLOCK();
ifp = ia->ia_ifp;
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 ||
(im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp))
(im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) {
ifa_free(&ia->ia_ifa);
return (EADDRNOTAVAIL);
}
if (!sc->sc_naddrs6) {
struct in6_multi *in6m;
@ -1811,12 +1818,14 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
carp_setrun(sc, 0);
CARP_UNLOCK(cif);
ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */
return (0);
cleanup:
if (!sc->sc_naddrs6)
carp_multicast6_cleanup(sc);
ifa_free(&ia->ia_ifa);
return (error);
}

View File

@ -831,8 +831,10 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
TAILQ_INSERT_TAIL(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
IF_ADDR_UNLOCK(ifp);
ifa_ref(&ia->ia_ifa); /* in6_if_addrhead */
ifa_ref(&ia->ia_ifa); /* in6_ifaddrhead */
IN6_IFADDR_WLOCK();
TAILQ_INSERT_TAIL(&V_in6_ifaddrhead, ia, ia_link);
IN6_IFADDR_WUNLOCK();
}
/* update timestamp */
@ -1376,7 +1378,9 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
IF_ADDR_UNLOCK(ifp);
ifa_free(&ia->ia_ifa); /* if_addrhead */
IN6_IFADDR_WLOCK();
TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link);
IN6_IFADDR_WUNLOCK();
ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */
/*
@ -1917,12 +1921,15 @@ in6_localaddr(struct in6_addr *in6)
if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6))
return 1;
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
&ia->ia_prefixmask.sin6_addr)) {
IN6_IFADDR_RUNLOCK();
return 1;
}
}
IN6_IFADDR_RUNLOCK();
return (0);
}
@ -1933,14 +1940,18 @@ in6_is_addr_deprecated(struct sockaddr_in6 *sa6)
INIT_VNET_INET6(curvnet);
struct in6_ifaddr *ia;
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr,
&sa6->sin6_addr) &&
(ia->ia6_flags & IN6_IFF_DEPRECATED) != 0)
(ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) {
IN6_IFADDR_RUNLOCK();
return (1); /* true */
}
/* XXX: do we still have to go thru the rest of the list? */
}
IN6_IFADDR_RUNLOCK();
return (0); /* false */
}
@ -2074,7 +2085,9 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
IF_ADDR_UNLOCK(ifp);
return (besta);
}
IF_ADDR_UNLOCK(ifp);
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@ -2092,10 +2105,10 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst)
if (ifa != NULL)
ifa_ref(ifa);
IF_ADDR_UNLOCK(ifp);
IN6_IFADDR_RUNLOCK();
return (struct in6_ifaddr *)ifa;
}
IF_ADDR_UNLOCK(ifp);
IN6_IFADDR_RUNLOCK();
/* use the last-resort values, that are, deprecated addresses */
if (dep[0])

View File

@ -836,7 +836,9 @@ in6_ifdetach(struct ifnet *ifp)
IF_ADDR_UNLOCK(ifp);
ifa_free(ifa); /* if_addrhead */
IN6_IFADDR_WLOCK();
TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link);
IN6_IFADDR_WUNLOCK();
ifa_free(ifa);
}

View File

@ -289,6 +289,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
if (error)
return (error);
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
int new_scope = -1, new_matchlen = -1;
struct in6_addrpolicy *new_policy = NULL;
@ -466,13 +467,16 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
break;
}
if ((ia = ia_best) == NULL)
if ((ia = ia_best) == NULL) {
IN6_IFADDR_RUNLOCK();
return (EADDRNOTAVAIL);
}
if (ifpp)
*ifpp = ifp;
bcopy(&ia->ia_addr.sin6_addr, srcp, sizeof(*srcp));
IN6_IFADDR_RUNLOCK();
return (0);
}

View File

@ -493,6 +493,16 @@ extern struct icmp6stat icmp6stat;
extern unsigned long in6_maxmtu;
#endif /* VIMAGE_GLOBALS */
extern struct rwlock in6_ifaddr_lock;
#define IN6_IFADDR_LOCK_ASSERT( ) rw_assert(&in6_ifaddr_lock, RA_LOCKED)
#define IN6_IFADDR_RLOCK() rw_rlock(&in6_ifaddr_lock)
#define IN6_IFADDR_RLOCK_ASSERT() rw_assert(&in6_ifaddr_lock, RA_RLOCKED)
#define IN6_IFADDR_RUNLOCK() rw_runlock(&in6_ifaddr_lock)
#define IN6_IFADDR_WLOCK() rw_wlock(&in6_ifaddr_lock)
#define IN6_IFADDR_WLOCK_ASSERT() rw_assert(&in6_ifaddr_lock, RA_WLOCKED)
#define IN6_IFADDR_WUNLOCK() rw_wunlock(&in6_ifaddr_lock)
#define in6_ifstat_inc(ifp, tag) \
do { \
if (ifp) \

View File

@ -150,6 +150,9 @@ extern int udp6_sendspace;
extern int udp6_recvspace;
#endif
struct rwlock in6_ifaddr_lock;
RW_SYSINIT(in6_ifaddr_lock, &in6_ifaddr_lock, "in6_ifaddr_lock");
struct pfil_head inet6_pfil_hook;
static void ip6_init2(void *);

View File

@ -624,6 +624,8 @@ nd6_timer(void *arg)
* in the past the loop was inside prefix expiry processing.
* However, from a stricter speci-confrmance standpoint, we should
* rather separate address lifetimes and prefix lifetimes.
*
* XXXRW: in6_ifaddrhead locking.
*/
addrloop:
TAILQ_FOREACH_SAFE(ia6, &V_in6_ifaddrhead, ia_link, nia6) {
@ -1328,6 +1330,7 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
continue; /* XXX */
/* do we really have to remove addresses as well? */
/* XXXRW: in6_ifaddrhead locking. */
TAILQ_FOREACH_SAFE(ia, &V_in6_ifaddrhead, ia_link,
ia_next) {
if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0)

View File

@ -1500,6 +1500,8 @@ pfxlist_onlink_check()
* detached. Note, however, that a manually configured address should
* always be attached.
* The precise detection logic is same as the one for prefixes.
*
* XXXRW: in6_ifaddrhead locking.
*/
TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF))
@ -1949,10 +1951,12 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay)
* there may be a time lag between generation of the ID and generation
* of the address. So, we'll do one more sanity check.
*/
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr,
&ifra.ifra_addr.sin6_addr)) {
if (trylimit-- == 0) {
IN6_IFADDR_RUNLOCK();
/*
* Give up. Something strange should have
* happened.
@ -1961,10 +1965,12 @@ in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen, int delay)
"find a unique random IFID\n"));
return (EEXIST);
}
IN6_IFADDR_RUNLOCK();
forcegen = 1;
goto again;
}
}
IN6_IFADDR_RUNLOCK();
/*
* The Valid Lifetime is the lower of the Valid Lifetime of the

View File

@ -3982,10 +3982,13 @@ key_ismyaddr6(sin6)
struct in6_multi *in6m;
#endif
IN6_IFADDR_RLOCK();
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
if (key_sockaddrcmp((struct sockaddr *)&sin6,
(struct sockaddr *)&ia->ia_addr, 0) == 0)
(struct sockaddr *)&ia->ia_addr, 0) == 0) {
IN6_IFADDR_RUNLOCK();
return 1;
}
#if 0
/*
@ -3996,10 +3999,13 @@ key_ismyaddr6(sin6)
*/
in6m = NULL;
IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m);
if (in6m)
if (in6m) {
IN6_IFADDR_RUNLOCK();
return 1;
}
#endif
}
IN6_IFADDR_RUNLOCK();
/* loopback, just for safety */
if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))