summaryrefslogtreecommitdiffstats
path: root/sys/net/if_llatbl.c
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2015-08-20 12:05:17 +0000
committermelifaro <melifaro@FreeBSD.org>2015-08-20 12:05:17 +0000
commit54b3b78856caa3ef7df00c807fd13701f84a49cc (patch)
treee95d0b96f23a00ddfd22b59844e6418afadff0ed /sys/net/if_llatbl.c
parent0bfc11710819b319c2d15748a276affb6f2e4343 (diff)
downloadFreeBSD-src-54b3b78856caa3ef7df00c807fd13701f84a49cc.zip
FreeBSD-src-54b3b78856caa3ef7df00c807fd13701f84a49cc.tar.gz
* Split allocation and table linking for lle's.
Before that, the logic besides lle_create() was the following: return existing if found, create if not. This behaviour was error-prone since we had to deal with 'sudden' static<>dynamic lle changes. This commit fixes bunch of different issues like: - refcount leak when lle is converted to static. Simple check case: console 1: while true; do for i in `arp -an|awk '$4~/incomp/{print$2}'|tr -d '()'`; do arp -s $i 00:22:44:66:88:00 ; arp -d $i; done; done console 2: ping -f any-dead-host-in-L2 console 3: # watch for memory consumption: vmstat -m | awk '$1~/lltable/{print$2}' - possible problems in arptimer() / nd6_timer() when dropping/reacquiring lock. New logic explicitly handles use-or-create cases in every lla_create user. Basically, most of the changes are purely mechanical. However, we explicitly avoid using existing lle's for interface/static LLE records. * While here, call lle_event handlers on all real table lle change. * Create lltable_free_entry() calling existing per-lltable lle_free_t callback for entry deletion
Diffstat (limited to 'sys/net/if_llatbl.c')
-rw-r--r--sys/net/if_llatbl.c99
1 files changed, 79 insertions, 20 deletions
diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c
index 3f9e38a..38f1c1f 100644
--- a/sys/net/if_llatbl.c
+++ b/sys/net/if_llatbl.c
@@ -228,7 +228,7 @@ htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
IF_AFDATA_WUNLOCK(llt->llt_ifp);
LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
- llt->llt_free_entry(llt, lle);
+ lltable_free_entry(llt, lle);
}
static void
@@ -278,10 +278,13 @@ lltable_drop_entry_queue(struct llentry *lle)
}
/*
- * Deletes an address from the address table.
- * This function is called by the timer functions
- * such as arptimer() and nd6_llinfo_timer(), and
- * the caller does the locking.
+ *
+ * Performes generic cleanup routines and frees lle.
+ *
+ * Called for non-linked entries, with callouts and
+ * other AF-specific cleanups performed.
+ *
+ * @lle must be passed WLOCK'ed
*
* Returns the number of held packets, if any, that were dropped.
*/
@@ -316,21 +319,35 @@ struct llentry *
llentry_alloc(struct ifnet *ifp, struct lltable *lt,
struct sockaddr_storage *dst)
{
- struct llentry *la;
+ struct llentry *la, *la_tmp;
IF_AFDATA_RLOCK(ifp);
la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
IF_AFDATA_RUNLOCK(ifp);
- if ((la == NULL) &&
- (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
- IF_AFDATA_WLOCK(ifp);
- la = lla_create(lt, 0, (struct sockaddr *)dst);
- IF_AFDATA_WUNLOCK(ifp);
- }
if (la != NULL) {
LLE_ADDREF(la);
LLE_WUNLOCK(la);
+ return (la);
+ }
+
+ if ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
+ la = lltable_alloc_entry(lt, 0, (struct sockaddr *)dst);
+ if (la == NULL)
+ return (NULL);
+ IF_AFDATA_WLOCK(ifp);
+ LLE_WLOCK(la);
+ /* Prefer any existing LLE over newly-created one */
+ la_tmp = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
+ if (la_tmp == NULL)
+ lltable_link_entry(lt, la);
+ IF_AFDATA_WUNLOCK(ifp);
+ if (la_tmp != NULL) {
+ lltable_free_entry(lt, la);
+ la = la_tmp;
+ }
+ LLE_ADDREF(la);
+ LLE_WUNLOCK(la);
}
return (la);
@@ -483,6 +500,21 @@ lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
return (llt->llt_foreach_entry(llt, f, farg));
}
+struct llentry *
+lltable_alloc_entry(struct lltable *llt, u_int flags,
+ const struct sockaddr *l3addr)
+{
+
+ return (llt->llt_alloc_entry(llt, flags, l3addr));
+}
+
+void
+lltable_free_entry(struct lltable *llt, struct llentry *lle)
+{
+
+ llt->llt_free_entry(llt, lle);
+}
+
void
lltable_link_entry(struct lltable *llt, struct llentry *lle)
{
@@ -531,7 +563,7 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST];
struct ifnet *ifp;
struct lltable *llt;
- struct llentry *lle;
+ struct llentry *lle, *lle_tmp;
u_int laflags = 0;
int error;
@@ -560,13 +592,9 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
switch (rtm->rtm_type) {
case RTM_ADD:
/* Add static LLE */
- IF_AFDATA_WLOCK(ifp);
- lle = lla_create(llt, 0, dst);
- if (lle == NULL) {
- IF_AFDATA_WUNLOCK(ifp);
+ lle = lltable_alloc_entry(llt, 0, dst);
+ if (lle == NULL)
return (ENOMEM);
- }
-
bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
if ((rtm->rtm_flags & RTF_ANNOUNCE))
@@ -589,8 +617,39 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
} else
lle->la_expire = rtm->rtm_rmx.rmx_expire;
laflags = lle->la_flags;
- LLE_WUNLOCK(lle);
+
+ /* Try to link new entry */
+ lle_tmp = NULL;
+ IF_AFDATA_WLOCK(ifp);
+ LLE_WLOCK(lle);
+ lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, dst);
+ if (lle_tmp != NULL) {
+ /* Check if we are trying to replace immutable entry */
+ if ((lle_tmp->la_flags & LLE_IFADDR) != 0) {
+ IF_AFDATA_WUNLOCK(ifp);
+ LLE_WUNLOCK(lle_tmp);
+ lltable_free_entry(llt, lle);
+ return (EPERM);
+ }
+ /* Unlink existing entry from table */
+ lltable_unlink_entry(llt, lle_tmp);
+ }
+ lltable_link_entry(llt, lle);
IF_AFDATA_WUNLOCK(ifp);
+
+ if (lle_tmp != NULL) {
+ EVENTHANDLER_INVOKE(lle_event, lle_tmp,LLENTRY_EXPIRED);
+ lltable_free_entry(llt, lle_tmp);
+ }
+
+ /*
+ * By invoking LLE handler here we might get
+ * two events on static LLE entry insertion
+ * in routing socket. However, since we might have
+ * other subscribers we need to generate this event.
+ */
+ EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED);
+ LLE_WUNLOCK(lle);
#ifdef INET
/* gratuitous ARP */
if ((laflags & LLE_PUB) && dst->sa_family == AF_INET)
OpenPOWER on IntegriCloud