Subject: IPsec policy cache hint
To: None <tech-net@netbsd.org>
From: Jason Thorpe <thorpej@wasabisystems.com>
List: tech-net
Date: 02/27/2004 22:58:28
--Apple-Mail-21-860060980
Content-Type: multipart/mixed; boundary=Apple-Mail-20-860060964


--Apple-Mail-20-860060964
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

Hi folks...

For a project I am working on, I have a need to know if a TCP 
connection requires IPsec processing before the actual TCP segments are 
generated by tcp_output().  Unfortunately, you can't really do IPsec 
policy look-ups that early, because the IPsec code wants to have a 
fully-formed IP packet, and where I need to perform the test, I don't 
have that yet.

So, what I've done is added another field to the PCB's policy cache, a 
"hint" as to whether or not IPsec processing is going to be necessary.  
The hint has 3 values:

	MAYBE - maybe IPsec processing is required (we don't know yet)
	YES - IPsec processing is required
	NO - IPsec processing is not required

The hint is initialized to MAYBE, and reset to MAYBE whenever the PCB's 
policy cache is invalidated.  When the policy cache is initialized, if 
the policy is NONE or BYPASS, then the hint is set to NO, otherwise, it 
is set to YES.

This hint allows me to quickly perform the test I need to perform.  For 
a TCP connection, the hint will be initialized on the SYN or SYN,ACK, 
which is perfectly adequate for my application.

This also allows us to avoid a function call and other tests for the 
case of "no IPsec processing required" on connected sockets in 
ip_output().

Note that I have not actually tested this change yet.  I will do that 
maybe this weekend, or more likely early next week.  Note the patch 
also includes some cleanup to the PCB policy cache itself (turn 3 
arrays into one array of a struct).

If this works out, I am planning on pulling the PCB policy cache over 
into the FAST_IPSEC code, as well.

Comments?

         -- Jason R. Thorpe <thorpej@wasabisystems.com>

--Apple-Mail-20-860060964
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	x-unix-mode=0644;
	name="ipsec-hint-patch.txt"
Content-Disposition: attachment;
	filename=ipsec-hint-patch.txt

Index: netinet6/ipsec.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ipsec.c,v
retrieving revision 1.93
diff -u -r1.93 ipsec.c
--- netinet6/ipsec.c	24 Feb 2004 15:12:52 -0000	1.93
+++ netinet6/ipsec.c	28 Feb 2004 06:29:24 -0000
@@ -107,8 +107,6 @@
 struct secpolicy *ip4_def_policy;
 int ip4_ipsec_ecn = 0;		/* ECN ignore(-1)/forbidden(0)/allowed(1) */
 
-static int sp_cachegen = 1;	/* cache generation # */
-
 #ifdef INET6
 struct ipsecstat ipsec6stat;
 int ip6_esp_trans_deflev = IPSEC_LEVEL_USE;
@@ -120,6 +118,8 @@
 
 #endif /* INET6 */
 
+u_int ipsec_spdgen = 1;		/* SPD generation # */
+
 #ifdef SADB_X_EXT_TAG
 static struct pf_tag *ipsec_get_tag __P((struct mbuf *));
 #endif
@@ -193,31 +193,32 @@
 		return NULL;
 	}
 #ifdef DIAGNOSTIC
-	if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0]))
+	if (dir >= sizeof(pcbsp->sp_cache)/sizeof(pcbsp->sp_cache[0]))
 		panic("dir too big in ipsec_checkpcbcache");
 #endif
 	/* SPD table change invalidates all the caches */
-	if (pcbsp->cachegen[dir] == 0 || sp_cachegen > pcbsp->cachegen[dir]) {
+	if (ipsec_spdgen != pcbsp->sp_cache[dir].cachegen) {
 		ipsec_invalpcbcache(pcbsp, dir);
 		return NULL;
 	}
-	if (!pcbsp->cache[dir])
+	if (!pcbsp->sp_cache[dir].cachesp)
 		return NULL;
-	if (pcbsp->cache[dir]->state != IPSEC_SPSTATE_ALIVE) {
+	if (pcbsp->sp_cache[dir].cachesp->state != IPSEC_SPSTATE_ALIVE) {
 		ipsec_invalpcbcache(pcbsp, dir);
 		return NULL;
 	}
-	if ((pcbsp->cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {
-		if (!pcbsp->cache[dir])
+	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->cacheidx[dir], &spidx, sizeof(spidx))) {
-			if (!pcbsp->cache[dir]->spidx ||
-			    !key_cmpspidx_withmask(pcbsp->cache[dir]->spidx,
+		if (bcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx,
+			 sizeof(spidx))) {
+			if (!pcbsp->sp_cache[dir].cachesp->spidx ||
+			    !key_cmpspidx_withmask(pcbsp->sp_cache[dir].cachesp->spidx,
 			    &spidx))
 				return NULL;
-			pcbsp->cacheidx[dir] = spidx;
+			pcbsp->sp_cache[dir].cacheidx = spidx;
 		}
 	} else {
 		/*
@@ -232,12 +233,13 @@
 		 */
 	}
 
-	pcbsp->cache[dir]->lastused = mono_time.tv_sec;
-	pcbsp->cache[dir]->refcnt++;
+	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->cache[dir]->refcnt, pcbsp->cache[dir]));
-	return pcbsp->cache[dir];
+		pcbsp->sp_cache[dir].cachesp->refcnt,
+		pcbsp->sp_cache[dir].cachesp));
+	return pcbsp->sp_cache[dir].cachesp;
 }
 
 static int
@@ -256,24 +258,35 @@
 		return EINVAL;
 	}
 #ifdef DIAGNOSTIC
-	if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0]))
+	if (dir >= sizeof(pcbsp->sp_cache)/sizeof(pcbsp->sp_cache[0]))
 		panic("dir too big in ipsec_checkpcbcache");
 #endif
 
-	if (pcbsp->cache[dir])
-		key_freesp(pcbsp->cache[dir]);
-	pcbsp->cache[dir] = NULL;
-	if (ipsec_setspidx(m, &pcbsp->cacheidx[dir], 1) != 0) {
+	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->cache[dir] = sp;
-	if (pcbsp->cache[dir]) {
-		pcbsp->cache[dir]->refcnt++;
+	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->cache[dir]->refcnt, pcbsp->cache[dir]));
+			pcbsp->sp_cache[dir].cachesp->refcnt,
+			pcbsp->sp_cache[dir].cachesp));
+
+		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->cachegen[dir] = sp_cachegen;
+	pcbsp->sp_cache[dir].cachegen = ipsec_spdgen;
 
 	return 0;
 }
@@ -288,11 +301,13 @@
 	for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) {
 		if (dir != IPSEC_DIR_ANY && i != dir)
 			continue;
-		if (pcbsp->cache[i])
-			key_freesp(pcbsp->cache[i]);
-		pcbsp->cache[i] = NULL;
-		pcbsp->cachegen[i] = 0;
-		bzero(&pcbsp->cacheidx[i], sizeof(pcbsp->cacheidx[i]));
+		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;
 }
@@ -302,7 +317,7 @@
 	struct inpcbpolicy *pcbsp;
 {
 
-	pcbsp->cacheflags |= IPSEC_PCBSP_CONNECTED;
+	pcbsp->sp_cacheflags |= IPSEC_PCBSP_CONNECTED;
 	ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
 	return 0;
 }
@@ -312,17 +327,19 @@
 	struct inpcbpolicy *pcbsp;
 {
 
-	pcbsp->cacheflags &= ~IPSEC_PCBSP_CONNECTED;
+	pcbsp->sp_cacheflags &= ~IPSEC_PCBSP_CONNECTED;
 	ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
 	return 0;
 }
 
-int
+void
 ipsec_invalpcbcacheall()
 {
 
-	sp_cachegen++;
-	return 0;
+	if (ipsec_spdgen == UINT_MAX)
+		ipsec_spdgen = 1;
+	else
+		ipsec_spdgen++;
 }
 
 #ifdef SADB_X_EXT_TAG
Index: netinet6/ipsec.h
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ipsec.h,v
retrieving revision 1.41
diff -u -r1.41 ipsec.h
--- netinet6/ipsec.h	22 Sep 2003 04:47:44 -0000	1.41
+++ netinet6/ipsec.h	28 Feb 2004 06:29:24 -0000
@@ -127,14 +127,23 @@
 	int priv;			/* privileged socket ? */
 
 	/* cached policy */
-	/* XXX 3 == IPSEC_DIR_MAX */
-	struct secpolicy *cache[3];
-	struct secpolicyindex cacheidx[3];
-	int cachegen[3]; 	/* cache generation #, the time we filled it */
-	int cacheflags;
+	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
 };
 
+#define	IPSEC_PCB_SKIP_IPSEC(inpp, dir)					\
+	((inpp)->sp_cache[(dir)].cachehint == IPSEC_PCBHINT_NO &&	\
+	 (inpp)->sp_cache[(dir)].cachegen == ipsec_spdgen)
+
 /* SP acquiring list table. */
 struct secspacq {
 	LIST_ENTRY(secspacq) chain;
@@ -337,7 +346,9 @@
 
 extern int ipsec_pcbconn __P((struct inpcbpolicy *));
 extern int ipsec_pcbdisconn __P((struct inpcbpolicy *));
-extern int ipsec_invalpcbcacheall __P((void));
+extern void ipsec_invalpcbcacheall __P((void));
+
+extern u_int ipsec_spdgen;
 
 extern struct secpolicy *ipsec4_getpolicybysock
 	__P((struct mbuf *, u_int, struct socket *, int *));
Index: netinet/in_pcb_hdr.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_pcb_hdr.h,v
retrieving revision 1.2
diff -u -r1.2 in_pcb_hdr.h
--- netinet/in_pcb_hdr.h	28 Oct 2003 17:18:37 -0000	1.2
+++ netinet/in_pcb_hdr.h	28 Feb 2004 06:29:25 -0000
@@ -84,6 +84,8 @@
 #endif
 };
 
+#define	sotoinpcb_hdr(so)	((struct inpcb_hdr *)(so)->so_pcb)
+
 LIST_HEAD(inpcbhead, inpcb_hdr);
 
 struct inpcbtable {
Index: netinet/ip_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_output.c,v
retrieving revision 1.129
diff -u -r1.129 ip_output.c
--- netinet/ip_output.c	10 Dec 2003 11:46:33 -0000	1.129
+++ netinet/ip_output.c	28 Feb 2004 06:29:26 -0000
@@ -218,7 +218,7 @@
 		inp = (struct inpcb *)so->so_pcb;
 	else
 		inp = NULL;
-#endif /*IPSEC*/
+#endif /* FAST_IPSEC */
 
 #ifdef	DIAGNOSTIC
 	if ((m->m_flags & M_PKTHDR) == 0)
@@ -469,8 +469,12 @@
 	if (so == NULL)
 		sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND,
 		    flags, &error);
-	else
+	else {
+		if (IPSEC_PCB_SKIP_IPSEC(sotoinpcb_hdr(so)->inph_sp,
+					 IPSEC_DIR_OUTBOUND))
+			goto skip_ipsec;
 		sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error);
+	}
 
 	if (sp == NULL) {
 		ipsecstat.out_inval++;

--Apple-Mail-20-860060964--

--Apple-Mail-21-860060980
content-type: application/pgp-signature; x-mac-type=70674453;
	name=PGP.sig
content-description: This is a digitally signed message part
content-disposition: inline; filename=PGP.sig
content-transfer-encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (Darwin)

iD8DBQFAQDwbOpVKkaBm8XkRAvbQAKCneF7yYUGZ5UTAwvX94uV8WMEj7wCgkROo
AHKRi50mAUCL4zN/w5xbxFw=
=5Ok7
-----END PGP SIGNATURE-----

--Apple-Mail-21-860060980--