Module Name: src
Committed By: ozaki-r
Date: Wed Aug 2 01:28:03 UTC 2017
Modified Files:
src/sys/netinet6: ip6_forward.c ip6_output.c
src/sys/netipsec: ipsec.c ipsec.h key.c key.h xform_ah.c xform_esp.c
xform_ipcomp.c
src/sys/rump/librump/rumpnet: net_stub.c
Log Message:
Make IPsec SPD MP-safe
We use localcount(9), not psref(9), to make the sptree and secpolicy (SP)
entries MP-safe because SPs need to be referenced over opencrypto
processing that executes a callback in a different context.
SPs on sockets aren't managed by the sptree and can be destroyed in softint.
localcount_drain cannot be used in softint so we delay the destruction of
such SPs to a thread context. To do so, a list to manage such SPs is added
(key_socksplist) and key_timehandler_spd deletes dead SPs in the list.
For more details please read the locking notes in key.c.
Proposed on tech-kern@ and tech-net@
To generate a diff of this commit:
cvs rdiff -u -r1.87 -r1.88 src/sys/netinet6/ip6_forward.c
cvs rdiff -u -r1.192 -r1.193 src/sys/netinet6/ip6_output.c
cvs rdiff -u -r1.112 -r1.113 src/sys/netipsec/ipsec.c
cvs rdiff -u -r1.57 -r1.58 src/sys/netipsec/ipsec.h
cvs rdiff -u -r1.196 -r1.197 src/sys/netipsec/key.c
cvs rdiff -u -r1.25 -r1.26 src/sys/netipsec/key.h
cvs rdiff -u -r1.69 -r1.70 src/sys/netipsec/xform_ah.c
cvs rdiff -u -r1.67 -r1.68 src/sys/netipsec/xform_esp.c
cvs rdiff -u -r1.48 -r1.49 src/sys/netipsec/xform_ipcomp.c
cvs rdiff -u -r1.26 -r1.27 src/sys/rump/librump/rumpnet/net_stub.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/netinet6/ip6_forward.c
diff -u src/sys/netinet6/ip6_forward.c:1.87 src/sys/netinet6/ip6_forward.c:1.88
--- src/sys/netinet6/ip6_forward.c:1.87 Tue May 9 04:24:10 2017
+++ src/sys/netinet6/ip6_forward.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ip6_forward.c,v 1.87 2017/05/09 04:24:10 ozaki-r Exp $ */
+/* $NetBSD: ip6_forward.c,v 1.88 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $KAME: ip6_forward.c,v 1.109 2002/09/11 08:10:17 sakane Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_forward.c,v 1.87 2017/05/09 04:24:10 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_forward.c,v 1.88 2017/08/02 01:28:03 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_gateway.h"
@@ -462,7 +462,7 @@ ip6_forward(struct mbuf *m, int srcrt)
out:
#ifdef IPSEC
if (sp != NULL)
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
#endif
rtcache_unref(rt, ro);
if (ro != NULL)
Index: src/sys/netinet6/ip6_output.c
diff -u src/sys/netinet6/ip6_output.c:1.192 src/sys/netinet6/ip6_output.c:1.193
--- src/sys/netinet6/ip6_output.c:1.192 Mon Jun 26 08:01:53 2017
+++ src/sys/netinet6/ip6_output.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ip6_output.c,v 1.192 2017/06/26 08:01:53 ozaki-r Exp $ */
+/* $NetBSD: ip6_output.c,v 1.193 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.192 2017/06/26 08:01:53 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ip6_output.c,v 1.193 2017/08/02 01:28:03 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -1069,7 +1069,7 @@ done:
#ifdef IPSEC
if (sp != NULL)
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
#endif /* IPSEC */
if_put(ifp, &psref);
Index: src/sys/netipsec/ipsec.c
diff -u src/sys/netipsec/ipsec.c:1.112 src/sys/netipsec/ipsec.c:1.113
--- src/sys/netipsec/ipsec.c:1.112 Wed Jul 26 07:39:54 2017
+++ src/sys/netipsec/ipsec.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ipsec.c,v 1.112 2017/07/26 07:39:54 ozaki-r Exp $ */
+/* $NetBSD: ipsec.c,v 1.113 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/netipsec/ipsec.c,v 1.2.2.2 2003/07/01 01:38:13 sam Exp $ */
/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
@@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.112 2017/07/26 07:39:54 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.113 2017/08/02 01:28:03 ozaki-r Exp $");
/*
* IPsec controller part.
@@ -59,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.
#include <sys/kauth.h>
#include <sys/cpu.h>
#include <sys/kmem.h>
+#include <sys/pserialize.h>
#include <net/if.h>
#include <net/route.h>
@@ -201,6 +202,7 @@ static struct secpolicy *ipsec_deepcopy_
static int ipsec_set_policy (struct secpolicy **, int, const void *, size_t,
kauth_cred_t);
static int ipsec_get_policy (struct secpolicy *, struct mbuf **);
+static void ipsec_destroy_policy(struct secpolicy *);
static void vshiftl (unsigned char *, int, int);
static size_t ipsec_hdrsiz (const struct secpolicy *);
@@ -211,34 +213,49 @@ static struct secpolicy *
ipsec_checkpcbcache(struct mbuf *m, struct inpcbpolicy *pcbsp, int dir)
{
struct secpolicyindex spidx;
+ struct secpolicy *sp = NULL;
+ int s;
KASSERT(IPSEC_DIR_IS_VALID(dir));
KASSERT(pcbsp != NULL);
KASSERT(dir < __arraycount(pcbsp->sp_cache));
KASSERT(inph_locked(pcbsp->sp_inph));
+ /*
+ * Checking the generation and sp->state and taking a reference to an SP
+ * must be in a critical section of pserialize. See key_unlink_sp.
+ */
+ s = pserialize_read_enter();
/* SPD table change invalidate all the caches. */
if (ipsec_spdgen != pcbsp->sp_cache[dir].cachegen) {
ipsec_invalpcbcache(pcbsp, dir);
- return NULL;
+ goto out;
}
- if (!pcbsp->sp_cache[dir].cachesp)
- return NULL;
- if (pcbsp->sp_cache[dir].cachesp->state != IPSEC_SPSTATE_ALIVE) {
+ sp = pcbsp->sp_cache[dir].cachesp;
+ if (sp == NULL)
+ goto out;
+ if (sp->state != IPSEC_SPSTATE_ALIVE) {
+ sp = NULL;
ipsec_invalpcbcache(pcbsp, dir);
- return NULL;
+ goto out;
}
if ((pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {
- if (ipsec_setspidx(m, &spidx, 1) != 0)
- return NULL;
+ /* NB: assume ipsec_setspidx never sleep */
+ if (ipsec_setspidx(m, &spidx, 1) != 0) {
+ sp = NULL;
+ goto out;
+ }
/*
* We have to make an exact match here since the cached rule
* might have lower priority than a rule that would otherwise
* have matched the packet.
*/
- if (memcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx, sizeof(spidx)))
- return NULL;
+ if (memcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx,
+ sizeof(spidx))) {
+ sp = NULL;
+ goto out;
+ }
} else {
/*
* The pcb is connected, and the L4 code is sure that:
@@ -252,13 +269,14 @@ ipsec_checkpcbcache(struct mbuf *m, stru
*/
}
- pcbsp->sp_cache[dir].cachesp->lastused = time_second;
- KEY_SP_REF(pcbsp->sp_cache[dir].cachesp);
+ sp->lastused = time_second;
+ KEY_SP_REF(sp);
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
"DP cause refcnt++:%d SP:%p\n",
- key_sp_refcnt(pcbsp->sp_cache[dir].cachesp),
- pcbsp->sp_cache[dir].cachesp);
- return pcbsp->sp_cache[dir].cachesp;
+ key_sp_refcnt(sp), pcbsp->sp_cache[dir].cachesp);
+out:
+ pserialize_read_exit(s);
+ return sp;
}
static int
@@ -270,8 +288,6 @@ ipsec_fillpcbcache(struct inpcbpolicy *p
KASSERT(dir < __arraycount(pcbsp->sp_cache));
KASSERT(inph_locked(pcbsp->sp_inph));
- if (pcbsp->sp_cache[dir].cachesp)
- KEY_FREESP(&pcbsp->sp_cache[dir].cachesp);
pcbsp->sp_cache[dir].cachesp = NULL;
pcbsp->sp_cache[dir].cachehint = IPSEC_PCBHINT_UNKNOWN;
if (ipsec_setspidx(m, &pcbsp->sp_cache[dir].cacheidx, 1) != 0) {
@@ -279,7 +295,6 @@ ipsec_fillpcbcache(struct inpcbpolicy *p
}
pcbsp->sp_cache[dir].cachesp = sp;
if (pcbsp->sp_cache[dir].cachesp) {
- KEY_SP_REF(pcbsp->sp_cache[dir].cachesp);
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
"DP cause refcnt++:%d SP:%p\n",
key_sp_refcnt(pcbsp->sp_cache[dir].cachesp),
@@ -317,8 +332,6 @@ ipsec_invalpcbcache(struct inpcbpolicy *
for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) {
if (dir != IPSEC_DIR_ANY && i != dir)
continue;
- if (pcbsp->sp_cache[i].cachesp)
- KEY_FREESP(&pcbsp->sp_cache[i].cachesp);
pcbsp->sp_cache[i].cachesp = NULL;
pcbsp->sp_cache[i].cachehint = IPSEC_PCBHINT_UNKNOWN;
pcbsp->sp_cache[i].cachegen = 0;
@@ -609,7 +622,7 @@ ipsec4_checkpolicy(struct mbuf *m, u_int
break;
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
sp = NULL; /* NB: force NULL result */
break;
case IPSEC_POLICY_IPSEC:
@@ -617,7 +630,7 @@ ipsec4_checkpolicy(struct mbuf *m, u_int
break;
}
if (*error != 0) {
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
sp = NULL;
IPSECLOG(LOG_DEBUG, "done, error %d\n", *error);
}
@@ -697,7 +710,7 @@ ipsec4_output(struct mbuf *m, struct inp
*/
*mtu = _mtu;
*natt_frag = true;
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
splx(s);
return 0;
}
@@ -711,7 +724,7 @@ ipsec4_output(struct mbuf *m, struct inp
*/
if (error == ENOENT)
error = 0;
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
splx(s);
*done = true;
return error;
@@ -734,7 +747,7 @@ ipsec4_input(struct mbuf *m, int flags)
* Check security policy against packet attributes.
*/
error = ipsec_in_reject(sp, m);
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
splx(s);
if (error) {
return error;
@@ -753,7 +766,7 @@ ipsec4_input(struct mbuf *m, int flags)
sp = ipsec4_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags, &error, NULL);
if (sp != NULL) {
m->m_flags &= ~M_CANFASTFWD;
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
}
splx(s);
return 0;
@@ -802,7 +815,7 @@ ipsec4_forward(struct mbuf *m, int *dest
rtcache_unref(rt, ro);
KEY_FREESAV(&sav);
}
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
return 0;
}
@@ -838,7 +851,7 @@ ipsec6_checkpolicy(struct mbuf *m, u_int
break;
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
sp = NULL; /* NB: force NULL result */
break;
case IPSEC_POLICY_IPSEC:
@@ -846,7 +859,7 @@ ipsec6_checkpolicy(struct mbuf *m, u_int
break;
}
if (*error != 0) {
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
sp = NULL;
IPSECLOG(LOG_DEBUG, "done, error %d\n", *error);
}
@@ -1236,20 +1249,26 @@ ipsec_init_policy(struct socket *so, str
else
new->priv = 0;
+ /*
+ * These SPs are dummy. Never be used because the policy
+ * is ENTRUST. See ipsec_getpolicybysock.
+ */
if ((new->sp_in = KEY_NEWSP()) == NULL) {
ipsec_delpcbpolicy(new);
return ENOBUFS;
}
new->sp_in->state = IPSEC_SPSTATE_ALIVE;
new->sp_in->policy = IPSEC_POLICY_ENTRUST;
+ new->sp_in->created = 0; /* Indicates dummy */
if ((new->sp_out = KEY_NEWSP()) == NULL) {
- KEY_FREESP(&new->sp_in);
+ KEY_SP_UNREF(&new->sp_in);
ipsec_delpcbpolicy(new);
return ENOBUFS;
}
new->sp_out->state = IPSEC_SPSTATE_ALIVE;
new->sp_out->policy = IPSEC_POLICY_ENTRUST;
+ new->sp_out->created = 0; /* Indicates dummy */
*policy = new;
@@ -1264,14 +1283,14 @@ ipsec_copy_policy(const struct inpcbpoli
sp = ipsec_deepcopy_policy(old->sp_in);
if (sp) {
- KEY_FREESP(&new->sp_in);
+ KEY_SP_UNREF(&new->sp_in);
new->sp_in = sp;
} else
return ENOBUFS;
sp = ipsec_deepcopy_policy(old->sp_out);
if (sp) {
- KEY_FREESP(&new->sp_out);
+ KEY_SP_UNREF(&new->sp_out);
new->sp_out = sp;
} else
return ENOBUFS;
@@ -1326,6 +1345,23 @@ ipsec_deepcopy_policy(const struct secpo
return dst;
}
+static void
+ipsec_destroy_policy(struct secpolicy *sp)
+{
+
+ if (sp->created == 0)
+ /* It's dummy. We can simply free it */
+ key_free_sp(sp);
+ else {
+ /*
+ * We cannot destroy here because it can be called in
+ * softint. So mark the SP as DEAD and let the timer
+ * destroy it. See key_timehandler_spd.
+ */
+ sp->state = IPSEC_SPSTATE_DEAD;
+ }
+}
+
/* set policy and ipsec request if present. */
static int
ipsec_set_policy(
@@ -1337,7 +1373,7 @@ ipsec_set_policy(
)
{
const struct sadb_x_policy *xpl;
- struct secpolicy *newsp = NULL;
+ struct secpolicy *newsp = NULL, *oldsp;
int error;
KASSERT(!cpu_softintr_p());
@@ -1372,11 +1408,16 @@ ipsec_set_policy(
if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
return error;
- newsp->state = IPSEC_SPSTATE_ALIVE;
+ key_init_sp(newsp);
+ newsp->created = time_uptime;
+ /* Insert the global list for SPs for sockets */
+ key_socksplist_add(newsp);
/* clear old SP and set new SP */
- KEY_FREESP(policy);
+ oldsp = *policy;
*policy = newsp;
+ ipsec_destroy_policy(oldsp);
+
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
printf("%s: new policy\n", __func__);
kdebug_secpolicy(newsp);
@@ -1416,6 +1457,7 @@ ipsec4_set_policy(struct inpcb *inp, int
struct secpolicy **policy;
KASSERT(!cpu_softintr_p());
+ KASSERT(inp_locked(inp));
/* sanity check. */
if (inp == NULL || request == NULL)
@@ -1486,10 +1528,10 @@ ipsec4_delete_pcbpolicy(struct inpcb *in
return 0;
if (inp->inp_sp->sp_in != NULL)
- KEY_FREESP(&inp->inp_sp->sp_in);
+ ipsec_destroy_policy(inp->inp_sp->sp_in);
if (inp->inp_sp->sp_out != NULL)
- KEY_FREESP(&inp->inp_sp->sp_out);
+ ipsec_destroy_policy(inp->inp_sp->sp_out);
ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY);
@@ -1508,6 +1550,7 @@ ipsec6_set_policy(struct in6pcb *in6p, i
struct secpolicy **policy;
KASSERT(!cpu_softintr_p());
+ KASSERT(in6p_locked(in6p));
/* sanity check. */
if (in6p == NULL || request == NULL)
@@ -1575,10 +1618,10 @@ ipsec6_delete_pcbpolicy(struct in6pcb *i
return 0;
if (in6p->in6p_sp->sp_in != NULL)
- KEY_FREESP(&in6p->in6p_sp->sp_in);
+ ipsec_destroy_policy(in6p->in6p_sp->sp_in);
if (in6p->in6p_sp->sp_out != NULL)
- KEY_FREESP(&in6p->in6p_sp->sp_out);
+ ipsec_destroy_policy(in6p->in6p_sp->sp_out);
ipsec_invalpcbcache(in6p->in6p_sp, IPSEC_DIR_ANY);
@@ -1778,7 +1821,7 @@ ipsec4_in_reject(struct mbuf *m, struct
result = ipsec_in_reject(sp, m);
if (result)
IPSEC_STATINC(IPSEC_STAT_IN_POLVIO);
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
} else {
result = 0; /* XXX should be panic ?
* -> No, there may be error. */
@@ -1817,7 +1860,7 @@ ipsec6_in_reject(struct mbuf *m, struct
result = ipsec_in_reject(sp, m);
if (result)
IPSEC_STATINC(IPSEC_STAT_IN_POLVIO);
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
} else {
result = 0;
}
@@ -1929,7 +1972,7 @@ ipsec4_hdrsiz(struct mbuf *m, u_int dir,
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DATA, "size:%lu.\n",
(unsigned long)size);
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
} else {
size = 0; /* XXX should be panic ? */
}
@@ -1964,7 +2007,7 @@ ipsec6_hdrsiz(struct mbuf *m, u_int dir,
return 0;
size = ipsec_hdrsiz(sp);
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DATA, "size:%zu.\n", size);
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
return size;
}
@@ -2279,7 +2322,7 @@ ipsec6_input(struct mbuf *m)
* attributes.
*/
error = ipsec_in_reject(sp, m);
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
} else {
/* XXX error stat??? */
error = EINVAL;
Index: src/sys/netipsec/ipsec.h
diff -u src/sys/netipsec/ipsec.h:1.57 src/sys/netipsec/ipsec.h:1.58
--- src/sys/netipsec/ipsec.h:1.57 Wed Jul 26 09:18:15 2017
+++ src/sys/netipsec/ipsec.h Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ipsec.h,v 1.57 2017/07/26 09:18:15 ozaki-r Exp $ */
+/* $NetBSD: ipsec.h,v 1.58 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/netipsec/ipsec.h,v 1.2.4.2 2004/02/14 22:23:23 bms Exp $ */
/* $KAME: ipsec.h,v 1.53 2001/11/20 08:32:38 itojun Exp $ */
@@ -47,6 +47,7 @@
#ifdef _KERNEL
#include <sys/socketvar.h>
+#include <sys/localcount.h>
#include <netinet/in_pcb_hdr.h>
#include <netipsec/keydb.h>
@@ -76,7 +77,7 @@ struct secpolicyindex {
struct secpolicy {
struct pslist_entry pslist_entry;
- u_int refcnt; /* reference count */
+ struct localcount localcount; /* reference count */
struct secpolicyindex spidx; /* selector */
u_int32_t id; /* It's unique number on the system. */
u_int state; /* 0: dead, others: alive */
Index: src/sys/netipsec/key.c
diff -u src/sys/netipsec/key.c:1.196 src/sys/netipsec/key.c:1.197
--- src/sys/netipsec/key.c:1.196 Thu Jul 27 09:53:57 2017
+++ src/sys/netipsec/key.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: key.c,v 1.196 2017/07/27 09:53:57 ozaki-r Exp $ */
+/* $NetBSD: key.c,v 1.197 2017/08/02 01:28:03 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.196 2017/07/27 09:53:57 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.197 2017/08/02 01:28:03 ozaki-r Exp $");
/*
* This code is referd to RFC 2367
@@ -42,6 +42,7 @@ __KERNEL_RCSID(0, "$NetBSD: key.c,v 1.19
#include "opt_inet.h"
#include "opt_ipsec.h"
#include "opt_gateway.h"
+#include "opt_net_mpsafe.h"
#endif
#include <sys/types.h>
@@ -67,6 +68,10 @@ __KERNEL_RCSID(0, "$NetBSD: key.c,v 1.19
#include <sys/cpu.h>
#include <sys/atomic.h>
#include <sys/pslist.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/localcount.h>
+#include <sys/pserialize.h>
#include <net/if.h>
#include <net/route.h>
@@ -130,6 +135,50 @@ percpu_t *pfkeystat_percpu;
* field hits 0 (= no external reference other than from SA header.
*/
+/*
+ * Locking notes on SPD:
+ * - Modifications to the sptree must be done with holding key_sp_mtx
+ * which is a adaptive mutex
+ * - Read accesses to the sptree must be in critical sections of pserialize(9)
+ * - SP's lifetime is managed by localcount(9)
+ * - An SP that has been inserted to the sptree is initially referenced by none,
+ * i.e., a reference from the pstree isn't counted
+ * - When an SP is being destroyed, we change its state as DEAD, wait for
+ * references to the SP to be released, and then deallocate the SP
+ * (see key_unlink_sp)
+ * - Getting an SP
+ * - Normally we get an SP from the sptree by incrementing the reference count
+ * of the SP
+ * - We can gain another reference from a held SP only if we check its state
+ * and take its reference in a critical section of pserialize
+ * (see esp_output for example)
+ * - We may get an SP from an SP cache. See below
+ * - Updating member variables of an SP
+ * - Most member variables of an SP are immutable
+ * - Only sp->state and sp->lastused can be changed
+ * - sp->state of an SP is updated only when destroying it under key_sp_mtx
+ * - SP caches
+ * - SPs can be cached in PCBs
+ * - The lifetime of the caches is controlled by the global generation counter
+ * (ipsec_spdgen)
+ * - The global counter value is stored when an SP is cached
+ * - If the stored value is different from the global counter then the cache
+ * is considered invalidated
+ * - The counter is incremented when an SP is being destroyed
+ * - So checking the generation and taking a reference to an SP should be
+ * in a critical section of pserialize
+ * - Note that caching doesn't increment the reference counter of an SP
+ * - SPs in sockets
+ * - Userland programs can set a policy to a socket by
+ * setsockopt(IP_IPSEC_POLICY)
+ * - Such policies (SPs) are set to a socket (PCB) and also inserted to
+ * the key_socksplist list (not the sptree)
+ * - Such a policy is destroyed when a corresponding socket is destroed,
+ * however, a socket can be destroyed in softint so we cannot destroy
+ * it directly instead we just mark it DEAD and delay the destruction
+ * until GC by the timer
+ */
+
u_int32_t key_debug_level = 0;
static u_int key_spi_trycnt = 1000;
static u_int32_t key_spi_minval = 0x100;
@@ -195,9 +244,22 @@ static LIST_HEAD(_spacqtree, secspacq) s
} while (0)
/*
+ * The list has SPs that are set to a socket via setsockopt(IP_IPSEC_POLICY)
+ * from userland. See ipsec_set_policy.
+ */
+static struct pslist_head key_socksplist;
+
+#define SOCKSPLIST_WRITER_FOREACH(sp) \
+ PSLIST_WRITER_FOREACH((sp), &key_socksplist, struct secpolicy, \
+ pslist_entry)
+
+/*
* Protect regtree, acqtree and items stored in the lists.
*/
static kmutex_t key_mtx __cacheline_aligned;
+static pserialize_t key_psz;
+static kmutex_t key_sp_mtx __cacheline_aligned;
+static kcondvar_t key_sp_cv __cacheline_aligned;
/* search order for SAs */
/*
@@ -432,9 +494,11 @@ static struct secasvar *key_lookup_sa_by
static void key_freeso(struct socket *);
static void key_freesp_so(struct secpolicy **);
#endif
-static void key_delsp (struct secpolicy *);
static struct secpolicy *key_getsp (const struct secpolicyindex *);
static struct secpolicy *key_getspbyid (u_int32_t);
+static struct secpolicy *key_lookup_and_remove_sp(const struct secpolicyindex *);
+static struct secpolicy *key_lookupbyid_and_remove_sp(u_int32_t);
+static void key_destroy_sp(struct secpolicy *);
static u_int16_t key_newreqid (void);
static struct mbuf *key_gather_mbuf (struct mbuf *,
const struct sadb_msghdr *, int, int, ...);
@@ -582,8 +646,6 @@ static const char *key_getfqdn (void);
static const char *key_getuserfqdn (void);
#endif
static void key_sa_chgstate (struct secasvar *, u_int8_t);
-static inline void key_sp_dead (struct secpolicy *);
-static void key_sp_unlink (struct secpolicy *sp);
static struct mbuf *key_alloc_mbuf (int);
@@ -622,55 +684,37 @@ static struct work key_timehandler_wk;
REFLOG("SA_DELREF", (p), (where), (tag)); \
} while (0)
-#define SP_ADDREF(p) do { \
- atomic_inc_uint(&(p)->refcnt); \
- REFLOG("SP_ADDREF", (p), __func__, __LINE__); \
- KASSERTMSG((p)->refcnt != 0, "SP refcnt overflow"); \
-} while (0)
-#define SP_ADDREF2(p, where, tag) do { \
- atomic_inc_uint(&(p)->refcnt); \
- REFLOG("SP_ADDREF", (p), (where), (tag)); \
- KASSERTMSG((p)->refcnt != 0, "SP refcnt overflow"); \
-} while (0)
-#define SP_DELREF(p) do { \
- KASSERTMSG((p)->refcnt > 0, "SP refcnt underflow"); \
- atomic_dec_uint(&(p)->refcnt); \
- REFLOG("SP_DELREF", (p), __func__, __LINE__); \
-} while (0)
-#define SP_DELREF2(p, nv, where, tag) do { \
- KASSERTMSG((p)->refcnt > 0, "SP refcnt underflow"); \
- nv = atomic_dec_uint_nv(&(p)->refcnt); \
- REFLOG("SP_DELREF", (p), (where), (tag)); \
-} while (0)
-
u_int
key_sp_refcnt(const struct secpolicy *sp)
{
- if (sp == NULL)
- return 0;
-
- return sp->refcnt;
+ /* FIXME */
+ return 0;
}
-static inline void
-key_sp_dead(struct secpolicy *sp)
+/*
+ * Remove the sp from the sptree and wait for references to the sp
+ * to be released. key_sp_mtx must be held.
+ */
+static void
+key_unlink_sp(struct secpolicy *sp)
{
- /* mark the SP dead */
+ KASSERT(mutex_owned(&key_sp_mtx));
+
sp->state = IPSEC_SPSTATE_DEAD;
-}
+ SPLIST_WRITER_REMOVE(sp);
-static void
-key_sp_unlink(struct secpolicy *sp)
-{
+ /* Invalidate all cached SPD pointers in the PCBs. */
+ ipsec_invalpcbcacheall();
- /* remove from SP index */
- SPLIST_WRITER_REMOVE(sp);
- /* Release refcount held just for being on chain */
- KEY_FREESP(&sp);
-}
+#ifdef NET_MPSAFE
+ KASSERT(mutex_ownable(softnet_lock));
+ pserialize_perform(key_psz);
+#endif
+ localcount_drain(&sp->localcount, &key_sp_cv, &key_sp_mtx);
+}
/*
* Return 0 when there are known to be no SP's for the specified
@@ -704,12 +748,12 @@ key_lookup_sp_byspidx(const struct secpo
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP from %s:%u\n", where, tag);
/* get a SP entry */
- s = splsoftnet(); /*called from softclock()*/
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) {
printf("*** objects\n");
kdebug_secpolicyindex(spidx);
}
+ s = pserialize_read_enter();
SPLIST_READER_FOREACH(sp, dir) {
if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) {
printf("*** in SPD\n");
@@ -729,9 +773,9 @@ found:
/* found a SPD entry */
sp->lastused = time_uptime;
- SP_ADDREF2(sp, where, tag);
+ key_sp_ref(sp, where, tag);
}
- splx(s);
+ pserialize_read_exit(s);
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
"DP return SP:%p (ID=%u) refcnt %u\n",
@@ -765,7 +809,7 @@ key_gettunnel(const struct sockaddr *osr
goto done;
}
- s = splsoftnet(); /*called from softclock()*/
+ s = pserialize_read_enter();
SPLIST_READER_FOREACH(sp, dir) {
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
@@ -805,9 +849,9 @@ key_gettunnel(const struct sockaddr *osr
found:
if (sp) {
sp->lastused = time_uptime;
- SP_ADDREF2(sp, where, tag);
+ key_sp_ref(sp, where, tag);
}
- splx(s);
+ pserialize_read_exit(s);
done:
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
"DP return SP:%p (ID=%u) refcnt %u\n",
@@ -1154,49 +1198,51 @@ key_validate_savlist(const struct secash
}
void
+key_init_sp(struct secpolicy *sp)
+{
+
+ ASSERT_SLEEPABLE();
+
+ sp->state = IPSEC_SPSTATE_ALIVE;
+ if (sp->policy == IPSEC_POLICY_IPSEC)
+ KASSERT(sp->req != NULL);
+ localcount_init(&sp->localcount);
+ SPLIST_ENTRY_INIT(sp);
+}
+
+void
key_sp_ref(struct secpolicy *sp, const char* where, int tag)
{
- SP_ADDREF2(sp, where, tag);
+ localcount_acquire(&sp->localcount);
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
- "DP SP:%p (ID=%u) from %s:%u; refcnt now %u\n",
+ "DP SP:%p (ID=%u) from %s:%u; refcnt++ now %u\n",
sp, sp->id, where, tag, key_sp_refcnt(sp));
}
void
-key_sa_ref(struct secasvar *sav, const char* where, int tag)
+key_sp_unref(struct secpolicy *sp, const char* where, int tag)
{
- SA_ADDREF2(sav, where, tag);
+ KASSERT(mutex_ownable(&key_sp_mtx));
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
- "DP cause refcnt++:%d SA:%p from %s:%u\n",
- sav->refcnt, sav, where, tag);
+ "DP SP:%p (ID=%u) from %s:%u; refcnt-- now %u\n",
+ sp, sp->id, where, tag, key_sp_refcnt(sp));
+
+ localcount_release(&sp->localcount, &key_sp_cv, &key_sp_mtx);
}
-/*
- * Must be called after calling key_lookup_sp*().
- * For both the packet without socket and key_freeso().
- */
void
-_key_freesp(struct secpolicy **spp, const char* where, int tag)
+key_sa_ref(struct secasvar *sav, const char* where, int tag)
{
- struct secpolicy *sp = *spp;
- unsigned int nv;
- KASSERT(sp != NULL);
-
- SP_DELREF2(sp, nv, where, tag);
+ SA_ADDREF2(sav, where, tag);
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
- "DP SP:%p (ID=%u) from %s:%u; refcnt now %u\n",
- sp, sp->id, where, tag, nv);
-
- if (nv == 0) {
- *spp = NULL;
- key_delsp(sp);
- }
+ "DP cause refcnt++:%d SA:%p from %s:%u\n",
+ sav->refcnt, sav, where, tag);
}
#if 0
@@ -1270,7 +1316,7 @@ key_freesp_so(struct secpolicy **sp)
KASSERTMSG((*sp)->policy == IPSEC_POLICY_IPSEC,
"invalid policy %u", (*sp)->policy);
- KEY_FREESP(sp);
+ KEY_SP_UNREF(&sp);
}
#endif
@@ -1309,20 +1355,18 @@ key_freesav(struct secasvar **psav, cons
* free security policy entry.
*/
static void
-key_delsp(struct secpolicy *sp)
+key_destroy_sp(struct secpolicy *sp)
{
- int s;
-
- KASSERT(sp != NULL);
- key_sp_dead(sp);
-
- KASSERTMSG(sp->refcnt == 0,
- "SP with references deleted (refcnt %u)", sp->refcnt);
+ SPLIST_ENTRY_DESTROY(sp);
+ localcount_fini(&sp->localcount);
- s = splsoftnet(); /*called from softclock()*/
+ key_free_sp(sp);
+}
- {
+void
+key_free_sp(struct secpolicy *sp)
+{
struct ipsecrequest *isr = sp->req, *nextisr;
while (isr != NULL) {
@@ -1330,12 +1374,17 @@ key_delsp(struct secpolicy *sp)
kmem_intr_free(isr, sizeof(*isr));
isr = nextisr;
}
- }
- SPLIST_ENTRY_DESTROY(sp);
kmem_intr_free(sp, sizeof(*sp));
+}
- splx(s);
+void
+key_socksplist_add(struct secpolicy *sp)
+{
+
+ mutex_enter(&key_sp_mtx);
+ PSLIST_WRITER_INSERT_HEAD(&key_socksplist, sp, pslist_entry);
+ mutex_exit(&key_sp_mtx);
}
/*
@@ -1347,22 +1396,52 @@ static struct secpolicy *
key_getsp(const struct secpolicyindex *spidx)
{
struct secpolicy *sp;
+ int s;
KASSERT(spidx != NULL);
+ s = pserialize_read_enter();
SPLIST_READER_FOREACH(sp, spidx->dir) {
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
if (key_spidx_match_exactly(spidx, &sp->spidx)) {
- SP_ADDREF(sp);
+ KEY_SP_REF(sp);
+ pserialize_read_exit(s);
return sp;
}
}
+ pserialize_read_exit(s);
return NULL;
}
/*
+ * search SPD and remove found SP
+ * OUT: NULL : not found
+ * others : found, pointer to a SP.
+ */
+static struct secpolicy *
+key_lookup_and_remove_sp(const struct secpolicyindex *spidx)
+{
+ struct secpolicy *sp = NULL;
+
+ mutex_enter(&key_sp_mtx);
+ SPLIST_WRITER_FOREACH(sp, spidx->dir) {
+ KASSERT(sp->state != IPSEC_SPSTATE_DEAD);
+
+ if (key_spidx_match_exactly(spidx, &sp->spidx)) {
+ key_unlink_sp(sp);
+ goto out;
+ }
+ }
+ sp = NULL;
+out:
+ mutex_exit(&key_sp_mtx);
+
+ return sp;
+}
+
+/*
* get SP by index.
* OUT: NULL : not found
* others : found, pointer to a SP.
@@ -1371,13 +1450,15 @@ static struct secpolicy *
key_getspbyid(u_int32_t id)
{
struct secpolicy *sp;
+ int s;
+ s = pserialize_read_enter();
SPLIST_READER_FOREACH(sp, IPSEC_DIR_INBOUND) {
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
if (sp->id == id) {
- SP_ADDREF(sp);
- return sp;
+ KEY_SP_REF(sp);
+ goto out;
}
}
@@ -1385,12 +1466,42 @@ key_getspbyid(u_int32_t id)
if (sp->state == IPSEC_SPSTATE_DEAD)
continue;
if (sp->id == id) {
- SP_ADDREF(sp);
- return sp;
+ KEY_SP_REF(sp);
+ goto out;
}
}
+out:
+ pserialize_read_exit(s);
+ return sp;
+}
- return NULL;
+/*
+ * get SP by index, remove and return it.
+ * OUT: NULL : not found
+ * others : found, pointer to a SP.
+ */
+static struct secpolicy *
+key_lookupbyid_and_remove_sp(u_int32_t id)
+{
+ struct secpolicy *sp;
+
+ mutex_enter(&key_sp_mtx);
+ SPLIST_READER_FOREACH(sp, IPSEC_DIR_INBOUND) {
+ KASSERT(sp->state != IPSEC_SPSTATE_DEAD);
+ if (sp->id == id)
+ goto out;
+ }
+
+ SPLIST_READER_FOREACH(sp, IPSEC_DIR_OUTBOUND) {
+ KASSERT(sp->state != IPSEC_SPSTATE_DEAD);
+ if (sp->id == id)
+ goto out;
+ }
+out:
+ if (sp != NULL)
+ key_unlink_sp(sp);
+ mutex_exit(&key_sp_mtx);
+ return sp;
}
struct secpolicy *
@@ -1399,8 +1510,6 @@ key_newsp(const char* where, int tag)
struct secpolicy *newsp = NULL;
newsp = kmem_intr_zalloc(sizeof(struct secpolicy), KM_NOSLEEP);
- if (newsp != NULL)
- newsp->refcnt = 1;
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
"DP from %s:%u return SP:%p\n", where, tag, newsp);
@@ -1451,7 +1560,7 @@ key_msg2sp(const struct sadb_x_policy *x
break;
default:
IPSECLOG(LOG_DEBUG, "invalid policy type.\n");
- KEY_FREESP(&newsp);
+ key_free_sp(newsp);
*error = EINVAL;
return NULL;
}
@@ -1605,7 +1714,7 @@ key_msg2sp(const struct sadb_x_policy *x
return newsp;
free_exit:
- KEY_FREESP(&newsp);
+ key_free_sp(newsp);
return NULL;
}
@@ -1857,16 +1966,14 @@ key_api_spdadd(struct socket *so, struct
{
struct secpolicy *sp;
- sp = key_getsp(&spidx);
if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
- if (sp) {
- key_sp_dead(sp);
- key_sp_unlink(sp); /* XXX jrs ordering */
- KEY_FREESP(&sp);
- }
+ sp = key_lookup_and_remove_sp(&spidx);
+ if (sp != NULL)
+ key_destroy_sp(sp);
} else {
+ sp = key_getsp(&spidx);
if (sp != NULL) {
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
IPSECLOG(LOG_DEBUG, "a SP entry exists already.\n");
return key_senderror(so, m, EEXIST);
}
@@ -1891,12 +1998,11 @@ key_api_spdadd(struct socket *so, struct
newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0;
newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0;
- newsp->refcnt = 1; /* do not reclaim until I say I do */
- newsp->state = IPSEC_SPSTATE_ALIVE;
- if (newsp->policy == IPSEC_POLICY_IPSEC)
- KASSERT(newsp->req != NULL);
- SPLIST_ENTRY_INIT(newsp);
+ key_init_sp(newsp);
+
+ mutex_enter(&key_sp_mtx);
SPLIST_WRITER_INSERT_TAIL(newsp->spidx.dir, newsp);
+ mutex_exit(&key_sp_mtx);
#ifdef notyet
/* delete the entry in spacqtree */
@@ -1984,7 +2090,7 @@ key_getnewspid(void)
if (sp == NULL)
break;
- KEY_FREESP(&sp);
+ KEY_SP_UNREF(&sp);
}
if (count == 0 || newid == 0) {
@@ -2044,7 +2150,7 @@ key_api_spddelete(struct socket *so, str
key_init_spidx_bymsghdr(&spidx, mhp);
/* Is there SP in SPD ? */
- sp = key_getsp(&spidx);
+ sp = key_lookup_and_remove_sp(&spidx);
if (sp == NULL) {
IPSECLOG(LOG_DEBUG, "no SP found.\n");
return key_senderror(so, m, EINVAL);
@@ -2053,12 +2159,7 @@ key_api_spddelete(struct socket *so, str
/* save policy id to buffer to be returned. */
xpl0->sadb_x_policy_id = sp->id;
- key_sp_dead(sp);
- key_sp_unlink(sp); /* XXX jrs ordering */
- KEY_FREESP(&sp); /* ref gained by key_getspbyid */
-
- /* Invalidate all cached SPD pointers in the PCBs. */
- ipsec_invalpcbcacheall();
+ key_destroy_sp(sp);
/* We're deleting policy; no need to invalidate the ipflow cache. */
@@ -2109,19 +2210,13 @@ key_api_spddelete2(struct socket *so, st
id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
/* Is there SP in SPD ? */
- sp = key_getspbyid(id);
+ sp = key_lookupbyid_and_remove_sp(id);
if (sp == NULL) {
IPSECLOG(LOG_DEBUG, "no SP found id:%u.\n", id);
return key_senderror(so, m, EINVAL);
}
- key_sp_dead(sp);
- key_sp_unlink(sp); /* XXX jrs ordering */
- KEY_FREESP(&sp); /* ref gained by key_getsp */
- sp = NULL;
-
- /* Invalidate all cached SPD pointers in the PCBs. */
- ipsec_invalpcbcacheall();
+ key_destroy_sp(sp);
/* We're deleting policy; no need to invalidate the ipflow cache. */
@@ -2211,7 +2306,7 @@ key_api_spdget(struct socket *so, struct
n = key_setdumpsp(sp, SADB_X_SPDGET, mhp->msg->sadb_msg_seq,
mhp->msg->sadb_msg_pid);
- KEY_FREESP(&sp); /* ref gained by key_getspbyid */
+ KEY_SP_UNREF(&sp); /* ref gained by key_getspbyid */
if (n != NULL) {
m_freem(m);
return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
@@ -2317,18 +2412,17 @@ key_api_spdflush(struct socket *so, stru
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
retry:
+ mutex_enter(&key_sp_mtx);
SPLIST_WRITER_FOREACH(sp, dir) {
- if (sp->state == IPSEC_SPSTATE_DEAD)
- continue;
- key_sp_dead(sp);
- key_sp_unlink(sp);
+ KASSERT(sp->state != IPSEC_SPSTATE_DEAD);
+ key_unlink_sp(sp);
+ mutex_exit(&key_sp_mtx);
+ key_destroy_sp(sp);
goto retry;
}
+ mutex_exit(&key_sp_mtx);
}
- /* Invalidate all cached SPD pointers in the PCBs. */
- ipsec_invalpcbcacheall();
-
/* We're deleting policy; no need to invalidate the ipflow cache. */
if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
@@ -2361,12 +2455,14 @@ key_setspddump_chain(int *errorp, int *l
struct mbuf *m, *n, *prev;
int totlen;
+ KASSERT(mutex_owned(&key_sp_mtx));
+
*lenp = 0;
/* search SPD entry and get buffer size. */
cnt = 0;
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- SPLIST_READER_FOREACH(sp, dir) {
+ SPLIST_WRITER_FOREACH(sp, dir) {
cnt++;
}
}
@@ -2380,7 +2476,7 @@ key_setspddump_chain(int *errorp, int *l
prev = m;
totlen = 0;
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- SPLIST_READER_FOREACH(sp, dir) {
+ SPLIST_WRITER_FOREACH(sp, dir) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, pid);
@@ -2423,7 +2519,7 @@ key_api_spddump(struct socket *so, struc
{
struct mbuf *n;
int error, len;
- int ok, s;
+ int ok;
pid_t pid;
pid = mhp->msg->sadb_msg_pid;
@@ -2438,9 +2534,9 @@ key_api_spddump(struct socket *so, struc
return key_senderror(so, m0, ENOBUFS);
}
- s = splsoftnet();
+ mutex_enter(&key_sp_mtx);
n = key_setspddump_chain(&error, &len, pid);
- splx(s);
+ mutex_exit(&key_sp_mtx);
if (n == NULL) {
return key_senderror(so, m0, ENOENT);
@@ -4341,24 +4437,37 @@ key_timehandler_spd(time_t now)
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
retry:
+ mutex_enter(&key_sp_mtx);
SPLIST_WRITER_FOREACH(sp, dir) {
- if (sp->state == IPSEC_SPSTATE_DEAD) {
- key_sp_unlink(sp); /*XXX*/
- goto retry;
- }
+ KASSERT(sp->state != IPSEC_SPSTATE_DEAD);
if (sp->lifetime == 0 && sp->validtime == 0)
continue;
- /* the deletion will occur next time */
if ((sp->lifetime && now - sp->created > sp->lifetime) ||
(sp->validtime && now - sp->lastused > sp->validtime)) {
- key_sp_dead(sp);
+ key_unlink_sp(sp);
+ mutex_exit(&key_sp_mtx);
key_spdexpire(sp);
+ key_destroy_sp(sp);
goto retry;
}
}
+ mutex_exit(&key_sp_mtx);
}
+
+ retry_socksplist:
+ mutex_enter(&key_sp_mtx);
+ SOCKSPLIST_WRITER_FOREACH(sp) {
+ if (sp->state != IPSEC_SPSTATE_DEAD)
+ continue;
+
+ key_unlink_sp(sp);
+ mutex_exit(&key_sp_mtx);
+ key_destroy_sp(sp);
+ goto retry_socksplist;
+ }
+ mutex_exit(&key_sp_mtx);
}
static void
@@ -7537,6 +7646,9 @@ key_do_init(void)
int i, error;
mutex_init(&key_mtx, MUTEX_DEFAULT, IPL_NONE);
+ key_psz = pserialize_create();
+ mutex_init(&key_sp_mtx, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&key_sp_cv, "key_sp");
pfkeystat_percpu = percpu_alloc(sizeof(uint64_t) * PFKEY_NSTATS);
@@ -7550,6 +7662,8 @@ key_do_init(void)
PSLIST_INIT(&sptree[i]);
}
+ PSLIST_INIT(&key_socksplist);
+
LIST_INIT(&sahtree);
for (i = 0; i <= SADB_SATYPE_MAX; i++) {
@@ -7565,11 +7679,13 @@ key_do_init(void)
/* system default */
ip4_def_policy.policy = IPSEC_POLICY_NONE;
- ip4_def_policy.refcnt++; /*never reclaim this*/
+ ip4_def_policy.state = IPSEC_SPSTATE_ALIVE;
+ localcount_init(&ip4_def_policy.localcount);
#ifdef INET6
ip6_def_policy.policy = IPSEC_POLICY_NONE;
- ip6_def_policy.refcnt++; /*never reclaim this*/
+ ip6_def_policy.state = IPSEC_SPSTATE_ALIVE;
+ localcount_init(&ip6_def_policy.localcount);
#endif
callout_reset(&key_timehandler_ch, hz, key_timehandler, NULL);
@@ -7909,10 +8025,12 @@ key_setspddump(int *errorp, pid_t pid)
u_int dir;
struct mbuf *m, *n;
+ KASSERT(mutex_owned(&key_sp_mtx));
+
/* search SPD entry and get buffer size. */
cnt = 0;
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- SPLIST_READER_FOREACH(sp, dir) {
+ SPLIST_WRITER_FOREACH(sp, dir) {
cnt++;
}
}
@@ -7924,7 +8042,7 @@ key_setspddump(int *errorp, pid_t pid)
m = NULL;
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- SPLIST_READER_FOREACH(sp, dir) {
+ SPLIST_WRITER_FOREACH(sp, dir) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, pid);
@@ -8029,16 +8147,16 @@ sysctl_net_key_dumpsp(SYSCTLFN_ARGS)
int err2 = 0;
char *p, *ep;
size_t len;
- int s, error;
+ int error;
if (newp)
return (EPERM);
if (namelen != 0)
return (EINVAL);
- s = splsoftnet();
+ mutex_enter(&key_sp_mtx);
m = key_setspddump(&error, l->l_proc->p_pid);
- splx(s);
+ mutex_exit(&key_sp_mtx);
if (!m)
return (error);
if (!oldp)
Index: src/sys/netipsec/key.h
diff -u src/sys/netipsec/key.h:1.25 src/sys/netipsec/key.h:1.26
--- src/sys/netipsec/key.h:1.25 Wed Jul 26 03:59:59 2017
+++ src/sys/netipsec/key.h Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: key.h,v 1.25 2017/07/26 03:59:59 ozaki-r Exp $ */
+/* $NetBSD: key.h,v 1.26 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $FreeBSD: src/sys/netipsec/key.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: key.h,v 1.21 2001/07/27 03:51:30 itojun Exp $ */
@@ -55,11 +55,15 @@ struct secpolicy *key_gettunnel(const st
const struct sockaddr *, const struct sockaddr *,
const struct sockaddr *, const char*, int);
/* NB: prepend with _ for KAME IPv6 compatbility */
-void _key_freesp(struct secpolicy **, const char*, int);
+void key_init_sp(struct secpolicy *);
+void key_free_sp(struct secpolicy *);
u_int key_sp_refcnt(const struct secpolicy *);
void key_sp_ref(struct secpolicy *, const char*, int);
+void key_sp_unref(struct secpolicy *, const char*, int);
void key_sa_ref(struct secasvar *, const char*, int);
+void key_socksplist_add(struct secpolicy *);
+
/*
* Access to the SADB are interlocked with splsoftnet. In particular,
* holders of SA's use this to block accesses by protocol processing
@@ -73,8 +77,8 @@ void key_sa_ref(struct secasvar *, const
key_newsp(__func__, __LINE__)
#define KEY_GETTUNNEL(osrc, odst, isrc, idst) \
key_gettunnel(osrc, odst, isrc, idst, __func__, __LINE__)
-#define KEY_FREESP(spp) \
- _key_freesp(spp, __func__, __LINE__)
+#define KEY_SP_UNREF(spp) \
+ key_sp_unref(*(spp), __func__, __LINE__)
#define KEY_SP_REF(sp) \
key_sp_ref(sp, __func__, __LINE__)
#define KEY_SA_REF(sav) \
Index: src/sys/netipsec/xform_ah.c
diff -u src/sys/netipsec/xform_ah.c:1.69 src/sys/netipsec/xform_ah.c:1.70
--- src/sys/netipsec/xform_ah.c:1.69 Thu Jul 27 06:59:28 2017
+++ src/sys/netipsec/xform_ah.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: xform_ah.c,v 1.69 2017/07/27 06:59:28 ozaki-r Exp $ */
+/* $NetBSD: xform_ah.c,v 1.70 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $FreeBSD: src/sys/netipsec/xform_ah.c,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_ah.c,v 1.63 2001/06/26 06:18:58 angelos Exp $ */
/*
@@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xform_ah.c,v 1.69 2017/07/27 06:59:28 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xform_ah.c,v 1.70 2017/08/02 01:28:03 ozaki-r Exp $");
#if defined(_KERNEL_OPT)
#include "opt_inet.h"
@@ -54,6 +54,7 @@ __KERNEL_RCSID(0, "$NetBSD: xform_ah.c,v
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
+#include <sys/pserialize.h>
#include <net/if.h>
@@ -1140,6 +1141,21 @@ ah_output(
goto bad;
}
+ {
+ int s = pserialize_read_enter();
+
+ if (__predict_false(isr->sp->state == IPSEC_SPSTATE_DEAD)) {
+ pserialize_read_exit(s);
+ pool_put(&ah_tdb_crypto_pool, tc);
+ crypto_freereq(crp);
+ AH_STATINC(AH_STAT_NOTDB);
+ error = ENOENT;
+ goto bad;
+ }
+ KEY_SP_REF(isr->sp);
+ pserialize_read_exit(s);
+ }
+
/* Crypto operation descriptor. */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
crp->crp_flags = CRYPTO_F_IMBUF;
@@ -1150,7 +1166,6 @@ ah_output(
/* These are passed as-is to the callback. */
tc->tc_isr = isr;
- KEY_SP_REF(isr->sp);
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
@@ -1255,13 +1270,13 @@ ah_output_cb(struct cryptop *crp)
/* NB: m is reclaimed by ipsec_process_done. */
err = ipsec_process_done(m, isr, sav);
KEY_FREESAV(&sav);
- KEY_FREESP(&isr->sp);
+ KEY_SP_UNREF(&isr->sp);
IPSEC_RELEASE_GLOBAL_LOCKS();
return err;
bad:
if (sav)
KEY_FREESAV(&sav);
- KEY_FREESP(&isr->sp);
+ KEY_SP_UNREF(&isr->sp);
IPSEC_RELEASE_GLOBAL_LOCKS();
if (m)
m_freem(m);
Index: src/sys/netipsec/xform_esp.c
diff -u src/sys/netipsec/xform_esp.c:1.67 src/sys/netipsec/xform_esp.c:1.68
--- src/sys/netipsec/xform_esp.c:1.67 Thu Jul 27 06:59:28 2017
+++ src/sys/netipsec/xform_esp.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: xform_esp.c,v 1.67 2017/07/27 06:59:28 ozaki-r Exp $ */
+/* $NetBSD: xform_esp.c,v 1.68 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $FreeBSD: src/sys/netipsec/xform_esp.c,v 1.2.2.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_esp.c,v 1.69 2001/06/26 06:18:59 angelos Exp $ */
@@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xform_esp.c,v 1.67 2017/07/27 06:59:28 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xform_esp.c,v 1.68 2017/08/02 01:28:03 ozaki-r Exp $");
#if defined(_KERNEL_OPT)
#include "opt_inet.h"
@@ -55,6 +55,7 @@ __KERNEL_RCSID(0, "$NetBSD: xform_esp.c,
#include <sys/sysctl.h>
#include <sys/cprng.h>
#include <sys/pool.h>
+#include <sys/pserialize.h>
#include <net/if.h>
@@ -897,9 +898,23 @@ esp_output(
goto bad;
}
+ {
+ int s = pserialize_read_enter();
+
+ if (__predict_false(isr->sp->state == IPSEC_SPSTATE_DEAD)) {
+ pserialize_read_exit(s);
+ pool_put(&esp_tdb_crypto_pool, tc);
+ crypto_freereq(crp);
+ ESP_STATINC(ESP_STAT_NOTDB);
+ error = ENOENT;
+ goto bad;
+ }
+ KEY_SP_REF(isr->sp);
+ pserialize_read_exit(s);
+ }
+
/* Callback parameters */
tc->tc_isr = isr;
- KEY_SP_REF(isr->sp);
tc->tc_spi = sav->spi;
tc->tc_dst = saidx->dst;
tc->tc_proto = saidx->proto;
@@ -1032,13 +1047,13 @@ esp_output_cb(struct cryptop *crp)
/* NB: m is reclaimed by ipsec_process_done. */
err = ipsec_process_done(m, isr, sav);
KEY_FREESAV(&sav);
- KEY_FREESP(&isr->sp);
+ KEY_SP_UNREF(&isr->sp);
IPSEC_RELEASE_GLOBAL_LOCKS();
return err;
bad:
if (sav)
KEY_FREESAV(&sav);
- KEY_FREESP(&isr->sp);
+ KEY_SP_UNREF(&isr->sp);
IPSEC_RELEASE_GLOBAL_LOCKS();
if (m)
m_freem(m);
Index: src/sys/netipsec/xform_ipcomp.c
diff -u src/sys/netipsec/xform_ipcomp.c:1.48 src/sys/netipsec/xform_ipcomp.c:1.49
--- src/sys/netipsec/xform_ipcomp.c:1.48 Thu Jul 27 06:59:28 2017
+++ src/sys/netipsec/xform_ipcomp.c Wed Aug 2 01:28:03 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: xform_ipcomp.c,v 1.48 2017/07/27 06:59:28 ozaki-r Exp $ */
+/* $NetBSD: xform_ipcomp.c,v 1.49 2017/08/02 01:28:03 ozaki-r Exp $ */
/* $FreeBSD: src/sys/netipsec/xform_ipcomp.c,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_ipcomp.c,v 1.1 2001/07/05 12:08:52 jjbg Exp $ */
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xform_ipcomp.c,v 1.48 2017/07/27 06:59:28 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xform_ipcomp.c,v 1.49 2017/08/02 01:28:03 ozaki-r Exp $");
/* IP payload compression protocol (IPComp), see RFC 2393 */
#if defined(_KERNEL_OPT)
@@ -45,6 +45,7 @@ __KERNEL_RCSID(0, "$NetBSD: xform_ipcomp
#include <sys/protosw.h>
#include <sys/sysctl.h>
#include <sys/pool.h>
+#include <sys/pserialize.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -479,8 +480,22 @@ ipcomp_output(
goto bad;
}
- tc->tc_isr = isr;
+ {
+ int s = pserialize_read_enter();
+
+ if (__predict_false(isr->sp->state == IPSEC_SPSTATE_DEAD)) {
+ pserialize_read_exit(s);
+ pool_put(&ipcomp_tdb_crypto_pool, tc);
+ crypto_freereq(crp);
+ IPCOMP_STATINC(IPCOMP_STAT_NOTDB);
+ error = ENOENT;
+ goto bad;
+ }
KEY_SP_REF(isr->sp);
+ pserialize_read_exit(s);
+ }
+
+ tc->tc_isr = isr;
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
@@ -646,13 +661,13 @@ ipcomp_output_cb(struct cryptop *crp)
/* NB: m is reclaimed by ipsec_process_done. */
error = ipsec_process_done(m, isr, sav);
KEY_FREESAV(&sav);
- KEY_FREESP(&isr->sp);
+ KEY_SP_UNREF(&isr->sp);
IPSEC_RELEASE_GLOBAL_LOCKS();
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
- KEY_FREESP(&isr->sp);
+ KEY_SP_UNREF(&isr->sp);
IPSEC_RELEASE_GLOBAL_LOCKS();
if (m)
m_freem(m);
Index: src/sys/rump/librump/rumpnet/net_stub.c
diff -u src/sys/rump/librump/rumpnet/net_stub.c:1.26 src/sys/rump/librump/rumpnet/net_stub.c:1.27
--- src/sys/rump/librump/rumpnet/net_stub.c:1.26 Fri Apr 14 02:43:28 2017
+++ src/sys/rump/librump/rumpnet/net_stub.c Wed Aug 2 01:28:02 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: net_stub.c,v 1.26 2017/04/14 02:43:28 ozaki-r Exp $ */
+/* $NetBSD: net_stub.c,v 1.27 2017/08/02 01:28:02 ozaki-r Exp $ */
/*
* Copyright (c) 2008 Antti Kantee. All Rights Reserved.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: net_stub.c,v 1.26 2017/04/14 02:43:28 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: net_stub.c,v 1.27 2017/08/02 01:28:02 ozaki-r Exp $");
#include <sys/mutex.h>
#include <sys/param.h>
@@ -108,7 +108,7 @@ __weak_alias(ipsec_init_policy,rumpnet_s
__weak_alias(ipsec_pcbconn,rumpnet_stub);
__weak_alias(ipsec_pcbdisconn,rumpnet_stub);
__weak_alias(key_sa_routechange,rumpnet_stub);
-__weak_alias(_key_freesp,rumpnet_stub);
+__weak_alias(key_sp_unref,rumpnet_stub);
struct ifnet_head ifnet_list;
struct pslist_head ifnet_pslist;