summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2015-09-14 16:48:19 +0000
committermelifaro <melifaro@FreeBSD.org>2015-09-14 16:48:19 +0000
commit5ad1f2444d736f306932d8a69b8357c63297cc83 (patch)
tree6b997784cb57fc09b4fd60b7c833b15d00ded1d9 /sys/netinet6
parentbbe0ea3ac0e8a5df29ff62333dbfaed8befc6da1 (diff)
downloadFreeBSD-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.c76
-rw-r--r--sys/netinet6/nd6.c19
-rw-r--r--sys/netinet6/nd6.h2
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 *);
OpenPOWER on IntegriCloud