Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c +++ sys/netinet6/in6.c @@ -1334,11 +1334,7 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { char ip6buf[INET6_ADDRSTRLEN]; - - 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 */ + struct in6_ifaddr *iatmp; /* * Defer the release of what might be the last reference to the @@ -1346,10 +1342,23 @@ * cleanup. */ IN6_IFADDR_WLOCK(); + 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(); + 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 * positive reference. 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: + IN6_IFADDR_RLOCK(); TAILQ_FOREACH_SAFE(ia6, &V_in6_ifaddrhead, ia_link, nia6) { /* 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,7 @@ regen = 1; } - in6_purgeaddr(&ia6->ia_ifa); + SLIST_INSERT_HEAD(&topurge, ia6, ia6_expiring); if (regen) goto addrloop; /* XXX: see below */ @@ -696,6 +704,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 +733,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 +744,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 +793,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); } Index: sys/netinet6/nd6_rtr.c =================================================================== --- sys/netinet6/nd6_rtr.c +++ sys/netinet6/nd6_rtr.c @@ -1468,9 +1468,8 @@ * 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. */ + IN6_IFADDR_RLOCK(); TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF)) continue; @@ -1505,8 +1504,7 @@ ifa->ia6_flags |= IN6_IFF_DETACHED; } } - } - else { + } else { TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) { if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; @@ -1519,6 +1517,7 @@ } } } + IN6_IFADDR_RUNLOCK(); } static int