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@
This commit is contained in:
ozaki-r 2017-08-02 01:28:02 +00:00
parent 02e8a058e6
commit 0c084e85e9
10 changed files with 440 additions and 229 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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.112 2017/07/26 07:39:54 ozaki-r Exp $");
#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_policy (const struct secpolicy *);
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, struct inpcbpolicy *pcbsp, int dir)
*/
}
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 *pcbsp, struct mbuf *m,
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 *pcbsp, struct mbuf *m,
}
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 *pcbsp, int dir)
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 dir, u_int flag, int *error,
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 dir, u_int flag, int *error,
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 inpcb *inp, int flags,
*/
*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 inpcb *inp, int flags,
*/
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 *destmtu)
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 dir, u_int flag, int *error,
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 dir, u_int flag, int *error,
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, struct inpcbpolicy **policy)
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 inpcbpolicy *old, struct inpcbpolicy *new)
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 secpolicy *src)
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 optname, const void *request,
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 *inp)
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, int optname, const void *request,
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 *in6p)
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 inpcb *inp)
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 in6pcb *in6p)
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, struct inpcb *inp)
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, struct in6pcb *in6p)
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;

View File

@ -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 */

View File

@ -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.196 2017/07/27 09:53:57 ozaki-r Exp $");
#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.196 2017/07/27 09:53:57 ozaki-r Exp $");
#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;
@ -194,10 +243,23 @@ static LIST_HEAD(_spacqtree, secspacq) spacqtree; /* SP acquiring list */
} \
} 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_bysaidx(const struct secasindex *);
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)
/* FIXME */
return 0;
return sp->refcnt;
}
static inline void
key_sp_dead(struct secpolicy *sp)
{
/* mark the SP dead */
sp->state = IPSEC_SPSTATE_DEAD;
}
/*
* 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_sp_unlink(struct secpolicy *sp)
key_unlink_sp(struct secpolicy *sp)
{
/* remove from SP index */
SPLIST_WRITER_REMOVE(sp);
/* Release refcount held just for being on chain */
KEY_FREESP(&sp);
}
KASSERT(mutex_owned(&key_sp_mtx));
sp->state = IPSEC_SPSTATE_DEAD;
SPLIST_WRITER_REMOVE(sp);
/* Invalidate all cached SPD pointers in the PCBs. */
ipsec_invalpcbcacheall();
#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 secpolicyindex *spidx,
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 *osrc,
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 *osrc,
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",
@ -1153,17 +1197,43 @@ key_validate_savlist(const struct secashead *sah, const u_int state)
#endif
}
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_sp_unref(struct secpolicy *sp, const char* where, int tag)
{
KASSERT(mutex_ownable(&key_sp_mtx));
KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
"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);
}
void
key_sa_ref(struct secasvar *sav, const char* where, int tag)
{
@ -1175,30 +1245,6 @@ key_sa_ref(struct secasvar *sav, const char* where, int tag)
sav->refcnt, sav, where, tag);
}
/*
* 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)
{
struct secpolicy *sp = *spp;
unsigned int nv;
KASSERT(sp != NULL);
SP_DELREF2(sp, nv, 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);
}
}
#if 0
/*
* Must be called after calling key_lookup_sp*().
@ -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,19 +1355,17 @@ key_freesav(struct secasvar **psav, const char* where, int tag)
* free security policy entry.
*/
static void
key_delsp(struct secpolicy *sp)
key_destroy_sp(struct secpolicy *sp)
{
int s;
KASSERT(sp != NULL);
SPLIST_ENTRY_DESTROY(sp);
localcount_fini(&sp->localcount);
key_sp_dead(sp);
KASSERTMSG(sp->refcnt == 0,
"SP with references deleted (refcnt %u)", sp->refcnt);
s = splsoftnet(); /*called from softclock()*/
key_free_sp(sp);
}
void
key_free_sp(struct secpolicy *sp)
{
struct ipsecrequest *isr = sp->req, *nextisr;
@ -1330,12 +1374,17 @@ key_delsp(struct secpolicy *sp)
kmem_intr_free(isr, sizeof(*isr));
isr = nextisr;
}
kmem_intr_free(sp, sizeof(*sp));
}
SPLIST_ENTRY_DESTROY(sp);
kmem_intr_free(sp, sizeof(*sp));
void
key_socksplist_add(struct secpolicy *sp)
{
splx(s);
mutex_enter(&key_sp_mtx);
PSLIST_WRITER_INSERT_HEAD(&key_socksplist, sp, pslist_entry);
mutex_exit(&key_sp_mtx);
}
/*
@ -1347,21 +1396,51 @@ 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
@ -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);
KEY_SP_REF(sp);
goto out;
}
}
out:
pserialize_read_exit(s);
return sp;
}
/*
* 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;
}
return NULL;
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 *xpl0, size_t len, int *error)
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 *xpl0, size_t len, int *error)
return newsp;
free_exit:
KEY_FREESP(&newsp);
key_free_sp(newsp);
return NULL;
}
@ -1857,16 +1966,14 @@ key_api_spdadd(struct socket *so, struct mbuf *m,
{
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 mbuf *m,
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, struct mbuf *m,
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, struct mbuf *m,
/* 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, struct mbuf *m,
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 mbuf *m,
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, struct mbuf *m,
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 *lenp, pid_t pid)
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 *lenp, pid_t pid)
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, struct mbuf *m0,
{
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, struct mbuf *m0,
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)

View File

@ -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 struct sockaddr *,
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 char*, int);
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) \

View File

@ -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 1.69 2017/07/27 06:59:28 ozaki-r Exp $"
#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);

View File

@ -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,v 1.67 2017/07/27 06:59:28 ozaki-r Exp $
#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);

View File

@ -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.c,v 1.48 2017/07/27 06:59:28 ozaki-r Ex
#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);

View File

@ -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_stub);
__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;