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
This commit is contained in:
Bjoern A. Zeeb 2010-04-11 16:04:08 +00:00
parent 3c7ae7bf67
commit becba438d2
5 changed files with 30 additions and 6 deletions

View File

@ -170,9 +170,12 @@ lltable_free(struct lltable *llt)
for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { for (i=0; i < LLTBL_HASHTBL_SIZE; i++) {
LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
int canceled;
callout_drain(&lle->la_timer); canceled = callout_drain(&lle->la_timer);
LLE_WLOCK(lle); LLE_WLOCK(lle);
if (canceled)
LLE_REMREF(lle);
llentry_free(lle); llentry_free(lle);
} }
} }

View File

@ -180,6 +180,8 @@ arptimer(void *arg)
else { else {
if (!callout_pending(&lle->la_timer) && if (!callout_pending(&lle->la_timer) &&
callout_active(&lle->la_timer)) { callout_active(&lle->la_timer)) {
callout_stop(&lle->la_timer);
LLE_REMREF(lle);
(void) llentry_free(lle); (void) llentry_free(lle);
ARPSTAT_INC(timeouts); ARPSTAT_INC(timeouts);
} }
@ -382,9 +384,14 @@ retry:
EHOSTUNREACH : EHOSTDOWN; EHOSTUNREACH : EHOSTDOWN;
if (renew) { if (renew) {
int canceled;
LLE_ADDREF(la); LLE_ADDREF(la);
la->la_expire = time_second + V_arpt_down; 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++; la->la_asked++;
LLE_WUNLOCK(la); LLE_WUNLOCK(la);
arprequest(ifp, NULL, &SIN(dst)->sin_addr, arprequest(ifp, NULL, &SIN(dst)->sin_addr,
@ -696,9 +703,14 @@ match:
EVENTHANDLER_INVOKE(arp_update_event, la); EVENTHANDLER_INVOKE(arp_update_event, la);
if (!(la->la_flags & LLE_STATIC)) { if (!(la->la_flags & LLE_STATIC)) {
int canceled;
LLE_ADDREF(la);
la->la_expire = time_second + V_arpt_keep; la->la_expire = time_second + V_arpt_keep;
callout_reset(&la->la_timer, hz * V_arpt_keep, canceled = callout_reset(&la->la_timer,
arptimer, la); hz * V_arpt_keep, arptimer, la);
if (canceled)
LLE_REMREF(la);
} }
la->la_asked = 0; la->la_asked = 0;
la->la_preempt = V_arp_maxtries; la->la_preempt = V_arp_maxtries;

View File

@ -1357,8 +1357,12 @@ in_lltable_prefix_free(struct lltable *llt,
if (IN_ARE_MASKED_ADDR_EQUAL((struct sockaddr_in *)L3_ADDR(lle), if (IN_ARE_MASKED_ADDR_EQUAL((struct sockaddr_in *)L3_ADDR(lle),
pfx, msk)) { pfx, msk)) {
callout_drain(&lle->la_timer); int canceled;
canceled = callout_drain(&lle->la_timer);
LLE_WLOCK(lle); LLE_WLOCK(lle);
if (canceled)
LLE_REMREF(lle);
llentry_free(lle); llentry_free(lle);
} }
} }

View File

@ -2344,8 +2344,12 @@ in6_lltable_prefix_free(struct lltable *llt,
&((struct sockaddr_in6 *)L3_ADDR(lle))->sin6_addr, &((struct sockaddr_in6 *)L3_ADDR(lle))->sin6_addr,
&pfx->sin6_addr, &pfx->sin6_addr,
&msk->sin6_addr)) { &msk->sin6_addr)) {
callout_drain(&lle->la_timer); int canceled;
canceled = callout_drain(&lle->la_timer);
LLE_WLOCK(lle); LLE_WLOCK(lle);
if (canceled)
LLE_REMREF(lle);
llentry_free(lle); llentry_free(lle);
} }
} }

View File

@ -1125,6 +1125,7 @@ nd6_free(struct llentry *ln, int gc)
ifp = ln->lle_tbl->llt_ifp; ifp = ln->lle_tbl->llt_ifp;
IF_AFDATA_LOCK(ifp); IF_AFDATA_LOCK(ifp);
LLE_WLOCK(ln); LLE_WLOCK(ln);
LLE_REMREF(ln);
llentry_free(ln); llentry_free(ln);
IF_AFDATA_UNLOCK(ifp); IF_AFDATA_UNLOCK(ifp);