diff options
author | bz <bz@FreeBSD.org> | 2010-04-11 16:04:08 +0000 |
---|---|---|
committer | bz <bz@FreeBSD.org> | 2010-04-11 16:04:08 +0000 |
commit | d7a91dc6bf166a266421facb5e7cc8067695b03b (patch) | |
tree | 62a56a95d03df4cca3ba14e6c3cae4d3f5a44931 /sys/netinet/if_ether.c | |
parent | 63955b94c288cde6ff80eaba0accf6043c55844a (diff) | |
download | FreeBSD-src-d7a91dc6bf166a266421facb5e7cc8067695b03b.zip FreeBSD-src-d7a91dc6bf166a266421facb5e7cc8067695b03b.tar.gz |
Plug reference leaks in the link-layer code ("new-arp") that previously
prevented the link-layer entry from being freed.
In both in.c and in6.c (though that code path seems to be basically dead)
plug a reference leak in case of a pending callout being drained.
In if_ether.c consistently add a reference before resetting the callout
and in case we canceled a pending one remove the reference for that.
In the final case in arptimer, before freeing the expired entry, remove
the reference again and explicitly call callout_stop() to clear the active
flag.
In nd6.c:nd6_free() we are only ever called from the callout function and
thus need to remove the reference there as well before calling into
llentry_free().
In if_llatbl.c when freeing entire tables make sure that in case we cancel
a pending callout to remove the reference as well.
Reviewed by: qingli (earlier version)
MFC after: 10 days
Problem observed, patch tested by: simon on ipv6gw.f.o,
Christian Kratzer (ck cksoft.de),
Evgenii Davidov (dado korolev-net.ru)
PR: kern/144564
Configurations still affected: with options FLOWTABLE
Diffstat (limited to 'sys/netinet/if_ether.c')
-rw-r--r-- | sys/netinet/if_ether.c | 18 |
1 files changed, 15 insertions, 3 deletions
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 97152a7..25fba9f 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -180,6 +180,8 @@ arptimer(void *arg) else { if (!callout_pending(&lle->la_timer) && callout_active(&lle->la_timer)) { + callout_stop(&lle->la_timer); + LLE_REMREF(lle); (void) llentry_free(lle); ARPSTAT_INC(timeouts); } @@ -382,9 +384,14 @@ retry: EHOSTUNREACH : EHOSTDOWN; if (renew) { + int canceled; + LLE_ADDREF(la); la->la_expire = time_second + V_arpt_down; - callout_reset(&la->la_timer, hz * V_arpt_down, arptimer, la); + canceled = callout_reset(&la->la_timer, hz * V_arpt_down, + arptimer, la); + if (canceled) + LLE_REMREF(la); la->la_asked++; LLE_WUNLOCK(la); arprequest(ifp, NULL, &SIN(dst)->sin_addr, @@ -696,9 +703,14 @@ match: EVENTHANDLER_INVOKE(arp_update_event, la); if (!(la->la_flags & LLE_STATIC)) { + int canceled; + + LLE_ADDREF(la); la->la_expire = time_second + V_arpt_keep; - callout_reset(&la->la_timer, hz * V_arpt_keep, - arptimer, la); + canceled = callout_reset(&la->la_timer, + hz * V_arpt_keep, arptimer, la); + if (canceled) + LLE_REMREF(la); } la->la_asked = 0; la->la_preempt = V_arp_maxtries; |