Index: sys/netipsec/key.c =================================================================== --- sys/netipsec/key.c +++ sys/netipsec/key.c @@ -631,6 +631,7 @@ #endif static void key_unlink(struct secpolicy *); +static void key_unlink_locked(struct secpolicy *); static struct secpolicy *key_do_allocsp(struct secpolicyindex *spidx, u_int dir); static struct secpolicy *key_getsp(struct secpolicyindex *); static struct secpolicy *key_getspbyid(u_int32_t); @@ -1219,17 +1220,24 @@ key_unlink(struct secpolicy *sp) { + SPTREE_WLOCK(); + key_unlink_locked(sp); + SPTREE_WUNLOCK(); +} + +static void +key_unlink_locked(struct secpolicy *sp) +{ + IPSEC_ASSERT(sp->spidx.dir == IPSEC_DIR_INBOUND || sp->spidx.dir == IPSEC_DIR_OUTBOUND, ("invalid direction %u", sp->spidx.dir)); - SPTREE_UNLOCK_ASSERT(); + SPTREE_WLOCK_ASSERT(); KEYDBG(KEY_STAMP, printf("%s: SP(%p)\n", __func__, sp)); - SPTREE_WLOCK(); if (sp->state != IPSEC_SPSTATE_ALIVE) { /* SP is already unlinked */ - SPTREE_WUNLOCK(); return; } sp->state = IPSEC_SPSTATE_DEAD; @@ -1237,9 +1245,6 @@ V_spd_size--; LIST_REMOVE(sp, idhash); V_sp_genid++; - SPTREE_WUNLOCK(); - if (SPDCACHE_ENABLED()) - spdcache_clear(); key_freesp(&sp); } @@ -1959,7 +1964,7 @@ struct sadb_address *src0, *dst0; struct sadb_x_policy *xpl0, *xpl; struct sadb_lifetime *lft = NULL; - struct secpolicy *newsp; + struct secpolicy *newsp, *oldsp; int error; IPSEC_ASSERT(so != NULL, ("null socket")); @@ -2037,25 +2042,19 @@ src0->sadb_address_proto, &spidx); /* Checking there is SP already or not. */ - newsp = key_getsp(&spidx); - if (newsp != NULL) { - if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { - KEYDBG(KEY_STAMP, - printf("%s: unlink SP(%p) for SPDUPDATE\n", - __func__, newsp)); - KEYDBG(KEY_DATA, kdebug_secpolicy(newsp)); - key_unlink(newsp); - key_freesp(&newsp); - } else { - key_freesp(&newsp); - ipseclog((LOG_DEBUG, "%s: a SP entry exists already.", - __func__)); - return (key_senderror(so, m, EEXIST)); - } + oldsp = key_getsp(&spidx); + if (oldsp != NULL && + mhp->msg->sadb_msg_type != SADB_X_SPDUPDATE) { + key_freesp(&oldsp); + ipseclog((LOG_DEBUG, "%s: a SP entry exists already.", + __func__)); + return (key_senderror(so, m, EEXIST)); } /* allocate new SP entry */ if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) { + if (oldsp != NULL) + key_freesp(&oldsp); return key_senderror(so, m, error); } @@ -2068,9 +2067,19 @@ SPTREE_WLOCK(); if ((newsp->id = key_getnewspid()) == 0) { SPTREE_WUNLOCK(); + if (oldsp != NULL) + key_freesp(&oldsp); key_freesp(&newsp); return key_senderror(so, m, ENOBUFS); } + if (oldsp != NULL) { + KEYDBG(KEY_STAMP, + printf("%s: unlink SP(%p) for SPDUPDATE\n", + __func__, oldsp)); + KEYDBG(KEY_DATA, kdebug_secpolicy(oldsp)); + key_unlink_locked(oldsp); + key_freesp(&oldsp); + } key_insertsp(newsp); SPTREE_WUNLOCK(); if (SPDCACHE_ENABLED()) @@ -2251,6 +2260,8 @@ KEYDBG(KEY_DATA, kdebug_secpolicy(sp)); key_unlink(sp); key_freesp(&sp); + if (SPDCACHE_ENABLED()) + spdcache_clear(); { struct mbuf *n; @@ -2322,6 +2333,9 @@ key_freesp(&sp); return (key_senderror(so, m, EACCES)); } + if (SPDCACHE_ENABLED()) + spdcache_clear(); + key_freesp(&sp); {