Fix bugs in SPD refcounts due to PCBpolicy cache, by backporting the

KAME sys/netkey/key.c rev 1.119 ke_sp_unlink()/key_sp_dead() logic.

I have been running a similar version for about 10 days now, and it
fixes the PCB-cache refcount problems for me.

Checked in as a candidate for pullup to the 2.0 branch.
This commit is contained in:
jonathan 2004-05-26 23:16:25 +00:00
parent eb8a427e28
commit 10c711c569
1 changed files with 56 additions and 15 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: key.c,v 1.17 2004/05/26 22:14:18 jonathan Exp $ */
/* $NetBSD: key.c,v 1.18 2004/05/26 23:16:25 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/key.c,v 1.3.2.3 2004/02/14 22:23:23 bms Exp $ */
/* $KAME: key.c,v 1.191 2001/06/27 10:46:49 sakane Exp $ */
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.17 2004/05/26 22:14:18 jonathan Exp $");
__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.18 2004/05/26 23:16:25 jonathan Exp $");
/*
* This code is referd to RFC 2367
@ -489,6 +489,9 @@ static const char *key_getfqdn __P((void));
static const char *key_getuserfqdn __P((void));
#endif
static void key_sa_chgstate __P((struct secasvar *, u_int8_t));
static __inline void key_sp_dead __P((struct secpolicy *));
static void key_sp_unlink __P((struct secpolicy *sp));
static struct mbuf *key_alloc_mbuf __P((int));
struct callout key_timehandler_ch;
@ -514,6 +517,28 @@ struct callout key_timehandler_ch;
(p)->refcnt--; \
} while (0)
static __inline void
key_sp_dead(struct secpolicy *sp)
{
/* mark the SP dead */
sp->state = IPSEC_SPSTATE_DEAD;
}
static void
key_sp_unlink(struct secpolicy *sp)
{
/* remove from SP index */
if (__LIST_CHAINED(sp)) {
LIST_REMOVE(sp, chain);
/* Release refcount held just for being on chain */
KEY_FREESP(&sp);
}
}
/*
* Return 0 when there are known to be no SP's for the specified
* direction. Otherwise return 1. This is used by IPsec code
@ -1188,16 +1213,13 @@ key_delsp(struct secpolicy *sp)
IPSEC_ASSERT(sp != NULL, ("key_delsp: null sp"));
sp->state = IPSEC_SPSTATE_DEAD;
key_sp_dead(sp);
IPSEC_ASSERT(sp->refcnt == 0,
("key_delsp: SP with references deleted (refcnt %u)",
sp->refcnt));
s = splsoftnet(); /*called from softclock()*/
/* remove from SP index */
if (__LIST_CHAINED(sp))
LIST_REMOVE(sp, chain);
{
struct ipsecrequest *isr = sp->req, *nextisr;
@ -1803,8 +1825,10 @@ key_spdadd(so, m, mhp)
newsp = key_getsp(&spidx);
if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
if (newsp) {
newsp->state = IPSEC_SPSTATE_DEAD;
key_sp_dead(newsp);
key_sp_unlink(newsp); /* XXX jrs ordering */
KEY_FREESP(&newsp);
newsp = NULL;
}
} else {
if (newsp != NULL) {
@ -2042,8 +2066,9 @@ key_spddelete(so, m, mhp)
/* save policy id to buffer to be returned. */
xpl0->sadb_x_policy_id = sp->id;
sp->state = IPSEC_SPSTATE_DEAD;
KEY_FREESP(&sp);
key_sp_dead(sp);
key_sp_unlink(sp); /* XXX jrs ordering */
KEY_FREESP(&sp); /* ref gained by key_getspbyid */
#if defined(__NetBSD__)
/* Invalidate all cached SPD pointers in the PCBs. */
@ -2111,8 +2136,10 @@ key_spddelete2(so, m, mhp)
key_senderror(so, m, EINVAL);
}
sp->state = IPSEC_SPSTATE_DEAD;
KEY_FREESP(&sp);
key_sp_dead(sp);
key_sp_unlink(sp); /* XXX jrs ordering */
KEY_FREESP(&sp); /* ref gained by key_getsp */
sp = NULL;
#if defined(__NetBSD__)
/* Invalidate all cached SPD pointers in the PCBs. */
@ -2325,8 +2352,18 @@ key_spdflush(so, m, mhp)
return key_senderror(so, m, EINVAL);
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
LIST_FOREACH(sp, &sptree[dir], chain) {
sp->state = IPSEC_SPSTATE_DEAD;
struct secpolicy * nextsp;
for (sp = LIST_FIRST(&sptree[dir]);
sp != NULL;
sp = nextsp) {
nextsp = LIST_NEXT(sp, chain);
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
key_sp_dead(sp);
key_sp_unlink(sp);
/* 'sp' dead; continue transfers to 'sp = nextsp' */
continue;
}
}
@ -4119,7 +4156,11 @@ key_timehandler(void* arg)
nextsp = LIST_NEXT(sp, chain);
if (sp->state == IPSEC_SPSTATE_DEAD) {
KEY_FREESP(&sp);
key_sp_unlink(sp); /*XXX*/
/* 'sp' dead; continue transfers to
* 'sp = nextsp'
*/
continue;
}
@ -4129,7 +4170,7 @@ key_timehandler(void* arg)
/* the deletion will occur next time */
if ((sp->lifetime && now - sp->created > sp->lifetime)
|| (sp->validtime && now - sp->lastused > sp->validtime)) {
sp->state = IPSEC_SPSTATE_DEAD;
key_sp_dead(sp);
key_spdexpire(sp);
continue;
}