diff options
author | melifaro <melifaro@FreeBSD.org> | 2015-09-14 16:48:19 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2015-09-14 16:48:19 +0000 |
commit | 5ad1f2444d736f306932d8a69b8357c63297cc83 (patch) | |
tree | 6b997784cb57fc09b4fd60b7c833b15d00ded1d9 /sys/netinet6 | |
parent | bbe0ea3ac0e8a5df29ff62333dbfaed8befc6da1 (diff) | |
download | FreeBSD-src-5ad1f2444d736f306932d8a69b8357c63297cc83.zip FreeBSD-src-5ad1f2444d736f306932d8a69b8357c63297cc83.tar.gz |
* Do more fine-grained locking: call eventhandlers/free_entry
without holding afdata wlock
* convert per-af delete_address callback to global lltable_delete_entry() and
more low-level "delete this lle" per-af callback
* fix some bugs/inconsistencies in IPv4/IPv6 ifscrub procedures
Sponsored by: Yandex LLC
Differential Revision: https://reviews.freebsd.org/D3573
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6.c | 76 | ||||
-rw-r--r-- | sys/netinet6/nd6.c | 19 | ||||
-rw-r--r-- | sys/netinet6/nd6.h | 2 |
3 files changed, 51 insertions, 46 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index b14af01..9996dbf 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1307,9 +1307,6 @@ in6_purgeaddr(struct ifaddr *ifa) /* stop DAD processing */ nd6_dad_stop(ifa); - /* Remove local address entry from lltable. */ - nd6_rem_ifa_lle(ia); - /* Leave multicast groups. */ while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); @@ -1333,6 +1330,7 @@ static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { char ip6buf[INET6_ADDRSTRLEN]; + int remove_lle; IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); @@ -1353,15 +1351,21 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) * Release the reference to the base prefix. There should be a * positive reference. */ + remove_lle = 0; if (ia->ia6_ndpr == NULL) { nd6log((LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia)))); } else { ia->ia6_ndpr->ndpr_refcnt--; + /* Do not delete lles within prefix if refcont != 0 */ + if (ia->ia6_ndpr->ndpr_refcnt == 0) + remove_lle = 1; ia->ia6_ndpr = NULL; } + nd6_rem_ifa_lle(ia, remove_lle); + /* * Also, if the address being removed is autoconf'ed, call * pfxlist_onlink_check() since the release might affect the status of @@ -2081,15 +2085,33 @@ in6_lltable_new(const struct in6_addr *addr6, u_int flags) } static int -in6_lltable_match_prefix(const struct sockaddr *prefix, - const struct sockaddr *mask, u_int flags, struct llentry *lle) +in6_lltable_match_prefix(const struct sockaddr *saddr, + const struct sockaddr *smask, u_int flags, struct llentry *lle) { - const struct sockaddr_in6 *pfx = (const struct sockaddr_in6 *)prefix; - const struct sockaddr_in6 *msk = (const struct sockaddr_in6 *)mask; + const struct in6_addr *addr, *mask, *lle_addr; + + addr = &((const struct sockaddr_in6 *)saddr)->sin6_addr; + mask = &((const struct sockaddr_in6 *)smask)->sin6_addr; + lle_addr = &lle->r_l3addr.addr6; + + if (IN6_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) + return (0); + + if (lle->la_flags & LLE_IFADDR) { + + /* + * Delete LLE_IFADDR records IFF address & flag matches. + * Note that addr is the interface address within prefix + * being matched. + */ + if (IN6_ARE_ADDR_EQUAL(addr, lle_addr) && + (flags & LLE_STATIC) != 0) + return (1); + return (0); + } - if (IN6_ARE_MASKED_ADDR_EQUAL(&lle->r_l3addr.addr6, - &pfx->sin6_addr, &msk->sin6_addr) && - ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))) + /* flags & LLE_STATIC means deleting both dynamic and static entries */ + if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) return (1); return (0); @@ -2200,36 +2222,16 @@ in6_lltable_find_dst(struct lltable *llt, const struct in6_addr *dst) return (lle); } -static int -in6_lltable_delete(struct lltable *llt, u_int flags, - const struct sockaddr *l3addr) +static void +in6_lltable_delete_entry(struct lltable *llt, struct llentry *lle) { - const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr; - struct llentry *lle; - - IF_AFDATA_LOCK_ASSERT(llt->llt_ifp); - KASSERT(l3addr->sa_family == AF_INET6, - ("sin_family %d", l3addr->sa_family)); - - lle = in6_lltable_find_dst(llt, &sin6->sin6_addr); - if (lle == NULL) - return (ENOENT); - - if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) { - LLE_WLOCK(lle); - lle->la_flags |= LLE_DELETED; - EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); + lle->la_flags |= LLE_DELETED; + EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); #ifdef DIAGNOSTIC - log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); + log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif - if ((lle->la_flags & (LLE_STATIC | LLE_IFADDR)) == LLE_STATIC) - llentry_free(lle); - else - LLE_WUNLOCK(lle); - } - - return (0); + llentry_free(lle); } static struct llentry * @@ -2369,7 +2371,7 @@ in6_lltattach(struct ifnet *ifp) llt->llt_lookup = in6_lltable_lookup; llt->llt_alloc_entry = in6_lltable_alloc; - llt->llt_delete = in6_lltable_delete; + llt->llt_delete_entry = in6_lltable_delete_entry; llt->llt_dump_entry = in6_lltable_dump_entry; llt->llt_hash = in6_lltable_hash; llt->llt_fill_sa_entry = in6_lltable_fill_sa_entry; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 11276a5..1284ffc 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -2245,23 +2245,26 @@ nd6_add_ifa_lle(struct in6_ifaddr *ia) } /* - * Removes ALL lle records for interface address prefix. - * XXXME: That's probably not we really want to do, we need - * to remove address record only and keep other records - * until we determine if given prefix is really going - * to be removed. + * Removes either all lle entries for given @ia, or lle + * corresponding to @ia address. */ void -nd6_rem_ifa_lle(struct in6_ifaddr *ia) +nd6_rem_ifa_lle(struct in6_ifaddr *ia, int all) { struct sockaddr_in6 mask, addr; + struct sockaddr *saddr, *smask; struct ifnet *ifp; ifp = ia->ia_ifa.ifa_ifp; memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - lltable_prefix_free(AF_INET6, (struct sockaddr *)&addr, - (struct sockaddr *)&mask, LLE_STATIC); + saddr = (struct sockaddr *)&addr; + smask = (struct sockaddr *)&mask; + + if (all != 0) + lltable_prefix_free(AF_INET6, saddr, smask, LLE_STATIC); + else + lltable_delete_addr(LLTABLE6(ifp), LLE_IFADDR, saddr); } /* diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index f4f7f65..e28e4cc 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -427,7 +427,7 @@ int nd6_flush_holdchain(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *); int nd6_need_cache(struct ifnet *); int nd6_add_ifa_lle(struct in6_ifaddr *); -void nd6_rem_ifa_lle(struct in6_ifaddr *); +void nd6_rem_ifa_lle(struct in6_ifaddr *, int); int nd6_storelladdr(struct ifnet *, struct mbuf *, const struct sockaddr *, u_char *, uint32_t *); |