Bring the PCB policy cache over from KAME IPsec, including the "hint"

used to short-circuit IPsec processing in other places.

This is enabled only for NetBSD at the moment; in order for it to function
correctly, ipsec_pcbconn() must be called as appropriate.
This commit is contained in:
thorpej 2004-03-02 02:22:56 +00:00
parent db4fcd885b
commit ce5ecc33b9
6 changed files with 286 additions and 34 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ipsec.c,v 1.8 2004/03/02 00:50:57 thorpej Exp $ */
/* $NetBSD: ipsec.c,v 1.9 2004/03/02 02:22:56 thorpej 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.8 2004/03/02 00:50:57 thorpej Exp $");
__KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.9 2004/03/02 02:22:56 thorpej Exp $");
/*
* IPsec controller part.
@ -118,6 +118,17 @@ int ip4_ah_net_deflev = IPSEC_LEVEL_USE;
struct secpolicy ip4_def_policy;
int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */
int ip4_esp_randpad = -1;
#ifdef __NetBSD__
u_int ipsec_spdgen = 1; /* SPD generation # */
static struct secpolicy *ipsec_checkpcbcache __P((struct mbuf *,
struct inpcbpolicy *, int));
static int ipsec_fillpcbcache __P((struct inpcbpolicy *, struct mbuf *,
struct secpolicy *, int));
static int ipsec_invalpcbcache __P((struct inpcbpolicy *, int));
#endif /* __NetBSD__ */
/*
* Crypto support requirements:
*
@ -218,6 +229,175 @@ static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp));
static void vshiftl __P((unsigned char *, int, int));
static size_t ipsec_hdrsiz __P((struct secpolicy *));
#ifdef __NetBSD__
/*
* Try to validate and use cached policy on a PCB.
*/
static struct secpolicy *
ipsec_checkpcbcache(struct mbuf *m, struct inpcbpolicy *pcbsp, int dir)
{
struct secpolicyindex spidx;
switch (dir) {
case IPSEC_DIR_INBOUND:
case IPSEC_DIR_OUTBOUND:
case IPSEC_DIR_ANY:
break;
default:
return NULL;
}
#ifdef DIAGNOSTIC
if (dir >= sizeof(pcbsp->sp_cache)/sizeof(pcbsp->sp_cache[0]))
panic("dir too big in ipsec_checkpcbcache");
#endif
/* SPD table change invalidate all the caches. */
if (ipsec_spdgen != pcbsp->sp_cache[dir].cachegen) {
ipsec_invalpcbcache(pcbsp, dir);
return NULL;
}
if (!pcbsp->sp_cache[dir].cachesp)
return NULL;
if (pcbsp->sp_cache[dir].cachesp->state != IPSEC_SPSTATE_ALIVE) {
ipsec_invalpcbcache(pcbsp, dir);
return NULL;
}
if ((pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {
if (!pcbsp->sp_cache[dir].cachesp)
return NULL;
if (ipsec_setspidx(m, &spidx, 1) != 0)
return NULL;
if (bcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx,
sizeof(spidx))) {
if (!key_cmpspidx_withmask(&pcbsp->sp_cache[dir].cachesp->spidx,
&spidx))
return NULL;
pcbsp->sp_cache[dir].cacheidx = spidx;
}
} else {
/*
* The pcb is connected, and the L4 code is sure that:
* - outgoing side uses inp_[lf]addr
* - incoming side looks up policy after inpcb lookup
* and address pair is know to be stable. We do not need
* to generate spidx again, nor check the address match again.
*
* For IPv4/v6 SOCK_STREAM sockets, this assumptions holds
* and there are calls to ipsec_pcbconn() from in_pcbconnect().
*/
}
pcbsp->sp_cache[dir].cachesp->lastused = mono_time.tv_sec;
pcbsp->sp_cache[dir].cachesp->refcnt++;
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP ipsec_checkpcbcache cause refcnt++:%d SP:%p\n",
pcbsp->sp_cache[dir].cachesp->refcnt,
pcbsp->sp_cache[dir].cachesp));
return pcbsp->sp_cache[dir].cachesp;
}
static int
ipsec_fillpcbcache(struct inpcbpolicy *pcbsp, struct mbuf *m,
struct secpolicy *sp, int dir)
{
if (sp != NULL && sp->policy == IPSEC_POLICY_ENTRUST)
panic("ipsec_fillpcbcache: ENTRUST");
switch (dir) {
case IPSEC_DIR_INBOUND:
case IPSEC_DIR_OUTBOUND:
break;
default:
return EINVAL;
}
#ifdef DIAGNOSTIC
if (dir >= sizeof(pcbsp->sp_cache)/sizeof(pcbsp->sp_cache[0]))
panic("dir too big in ipsec_fillpcbcache");
#endif
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_MAYBE;
if (ipsec_setspidx(m, &pcbsp->sp_cache[dir].cacheidx, 1) != 0) {
return EINVAL;
}
pcbsp->sp_cache[dir].cachesp = sp;
if (pcbsp->sp_cache[dir].cachesp) {
pcbsp->sp_cache[dir].cachesp->refcnt++;
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP ipsec_fillpcbcache cause refcnt++:%d SP:%p\n",
pcbsp->sp_cache[dir].cachesp->refcnt,
pcbsp->sp_cache[dir].cachesp));
/*
* If the PCB is connected, we can remember a hint to
* possibly short-circuit IPsec processing in other places.
*/
if (pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) {
switch (pcbsp->sp_cache[dir].cachesp->policy) {
case IPSEC_POLICY_NONE:
case IPSEC_POLICY_BYPASS:
pcbsp->sp_cache[dir].cachehint =
IPSEC_PCBHINT_NO;
break;
default:
pcbsp->sp_cache[dir].cachehint =
IPSEC_PCBHINT_YES;
}
}
}
pcbsp->sp_cache[dir].cachegen = ipsec_spdgen;
return 0;
}
static int
ipsec_invalpcbcache(struct inpcbpolicy *pcbsp, int dir)
{
int i;
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_MAYBE;
pcbsp->sp_cache[i].cachegen = 0;
bzero(&pcbsp->sp_cache[i].cacheidx,
sizeof(pcbsp->sp_cache[i].cacheidx));
}
return 0;
}
void
ipsec_pcbconn(struct inpcbpolicy *pcbsp)
{
pcbsp->sp_cacheflags |= IPSEC_PCBSP_CONNECTED;
ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
}
void
ipsec_pcbdisconn(struct inpcbpolicy *pcbsp)
{
pcbsp->sp_cacheflags &= ~IPSEC_PCBSP_CONNECTED;
ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
}
void
ipsec_invalpcbcacheall(void)
{
if (ipsec_spdgen == UINT_MAX)
ipsec_spdgen = 1;
else
ipsec_spdgen++;
}
#endif /* __NetBSD__ */
/*
* Return a held reference to the default SP.
*/
@ -304,7 +484,7 @@ ipsec_getpolicybysock(m, dir, inp, error)
IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
("ipsec_getpolicybysock: invalid direction %u", dir));
IPSEC_ASSERT(inp->inp_socket != NULL,
IPSEC_ASSERT(PCB_SOCKET(inp) != NULL,
("ipsec_getppolicybysock: null socket\n"));
/* XXX FIXME inpcb/in6pcb vs socket*/
@ -312,6 +492,17 @@ ipsec_getpolicybysock(m, dir, inp, error)
IPSEC_ASSERT(af == AF_INET || af == AF_INET6,
("ipsec_getpolicybysock: unexpected protocol family %u", af));
#ifdef __NetBSD__
/* If we have a cached entry, and if it is still valid, use it. */
ipsecstat.ips_spdcache_lookup++;
currsp = ipsec_checkpcbcache(m, /*inpcb_hdr*/inp->inph_sp, dir);
if (currsp) {
*error = 0;
return currsp;
}
ipsecstat.ips_spdcache_miss++;
#endif /* __NetBSD__ */
switch (af) {
case AF_INET: {
struct inpcb *in4p = PCB_TO_IN4PCB(inp);
@ -404,6 +595,9 @@ ipsec_getpolicybysock(m, dir, inp, error)
printf("DP ipsec_getpolicybysock (priv %u policy %u) allocates "
"SP:%p (refcnt %u)\n", pcbsp->priv, currsp->policy,
sp, sp->refcnt));
#ifdef __NetBSD__
ipsec_fillpcbcache(pcbsp, m, sp, dir);
#endif /* __NetBSD__ */
return sp;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: ipsec.h,v 1.6 2004/01/20 22:55:14 jonathan Exp $ */
/* $NetBSD: ipsec.h,v 1.7 2004/03/02 02:22:56 thorpej Exp $ */
/* $FreeBSD: src/sys/netipsec/ipsec.h,v 1.2.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipsec.h,v 1.53 2001/11/20 08:32:38 itojun Exp $ */
@ -117,8 +117,29 @@ struct inpcbpolicy {
struct secpolicy *sp_in;
struct secpolicy *sp_out;
int priv; /* privileged socket ? */
#ifdef __NetBSD__
/* cached policy */
struct {
struct secpolicy *cachesp;
struct secpolicyindex cacheidx;
int cachehint; /* processing requirement hint: */
#define IPSEC_PCBHINT_MAYBE 0 /* IPsec processing maybe required */
#define IPSEC_PCBHINT_YES 1 /* IPsec processing is required */
#define IPSEC_PCBHINT_NO 2 /* IPsec processing not required */
u_int cachegen; /* spdgen when cache filled */
} sp_cache[3]; /* XXX 3 == IPSEC_DIR_MAX */
int sp_cacheflags;
#define IPSEC_PCBSP_CONNECTED 1
#endif /* __NetBSD__ */
};
#ifdef __NetBSD__
#define IPSEC_PCB_SKIP_IPSEC(inpp, dir) \
((inpp)->sp_cache[(dir)].cachehint == IPSEC_PCBHINT_NO && \
(inpp)->sp_cache[(dir)].cachegen == ipsec_spdgen)
#endif /* __NetBSD__ */
/* SP acquiring list table. */
struct secspacq {
LIST_ENTRY(secspacq) chain;
@ -212,6 +233,11 @@ struct ipsecstat {
u_quad_t out_esphist[256];
u_quad_t out_ahhist[256];
u_quad_t out_comphist[256];
#ifdef __NetBSD__
u_quad_t spdcachelookup;
u_quad_t spdcachemiss;
#endif /* __NetBSD__ */
};
/* statistics for ipsec processing */
@ -234,6 +260,11 @@ struct newipsecstat {
u_int32_t ips_input_front;
u_int32_t ips_input_middle;
u_int32_t ips_input_end;
#ifdef __NetBSD__
u_quad_t ips_spdcache_lookup;
u_quad_t ips_spdcache_miss;
#endif /* __NetBSD__ */
};
/*
@ -333,6 +364,14 @@ extern int crypto_support;
/* for openbsd compatibility */
#define DPRINTF(x) do { if (ipsec_debug) printf x; } while (0)
#ifdef __NetBSD__
extern void ipsec_pcbconn __P((struct inpcbpolicy *));
extern void ipsec_pcbdisconn __P((struct inpcbpolicy *));
extern void ipsec_invalpcbcacheall __P((void));
extern u_int ipsec_spdgen;
#endif /* __NetBSD__ */
struct tdb_ident;
extern struct secpolicy *ipsec_getpolicy __P((struct tdb_ident*, u_int));
struct inpcb;
@ -355,8 +394,6 @@ ipsec_copy_pcbpolicy(struct inpcbpolicy *old, struct inpcbpolicy *new)
return (0);
}
struct inpcb;
#define ipsec_init_pcbpolicy ipsec_init_policy
extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **));

View File

@ -1,4 +1,4 @@
/* $NetBSD: ipsec_netbsd.c,v 1.5 2004/01/23 02:39:49 jonathan Exp $ */
/* $NetBSD: ipsec_netbsd.c,v 1.6 2004/03/02 02:22:56 thorpej Exp $ */
/* $KAME: esp_input.c,v 1.60 2001/09/04 08:43:19 itojun Exp $ */
/* $KAME: ah_input.c,v 1.64 2001/09/04 08:43:19 itojun Exp $ */
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ipsec_netbsd.c,v 1.5 2004/01/23 02:39:49 jonathan Exp $");
__KERNEL_RCSID(0, "$NetBSD: ipsec_netbsd.c,v 1.6 2004/03/02 02:22:56 thorpej Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
@ -306,15 +306,6 @@ esp6_ctlinput(cmd, sa, d)
}
#endif /* INET6 */
/*FIXME: placebo for invalpcbcacheall. Fast-IPsec has no pcb cache? */
void ipsec_invalpcbcacheall(void);
void
ipsec_invalpcbcacheall(void)
{
}
static int
sysctl_fast_ipsec(SYSCTLFN_ARGS)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: ipsec_osdep.h,v 1.7 2004/03/01 23:28:02 thorpej Exp $ */
/* $NetBSD: ipsec_osdep.h,v 1.8 2004/03/02 02:22:56 thorpej Exp $ */
/* $FreeBSD: /repoman/r/ncvs/src/sys/netipsec/ipsec_osdep.h,v 1.1 2003/09/29 22:47:45 sam Exp $ */
/*
@ -294,6 +294,9 @@ if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust)
* PCB_FAMILY(p): given a "generic" pcb_t p, returns the protocol
* family (AF_INET, AF_INET6) of the unperlying inpcb/in6pcb.
*
* PCB_SOCKET(p): given a "generic" pcb_t p, returns the associated
* socket pointer
*
* PCB_TO_IN4PCB(p): given generic pcb_t *p, returns a struct inpcb *
* PCB_TO_IN6PCB(p): given generic pcb_t *p, returns a struct in6pcb *
*
@ -303,6 +306,7 @@ if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust)
#ifdef __FreeBSD__
#define PCB_T struct inpcb
#define PCB_FAMILY(p) ((p)->inp_socket->so_proto->pr_domain->dom_family)
#define PCB_SOCKET(p) ((p)->inp_socket)
/* Convert generic pcb to IPv4/IPv6 pcb */
#define PCB_TO_IN4PCB(p) (p)
@ -317,9 +321,11 @@ if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust)
#ifndef notyet
# define PCB_T struct inpcb_hdr
# define PCB_FAMILY(p) ((p)->inph_af)
# define PCB_SOCKET(p) ((p)->inph_socket)
#else
# define PCB_T struct inpcb
# define PCB_FAMILY(p) ((p)->inp_head.inph_af)
# define PCB_SOCKET(p) ((p)->inp_socket)
#endif
#define PCB_TO_IN4PCB(p) ((struct inpcb *)(p))

View File

@ -1,4 +1,4 @@
/* $NetBSD: key.c,v 1.8 2004/03/01 18:33:03 thorpej Exp $ */
/* $NetBSD: key.c,v 1.9 2004/03/02 02:22:56 thorpej Exp $ */
/* $FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/netipsec/key.c,v 1.3.2.2 2003/07/01 01:38:13 sam 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.8 2004/03/01 18:33:03 thorpej Exp $");
__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.9 2004/03/02 02:22:56 thorpej Exp $");
/*
* This code is referd to RFC 2367
@ -430,10 +430,6 @@ static int key_ismyaddr6 __P((struct sockaddr_in6 *));
static int key_cmpsaidx
__P((const struct secasindex *, const struct secasindex *, int));
static int key_cmpspidx_exactly
__P((struct secpolicyindex *, struct secpolicyindex *));
static int key_cmpspidx_withmask
__P((struct secpolicyindex *, struct secpolicyindex *));
static int key_sockaddrcmp __P((const struct sockaddr *, const struct sockaddr *, int));
static int key_bbcmp __P((const void *, const void *, u_int));
static void key_srandom __P((void));
@ -1801,13 +1797,6 @@ key_spdadd(so, m, mhp)
}
}
#if defined(__NetBSD__) && defined(GATEWAY)
/*
* Nail the ipflow cache, since we're adding/changing an SPD
*/
ipflow_invalidate_all();
#endif
/* allocation new SP entry */
if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) {
return key_senderror(so, m, error);
@ -1876,6 +1865,16 @@ key_spdadd(so, m, mhp)
}
}
#if defined(__NetBSD__)
/* Invalidate all cached SPD pointers in the PCBs. */
ipsec_invalpcbcacheall();
#if defined(GATEWAY)
/* Invalidate the ipflow cache, as well. */
ipflow_invalidate_all();
#endif
#endif /* __NetBSD__ */
{
struct mbuf *n, *mpolicy;
struct sadb_msg *newmsg;
@ -2029,6 +2028,13 @@ key_spddelete(so, m, mhp)
sp->state = IPSEC_SPSTATE_DEAD;
KEY_FREESP(&sp);
#if defined(__NetBSD__)
/* Invalidate all cached SPD pointers in the PCBs. */
ipsec_invalpcbcacheall();
/* We're deleting policy; no need to invalidate the ipflow cache. */
#endif /* __NetBSD__ */
{
struct mbuf *n;
struct sadb_msg *newmsg;
@ -2091,6 +2097,13 @@ key_spddelete2(so, m, mhp)
sp->state = IPSEC_SPSTATE_DEAD;
KEY_FREESP(&sp);
#if defined(__NetBSD__)
/* Invalidate all cached SPD pointers in the PCBs. */
ipsec_invalpcbcacheall();
/* We're deleting policy; no need to invalidate the ipflow cache. */
#endif /* __NetBSD__ */
{
struct mbuf *n, *nn;
struct sadb_msg *newmsg;
@ -2300,6 +2313,13 @@ key_spdflush(so, m, mhp)
}
}
#if defined(__NetBSD__)
/* Invalidate all cached SPD pointers in the PCBs. */
ipsec_invalpcbcacheall();
/* We're deleting policy; no need to invalidate the ipflow cache. */
#endif /* __NetBSD__ */
if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
ipseclog((LOG_DEBUG, "key_spdflush: No more memory.\n"));
return key_senderror(so, m, ENOBUFS);
@ -3820,7 +3840,7 @@ key_cmpsaidx(
* 1 : equal
* 0 : not equal
*/
static int
int
key_cmpspidx_exactly(
struct secpolicyindex *spidx0,
struct secpolicyindex *spidx1)
@ -3850,7 +3870,7 @@ key_cmpspidx_exactly(
* 1 : equal
* 0 : not equal
*/
static int
int
key_cmpspidx_withmask(
struct secpolicyindex *spidx0,
struct secpolicyindex *spidx1)

View File

@ -1,4 +1,4 @@
/* $NetBSD: key.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $NetBSD: key.h,v 1.2 2004/03/02 02:22:56 thorpej 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 $ */
@ -96,6 +96,10 @@ extern struct secpolicy *key_msg2sp __P((struct sadb_x_policy *,
size_t, int *));
extern struct mbuf *key_sp2msg __P((struct secpolicy *));
extern int key_ismyaddr __P((struct sockaddr *));
extern int key_cmpspidx_exactly
__P((struct secpolicyindex *, struct secpolicyindex *));
extern int key_cmpspidx_withmask
__P((struct secpolicyindex *, struct secpolicyindex *));
extern int key_spdacquire __P((struct secpolicy *));
extern void key_timehandler __P((void*));
extern u_long key_random __P((void));