Module Name: src
Committed By: ozaki-r
Date: Tue Aug 8 04:17:34 UTC 2017
Modified Files:
src/sys/netipsec: key.c keydb.h
Log Message:
MP-ify SAD (key_sad.sahlist and sah entries)
localcount(9) is used to protect key_sad.sahlist and sah entries
as well as SPD (and will be used for SAD sav).
Please read the locking notes of SAD for more details.
To generate a diff of this commit:
cvs rdiff -u -r1.215 -r1.216 src/sys/netipsec/key.c
cvs rdiff -u -r1.18 -r1.19 src/sys/netipsec/keydb.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/netipsec/key.c
diff -u src/sys/netipsec/key.c:1.215 src/sys/netipsec/key.c:1.216
--- src/sys/netipsec/key.c:1.215 Tue Aug 8 01:56:49 2017
+++ src/sys/netipsec/key.c Tue Aug 8 04:17:34 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: key.c,v 1.215 2017/08/08 01:56:49 ozaki-r Exp $ */
+/* $NetBSD: key.c,v 1.216 2017/08/08 04:17:34 ozaki-r 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.215 2017/08/08 01:56:49 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.216 2017/08/08 04:17:34 ozaki-r Exp $");
/*
* This code is referd to RFC 2367
@@ -198,12 +198,35 @@ static u_int32_t acq_seq = 0;
* until GC by the timer
*/
/*
+ * Locking notes on SAD:
+ * - Data structures
+ * - SAs are managed by the list called key_sad.sahlist and sav lists of sah
+ * entries
+ * - A sah has sav lists for each SA state
+ * - Multiple sahs with the same saidx can exist
+ * - Only one entry has MATURE state and others should be DEAD
+ * - DEAD entries are just ignored from searching
+ * - Modifications to the key_sad.sahlist must be done with holding key_sad.lock
+ * which is a adaptive mutex
+ * - Read accesses to the key_sad.sahlist must be in pserialize(9) read sections
+ * - sah's lifetime is managed by localcount(9)
+ * - Getting an sah entry
+ * - We get an SP from the key_spd.splist
+ * - Must iterate the list and increment the reference count of a found sah
+ * (by key_sah_ref) in a pserialize read section
+ * - A gotten sah must be released after use by key_sah_unref
+ * - An sah is destroyed when its state become DEAD and no sav is
+ * listed to the sah
+ * - The destruction is done only in the timer (see key_timehandler_sad)
+ */
+/*
* Locking notes on misc data:
* - All lists of key_misc are protected by key_misc.lock
* - key_misc.lock must be held even for read accesses
*/
-static pserialize_t key_psz __read_mostly;
+static pserialize_t key_spd_psz __read_mostly;
+static pserialize_t key_sad_psz __read_mostly;
/* SPD */
static struct {
@@ -220,6 +243,7 @@ static struct {
/* SAD */
static struct {
kmutex_t lock;
+ kcondvar_t cv;
struct pslist_head sahlist;
} key_sad __cacheline_aligned;
@@ -614,13 +638,18 @@ static struct mbuf *key_setdumpsp (struc
static u_int key_getspreqmsglen (const struct secpolicy *);
static int key_spdexpire (struct secpolicy *);
static struct secashead *key_newsah (const struct secasindex *);
-static void key_delsah (struct secashead *);
+static void key_unlink_sah(struct secashead *);
+static void key_destroy_sah(struct secashead *);
+static bool key_sah_has_sav(struct secashead *);
+static void key_sah_ref(struct secashead *);
+static void key_sah_unref(struct secashead *);
static struct secasvar *key_newsav(struct mbuf *,
const struct sadb_msghdr *, int *, const char*, int);
#define KEY_NEWSAV(m, sadb, e) \
key_newsav(m, sadb, e, __func__, __LINE__)
static void key_delsav (struct secasvar *);
static struct secashead *key_getsah(const struct secasindex *, int);
+static struct secashead *key_getsah_ref(const struct secasindex *, int);
static bool key_checkspidup(const struct secasindex *, u_int32_t);
static struct secasvar *key_getsavbyspi (struct secashead *, u_int32_t);
static int key_setsaval (struct secasvar *, struct mbuf *,
@@ -800,7 +829,7 @@ key_unlink_sp(struct secpolicy *sp)
#ifdef NET_MPSAFE
KASSERT(mutex_ownable(softnet_lock));
- pserialize_perform(key_psz);
+ pserialize_perform(key_spd_psz);
#endif
localcount_drain(&sp->localcount, &key_spd.cv, &key_spd.lock);
@@ -2954,9 +2983,13 @@ key_newsah(const struct secasindex *said
PSLIST_INIT(&newsah->savlist[i]);
newsah->saidx = *saidx;
- /* add to saidxtree */
- newsah->state = SADB_SASTATE_MATURE;
+ localcount_init(&newsah->localcount);
+ /* Take a reference for the caller */
+ localcount_acquire(&newsah->localcount);
+
+ /* Add to the sah list */
SAHLIST_ENTRY_INIT(newsah);
+ newsah->state = SADB_SASTATE_MATURE;
mutex_enter(&key_sad.lock);
SAHLIST_WRITER_INSERT_HEAD(newsah);
mutex_exit(&key_sad.lock);
@@ -2964,51 +2997,55 @@ key_newsah(const struct secasindex *said
return newsah;
}
-/*
- * delete SA index and all SA registerd.
- */
-static void
-key_delsah(struct secashead *sah)
+static bool
+key_sah_has_sav(struct secashead *sah)
{
- struct secasvar *sav;
u_int state;
- int s;
- int zombie = 0;
- KASSERT(!cpu_softintr_p());
- KASSERT(sah != NULL);
-
- s = splsoftnet();
+ KASSERT(mutex_owned(&key_sad.lock));
- /* searching all SA registerd in the secindex. */
SASTATE_ANY_FOREACH(state) {
- SAVLIST_READER_FOREACH(sav, sah, state) {
- /* give up to delete this sa */
- zombie++;
- }
+ if (!SAVLIST_WRITER_EMPTY(sah, state))
+ return true;
}
- /* don't delete sah only if there are savs. */
- if (zombie) {
- splx(s);
- return;
- }
+ return false;
+}
- rtcache_free(&sah->sa_route);
+static void
+key_unlink_sah(struct secashead *sah)
+{
- /* remove from tree of SA index */
+ KASSERT(!cpu_softintr_p());
+ KASSERT(mutex_owned(&key_sad.lock));
+ KASSERT(sah->state == SADB_SASTATE_DEAD);
+
+ /* Remove from the sah list */
SAHLIST_WRITER_REMOVE(sah);
+#ifdef NET_MPSAFE
+ KASSERT(mutex_ownable(softnet_lock));
+ pserialize_perform(key_sad_psz);
+#endif
+
+ localcount_drain(&sah->localcount, &key_sad.cv, &key_sad.lock);
+}
+
+static void
+key_destroy_sah(struct secashead *sah)
+{
+
+ rtcache_free(&sah->sa_route);
+
+ SAHLIST_ENTRY_DESTROY(sah);
+ localcount_fini(&sah->localcount);
+
if (sah->idents != NULL)
kmem_free(sah->idents, sah->idents_len);
if (sah->identd != NULL)
kmem_free(sah->identd, sah->identd_len);
- SAHLIST_ENTRY_DESTROY(sah);
kmem_free(sah, sizeof(*sah));
-
- splx(s);
- return;
}
/*
@@ -3136,7 +3173,32 @@ key_delsav(struct secasvar *sav)
}
/*
- * search SAD.
+ * Must be called in a pserialize read section. A held sah
+ * must be released by key_sah_unref after use.
+ */
+static void
+key_sah_ref(struct secashead *sah)
+{
+
+ localcount_acquire(&sah->localcount);
+}
+
+/*
+ * Must be called without holding key_sad.lock because the lock
+ * would be held in localcount_release.
+ */
+static void
+key_sah_unref(struct secashead *sah)
+{
+
+ KDASSERT(mutex_ownable(&key_sad.lock));
+
+ localcount_release(&sah->localcount, &key_sad.cv, &key_sad.lock);
+}
+
+/*
+ * Search SAD and return sah. Must be called in a pserialize
+ * read section.
* OUT:
* NULL : not found
* others : found, pointer to a SA.
@@ -3157,6 +3219,28 @@ key_getsah(const struct secasindex *said
}
/*
+ * Search SAD and return sah. If sah is returned, the caller must call
+ * key_sah_unref to releaset a reference.
+ * OUT:
+ * NULL : not found
+ * others : found, pointer to a SA.
+ */
+static struct secashead *
+key_getsah_ref(const struct secasindex *saidx, int flag)
+{
+ struct secashead *sah;
+ int s;
+
+ s = pserialize_read_enter();
+ sah = key_getsah(saidx, flag);
+ if (sah != NULL)
+ key_sah_ref(sah);
+ pserialize_read_exit(s);
+
+ return sah;
+}
+
+/*
* check not to be duplicated SPI.
* NOTE: this function is too slow due to searching all SAD.
* OUT:
@@ -3168,6 +3252,7 @@ key_checkspidup(const struct secasindex
{
struct secashead *sah;
struct secasvar *sav;
+ int s;
/* check address family */
if (saidx->src.sa.sa_family != saidx->dst.sa.sa_family) {
@@ -3176,15 +3261,18 @@ key_checkspidup(const struct secasindex
}
/* check all SAD */
+ s = pserialize_read_enter();
SAHLIST_READER_FOREACH(sah) {
if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst))
continue;
sav = key_getsavbyspi(sah, spi);
if (sav != NULL) {
+ pserialize_read_exit(s);
KEY_SA_UNREF(&sav);
return true;
}
}
+ pserialize_read_exit(s);
return false;
}
@@ -4594,15 +4682,28 @@ static void
key_timehandler_sad(time_t now)
{
struct secashead *sah;
- struct secasvar *sav;
+ int s;
restart:
+ mutex_enter(&key_sad.lock);
SAHLIST_WRITER_FOREACH(sah) {
- /* if sah has been dead, then delete it and process next sah. */
- if (sah->state == SADB_SASTATE_DEAD) {
- key_delsah(sah);
+ /* If sah has been dead and has no sav, then delete it */
+ if (sah->state == SADB_SASTATE_DEAD &&
+ !key_sah_has_sav(sah)) {
+ key_unlink_sah(sah);
+ mutex_exit(&key_sad.lock);
+ key_destroy_sah(sah);
goto restart;
}
+ }
+ mutex_exit(&key_sad.lock);
+
+ s = pserialize_read_enter();
+ SAHLIST_READER_FOREACH(sah) {
+ struct secasvar *sav;
+
+ key_sah_ref(sah);
+ pserialize_read_exit(s);
/* if LARVAL entry doesn't become MATURE, delete it. */
restart_sav_LARVAL:
@@ -4727,7 +4828,11 @@ restart:
* (such as from SPD).
*/
}
+
+ s = pserialize_read_enter();
+ key_sah_unref(sah);
}
+ pserialize_read_exit(s);
}
static void
@@ -4984,7 +5089,7 @@ key_api_getspi(struct socket *so, struct
return key_senderror(so, m, EINVAL);
/* get a SA index */
- sah = key_getsah(&saidx, CMP_REQID);
+ sah = key_getsah_ref(&saidx, CMP_REQID);
if (sah == NULL) {
/* create a new SA index */
sah = key_newsah(&saidx);
@@ -4998,6 +5103,7 @@ key_api_getspi(struct socket *so, struct
/* XXX rewrite */
newsav = KEY_NEWSAV(m, mhp, &error);
if (newsav == NULL) {
+ key_sah_unref(sah);
/* XXX don't free new SA index allocated in above. */
return key_senderror(so, m, error);
}
@@ -5015,6 +5121,8 @@ key_api_getspi(struct socket *so, struct
mutex_exit(&key_sad.lock);
key_validate_savlist(sah, SADB_SASTATE_LARVAL);
+ key_sah_unref(sah);
+
#ifndef IPSEC_NONBLOCK_ACQUIRE
/* delete the entry in key_misc.acqlist */
if (mhp->msg->sadb_msg_seq != 0) {
@@ -5362,7 +5470,7 @@ key_api_update(struct socket *so, struct
return key_senderror(so, m, EINVAL);
/* get a SA header */
- sah = key_getsah(&saidx, CMP_REQID);
+ sah = key_getsah_ref(&saidx, CMP_REQID);
if (sah == NULL) {
IPSECLOG(LOG_DEBUG, "no SA index found.\n");
return key_senderror(so, m, ENOENT);
@@ -5372,7 +5480,7 @@ key_api_update(struct socket *so, struct
/* XXX rewrite */
error = key_setident(sah, m, mhp);
if (error)
- return key_senderror(so, m, error);
+ goto error_sah;
/* find a SA with sequence number. */
#ifdef IPSEC_DOSEQCHECK
@@ -5382,7 +5490,8 @@ key_api_update(struct socket *so, struct
IPSECLOG(LOG_DEBUG,
"no larval SA with sequence %u exists.\n",
mhp->msg->sadb_msg_seq);
- return key_senderror(so, m, ENOENT);
+ error = ENOENT;
+ goto error_sah;
}
}
#else
@@ -5390,7 +5499,8 @@ key_api_update(struct socket *so, struct
if (sav == NULL) {
IPSECLOG(LOG_DEBUG, "no such a SA found (spi:%u)\n",
(u_int32_t)ntohl(sa0->sadb_sa_spi));
- return key_senderror(so, m, EINVAL);
+ error = EINVAL;
+ goto error_sah;
}
#endif
@@ -5461,6 +5571,9 @@ key_api_update(struct socket *so, struct
KEY_FREESAV(&sav);
KEY_FREESAV(&sav);
+ key_sah_unref(sah);
+ sah = NULL;
+
{
struct mbuf *n;
@@ -5476,6 +5589,8 @@ key_api_update(struct socket *so, struct
}
error:
KEY_SA_UNREF(&sav);
+error_sah:
+ key_sah_unref(sah);
return key_senderror(so, m, error);
}
@@ -5593,7 +5708,7 @@ key_api_add(struct socket *so, struct mb
return key_senderror(so, m, EINVAL);
/* get a SA header */
- sah = key_getsah(&saidx, CMP_REQID);
+ sah = key_getsah_ref(&saidx, CMP_REQID);
if (sah == NULL) {
/* create a new SA header */
sah = key_newsah(&saidx);
@@ -5606,9 +5721,8 @@ key_api_add(struct socket *so, struct mb
/* set spidx if there */
/* XXX rewrite */
error = key_setident(sah, m, mhp);
- if (error) {
- return key_senderror(so, m, error);
- }
+ if (error)
+ goto error;
{
struct secasvar *sav;
@@ -5618,27 +5732,28 @@ key_api_add(struct socket *so, struct mb
if (sav != NULL) {
KEY_SA_UNREF(&sav);
IPSECLOG(LOG_DEBUG, "SA already exists.\n");
- return key_senderror(so, m, EEXIST);
+ error = EEXIST;
+ goto error;
}
}
/* create new SA entry. */
newsav = KEY_NEWSAV(m, mhp, &error);
- if (newsav == NULL) {
- return key_senderror(so, m, error);
- }
+ if (newsav == NULL)
+ goto error;
newsav->sah = sah;
error = key_handle_natt_info(newsav, mhp);
if (error != 0) {
key_delsav(newsav);
- return key_senderror(so, m, EINVAL);
+ error = EINVAL;
+ goto error;
}
error = key_init_xform(newsav);
if (error != 0) {
key_delsav(newsav);
- return key_senderror(so, m, error);
+ goto error;
}
/* add to satree */
@@ -5650,6 +5765,9 @@ key_api_add(struct socket *so, struct mb
mutex_exit(&key_sad.lock);
key_validate_savlist(sah, SADB_SASTATE_MATURE);
+ key_sah_unref(sah);
+ sah = NULL;
+
/*
* don't call key_freesav() here, as we would like to keep the SA
* in the database on success.
@@ -5668,6 +5786,9 @@ key_api_add(struct socket *so, struct mb
m_freem(m);
return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
}
+error:
+ key_sah_unref(sah);
+ return key_senderror(so, m, error);
}
/* m is retained */
@@ -5853,10 +5974,11 @@ key_api_delete(struct socket *so, struct
return key_senderror(so, m, EINVAL);
/* get a SA header */
- sah = key_getsah(&saidx, CMP_HEAD);
+ sah = key_getsah_ref(&saidx, CMP_HEAD);
if (sah != NULL) {
/* get a SA with SPI. */
sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
+ key_sah_unref(sah);
}
if (sav == NULL) {
@@ -5911,7 +6033,7 @@ key_delete_all(struct socket *so, struct
if (error != 0)
return key_senderror(so, m, EINVAL);
- sah = key_getsah(&saidx, CMP_HEAD);
+ sah = key_getsah_ref(&saidx, CMP_HEAD);
if (sah != NULL) {
/* Delete all non-LARVAL SAs. */
SASTATE_ALIVE_FOREACH(state) {
@@ -5933,6 +6055,7 @@ key_delete_all(struct socket *so, struct
goto restart;
}
}
+ key_sah_unref(sah);
}
{
struct mbuf *n;
@@ -5971,7 +6094,6 @@ key_api_get(struct socket *so, struct mb
struct sadb_sa *sa0;
const struct sockaddr *src, *dst;
struct secasindex saidx;
- struct secashead *sah;
struct secasvar *sav = NULL;
u_int16_t proto;
int error;
@@ -6008,11 +6130,17 @@ key_api_get(struct socket *so, struct mb
return key_senderror(so, m, EINVAL);
/* get a SA header */
+ {
+ struct secashead *sah;
+ int s = pserialize_read_enter();
+
sah = key_getsah(&saidx, CMP_HEAD);
if (sah != NULL) {
/* get a SA with SPI. */
sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
}
+ pserialize_read_exit(s);
+ }
if (sav == NULL) {
IPSECLOG(LOG_DEBUG, "no SA found.\n");
return key_senderror(so, m, ENOENT);
@@ -6023,7 +6151,7 @@ key_api_get(struct socket *so, struct mb
u_int8_t satype;
/* map proto to satype */
- satype = key_proto2satype(sah->saidx.proto);
+ satype = key_proto2satype(sav->sah->saidx.proto);
if (satype == 0) {
KEY_SA_UNREF(&sav);
IPSECLOG(LOG_DEBUG, "there was invalid proto in SAD.\n");
@@ -6623,7 +6751,6 @@ key_api_acquire(struct socket *so, struc
{
const struct sockaddr *src, *dst;
struct secasindex saidx;
- struct secashead *sah;
u_int16_t proto;
int error;
@@ -6703,11 +6830,18 @@ key_api_acquire(struct socket *so, struc
return key_senderror(so, m, EINVAL);
/* get a SA index */
+ {
+ struct secashead *sah;
+ int s = pserialize_read_enter();
+
sah = key_getsah(&saidx, CMP_MODE_REQID);
if (sah != NULL) {
+ pserialize_read_exit(s);
IPSECLOG(LOG_DEBUG, "a SA exists already.\n");
return key_senderror(so, m, EEXIST);
}
+ pserialize_read_exit(s);
+ }
error = key_acquire(&saidx, NULL);
if (error != 0) {
@@ -7051,6 +7185,7 @@ key_api_flush(struct socket *so, struct
struct secasvar *sav;
u_int16_t proto;
u_int8_t state;
+ int s;
/* map satype to proto */
proto = key_satype2proto(mhp->msg->sadb_msg_satype);
@@ -7060,11 +7195,15 @@ key_api_flush(struct socket *so, struct
}
/* no SATYPE specified, i.e. flushing all SA. */
+ s = pserialize_read_enter();
SAHLIST_READER_FOREACH(sah) {
if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC &&
proto != sah->saidx.proto)
continue;
+ key_sah_ref(sah);
+ pserialize_read_exit(s);
+
SASTATE_ALIVE_FOREACH(state) {
restart:
SAVLIST_WRITER_FOREACH(sav, sah, state) {
@@ -7074,8 +7213,11 @@ key_api_flush(struct socket *so, struct
}
}
+ s = pserialize_read_enter();
sah->state = SADB_SASTATE_DEAD;
+ key_sah_unref(sah);
}
+ pserialize_read_exit(s);
if (m->m_len < sizeof(struct sadb_msg) ||
sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
@@ -7784,10 +7926,12 @@ key_do_init(void)
int i, error;
mutex_init(&key_misc.lock, MUTEX_DEFAULT, IPL_NONE);
- key_psz = pserialize_create();
+ key_spd_psz = pserialize_create();
mutex_init(&key_spd.lock, MUTEX_DEFAULT, IPL_NONE);
cv_init(&key_spd.cv, "key_sp");
+ key_sad_psz = pserialize_create();
mutex_init(&key_sad.lock, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&key_sad.cv, "key_sa");
pfkeystat_percpu = percpu_alloc(sizeof(uint64_t) * PFKEY_NSTATS);
@@ -7982,16 +8126,26 @@ void
key_sa_routechange(struct sockaddr *dst)
{
struct secashead *sah;
- struct route *ro;
- const struct sockaddr *sa;
+ int s;
+ s = pserialize_read_enter();
SAHLIST_READER_FOREACH(sah) {
+ struct route *ro;
+ const struct sockaddr *sa;
+
+ key_sah_ref(sah);
+ pserialize_read_exit(s);
+
ro = &sah->sa_route;
sa = rtcache_getdst(ro);
if (sa != NULL && dst->sa_len == sa->sa_len &&
memcmp(dst, sa, dst->sa_len) == 0)
rtcache_free(ro);
+
+ s = pserialize_read_enter();
+ key_sah_unref(sah);
}
+ pserialize_read_exit(s);
return;
}
Index: src/sys/netipsec/keydb.h
diff -u src/sys/netipsec/keydb.h:1.18 src/sys/netipsec/keydb.h:1.19
--- src/sys/netipsec/keydb.h:1.18 Mon Aug 7 03:21:59 2017
+++ src/sys/netipsec/keydb.h Tue Aug 8 04:17:34 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: keydb.h,v 1.18 2017/08/07 03:21:59 ozaki-r Exp $ */
+/* $NetBSD: keydb.h,v 1.19 2017/08/08 04:17:34 ozaki-r Exp $ */
/* $FreeBSD: src/sys/netipsec/keydb.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: keydb.h,v 1.14 2000/08/02 17:58:26 sakane Exp $ */
@@ -36,6 +36,8 @@
#ifdef _KERNEL
+#include <sys/localcount.h>
+
#include <netipsec/key_var.h>
#include <net/route.h>
#include <netinet/in.h>
@@ -66,6 +68,7 @@ struct secasindex {
/* Security Association Data Base */
struct secashead {
struct pslist_entry pslist_entry;
+ struct localcount localcount; /* reference count */
struct secasindex saidx;