diff options
author | melifaro <melifaro@FreeBSD.org> | 2015-08-20 12:05:17 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2015-08-20 12:05:17 +0000 |
commit | 54b3b78856caa3ef7df00c807fd13701f84a49cc (patch) | |
tree | e95d0b96f23a00ddfd22b59844e6418afadff0ed /sys/net/if_llatbl.c | |
parent | 0bfc11710819b319c2d15748a276affb6f2e4343 (diff) | |
download | FreeBSD-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.c | 99 |
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) |