Subject: Re: IPSEC in GENERIC
To: Jason Thorpe <thorpej@shagadelic.org>
From: Christos Zoulas <christos@zoulas.com>
List: tech-kern
Date: 02/21/2006 21:08:43
On Feb 21,  3:50pm, thorpej@shagadelic.org (Jason Thorpe) wrote:
-- Subject: Re: IPSEC in GENERIC

Here's a patch to provide a fast path for both IPSEC and FAST_IPSEC.
The IPSEC code works fine. The FAST_IPSEC prints errors, but the code
has been rotting; i.e. it does not even compile with other IPSEC options
such as IPSEC_NAT_T.

christos

Index: netinet/in_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_pcb.c,v
retrieving revision 1.101
diff -u -r1.101 in_pcb.c
--- netinet/in_pcb.c	15 Nov 2005 18:39:46 -0000	1.101
+++ netinet/in_pcb.c	22 Feb 2006 02:05:56 -0000
@@ -193,10 +193,12 @@
 	inp->inp_socket = so;
 	inp->inp_errormtu = -1;
 #if defined(IPSEC) || defined(FAST_IPSEC)
-	error = ipsec_init_pcbpolicy(so, &inp->inp_sp);
-	if (error != 0) {
-		pool_put(&inpcb_pool, inp);
-		return error;
+	if (ipsec_havespd && inp->inp_sp) {
+		error = ipsec_init_pcbpolicy(so, &inp->inp_sp);
+		if (error != 0) {
+			pool_put(&inpcb_pool, inp);
+			return error;
+		}
 	}
 #endif
 	so->so_pcb = inp;
@@ -443,7 +445,8 @@
 	inp->inp_fport = sin->sin_port;
 	in_pcbstate(inp, INP_CONNECTED);
 #if defined(IPSEC) || defined(FAST_IPSEC)
-	if (inp->inp_socket->so_type == SOCK_STREAM)
+	if (ipsec_havespd  && inp->inp_sp &&
+	    inp->inp_socket->so_type == SOCK_STREAM)
 		ipsec_pcbconn(inp->inp_sp);
 #endif
 	return (0);
@@ -461,7 +464,8 @@
 	inp->inp_fport = 0;
 	in_pcbstate(inp, INP_BOUND);
 #if defined(IPSEC) || defined(FAST_IPSEC)
-	ipsec_pcbdisconn(inp->inp_sp);
+	if (ipsec_havespd && inp->inp_sp)
+		ipsec_pcbdisconn(inp->inp_sp);
 #endif
 	if (inp->inp_socket->so_state & SS_NOFDREF)
 		in_pcbdetach(inp);
@@ -478,7 +482,8 @@
 		return;
 
 #if defined(IPSEC) || defined(FAST_IPSEC)
-	ipsec4_delete_pcbpolicy(inp);
+	if (ipsec_havespd)
+		ipsec4_delete_pcbpolicy(inp);
 #endif /*IPSEC*/
 	so->so_pcb = 0;
 	sofree(so);
Index: netinet/ip_icmp.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.97
diff -u -r1.97 ip_icmp.c
--- netinet/ip_icmp.c	10 Nov 2005 13:40:38 -0000	1.97
+++ netinet/ip_icmp.c	22 Feb 2006 02:05:57 -0000
@@ -603,7 +603,8 @@
 
 		pfctlinput(PRC_REDIRECT_HOST, sintosa(&icmpsrc));
 #if defined(IPSEC) || defined(FAST_IPSEC)
-		key_sa_routechange((struct sockaddr *)&icmpsrc);
+		if (ipsec_havespd)
+			key_sa_routechange((struct sockaddr *)&icmpsrc);
 #endif
 		break;
 
Index: netinet/ip_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_input.c,v
retrieving revision 1.224
diff -u -r1.224 ip_input.c
--- netinet/ip_input.c	18 Feb 2006 17:47:07 -0000	1.224
+++ netinet/ip_input.c	22 Feb 2006 02:05:59 -0000
@@ -616,8 +616,11 @@
 	}
 
 #if defined(IPSEC)
-	/* ipflow (IP fast forwarding) is not compatible with IPsec. */
-	m->m_flags &= ~M_CANFASTFWD;
+	if (ipsec_havespd)
+		/* ipflow (IP fast forwarding) is not compatible with IPsec. */
+		m->m_flags &= ~M_CANFASTFWD;
+	else
+		m->m_flags |= M_CANFASTFWD;
 #else
 	/*
 	 * Assume that we can create a fast-forward IP flow entry
@@ -639,9 +642,9 @@
 	 * not the decapsulated packet.
 	 */
 #ifdef IPSEC
-	if (!ipsec_getnhist(m))
+	if (!ipsec_havespd || !ipsec_getnhist(m))
 #elif defined(FAST_IPSEC)
-	if (!ipsec_indone(m))
+	if (!ipsec_havespd || !ipsec_indone(m))
 #else
 	if (1)
 #endif
@@ -823,12 +826,13 @@
 			return;
 		}
 #ifdef IPSEC
-		if (ipsec4_in_reject(m, NULL)) {
+		if (ipsec_havespd && ipsec4_in_reject(m, NULL)) {
 			ipsecstat.in_polvio++;
 			goto bad;
 		}
 #endif
 #ifdef FAST_IPSEC
+		if (ipsec_havespd) {
 		mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
 		s = splsoftnet();
 		if (mtag != NULL) {
@@ -875,6 +879,7 @@
 			}
 			splx(s);
 		}
+		}
 #endif	/* FAST_IPSEC */
 
 		ip_forward(m, srcrt);
@@ -968,7 +973,8 @@
 	 * note that we do not visit this with protocols with pcb layer
 	 * code - like udp/tcp/raw ip.
 	 */
-	if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0 &&
+	if (ipsec_havespd &&
+	    (inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0 &&
 	    ipsec4_in_reject(m, NULL)) {
 		ipsecstat.in_polvio++;
 		goto bad;
@@ -980,7 +986,8 @@
 	 * note that we do not visit this with protocols with pcb layer
 	 * code - like udp/tcp/raw ip.
 	 */
-	if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) {
+	if (ipsec_havespd &&
+	    (inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) {
 		/*
 		 * Check if the packet has already had IPsec processing
 		 * done.  If so, then just pass it along.  This tag gets
@@ -1975,6 +1982,13 @@
 			size_t ipsechdr;
 			struct route *ro;
 
+			if (!ipsec_havespd) {
+				destmtu = ipforward_rt.ro_rt->rt_ifp->if_mtu;
+				ipstat.ips_cantfrag++;
+				break;
+			}
+
+			destmtu = ipforward_rt.ro_rt->rt_ifp->if_mtu;
 			sp = ipsec4_getpolicybyaddr(mcopy,
 			    IPSEC_DIR_OUTBOUND, IP_FORWARDING,
 			    &ipsecerror);
Index: netinet/ip_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_output.c,v
retrieving revision 1.159
diff -u -r1.159 ip_output.c
--- netinet/ip_output.c	11 Dec 2005 12:24:57 -0000	1.159
+++ netinet/ip_output.c	22 Feb 2006 02:06:03 -0000
@@ -523,6 +523,8 @@
 	ip_len = ntohs(ip->ip_len);
 
 #ifdef IPSEC
+	if (!ipsec_havespd || sotoinpcb_hdr(so)->inph_sp == NULL)
+		goto skip_ipsec;
 	/* get SP for this packet */
 	if (so == NULL)
 		sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND,
@@ -675,6 +677,8 @@
 skip_ipsec:
 #endif /*IPSEC*/
 #ifdef FAST_IPSEC
+	if (!ipsec_havespd)
+		goto spd_done;
 	/*
 	 * Check the security policy (SP) for the packet and, if
 	 * required, do IPsec-related processing.  There are two
@@ -693,7 +697,7 @@
 			error = -EINVAL;	/* force silent drop */
 		m_tag_delete(m, mtag);
 	} else {
-		if (inp != NULL &&
+		if (inp != NULL && inp->inp_sp &&
 		    IPSEC_PCB_SKIP_IPSEC(inp->inp_sp, IPSEC_DIR_OUTBOUND))
 			goto spd_done;
 		sp = ipsec4_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags,
@@ -872,8 +876,9 @@
 		}
 
 #ifdef IPSEC
-		/* clean ipsec history once it goes out of the node */
-		ipsec_delaux(m);
+		if (ipsec_havespd)
+			/* clean ipsec history once it goes out of the node */
+			ipsec_delaux(m);
 #endif
 
 		if (__predict_true(
@@ -930,6 +935,8 @@
 				    ntohs(ip->ip_len);
 #endif
 #ifdef IPSEC
+			if (!ipsec_havespd)
+				goto fastout;
 			/* clean ipsec history once it goes out of the node */
 			ipsec_delaux(m);
 
@@ -947,6 +954,9 @@
 #endif /* IPSEC_NAT_T */
 #endif /* IPSEC */
 			{
+#ifdef IPSEC
+fastout:
+#endif
 				KASSERT((m->m_pkthdr.csum_flags &
 				    (M_CSUM_UDPv4 | M_CSUM_TCPv4)) == 0);
 				error = (*ifp->if_output)(ifp, m, sintosa(dst),
@@ -965,14 +975,14 @@
 	}
 
 #ifdef IPSEC
-	if (sp != NULL) {
+	if (ipsec_havespd && sp != NULL) {
 		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
 			printf("DP ip_output call free SP:%p\n", sp));
 		key_freesp(sp);
 	}
 #endif /* IPSEC */
 #ifdef FAST_IPSEC
-	if (sp != NULL)
+	if (ipsec_havespd && sp != NULL)
 		KEY_FREESP(&sp);
 #endif /* FAST_IPSEC */
 
@@ -1350,6 +1360,10 @@
 
 #if defined(IPSEC) || defined(FAST_IPSEC)
 		case IP_IPSEC_POLICY:
+			if (!ipsec_havespd) {
+				error = ENOPROTOOPT;
+				break;
+			}
 		{
 			caddr_t req = NULL;
 			size_t len = 0;
Index: netinet/raw_ip.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/raw_ip.c,v
retrieving revision 1.88
diff -u -r1.88 raw_ip.c
--- netinet/raw_ip.c	11 Dec 2005 12:24:57 -0000	1.88
+++ netinet/raw_ip.c	22 Feb 2006 02:06:03 -0000
@@ -183,7 +183,8 @@
 
 #if defined(IPSEC) || defined(FAST_IPSEC)
 			/* check AH/ESP integrity. */
-			if (ipsec4_in_reject_so(m, last->inp_socket)) {
+			if (ipsec_havespd &&
+			    ipsec4_in_reject_so(m, last->inp_socket)) {
 				ipsecstat.in_polvio++;
 				/* do not inject data to pcb */
 			} else
@@ -207,7 +208,7 @@
 	}
 #if defined(IPSEC) || defined(FAST_IPSEC)
 	/* check AH/ESP integrity. */
-	if (last && ipsec4_in_reject_so(m, last->inp_socket)) {
+	if (last && ipsec_havespd && ipsec4_in_reject_so(m, last->inp_socket)) {
 		m_freem(m);
 		ipsecstat.in_polvio++;
 		ipstat.ips_delivered--;
Index: netinet/tcp_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_input.c,v
retrieving revision 1.239
diff -u -r1.239 tcp_input.c
--- netinet/tcp_input.c	18 Feb 2006 17:34:49 -0000	1.239
+++ netinet/tcp_input.c	22 Feb 2006 02:06:06 -0000
@@ -1193,6 +1193,8 @@
 			goto dropwithreset_ratelim;
 		}
 #if defined(IPSEC) || defined(FAST_IPSEC)
+		if (!ipsec_havespd)
+			break;
 		if (inp && (inp->inp_socket->so_options & SO_ACCEPTCONN) == 0 &&
 		    ipsec4_in_reject(m, inp)) {
 			ipsecstat.in_polvio++;
@@ -1236,6 +1238,8 @@
 			goto dropwithreset_ratelim;
 		}
 #if defined(IPSEC) || defined(FAST_IPSEC)
+		if (!ipsec_havespd)
+			break;
 		if ((in6p->in6p_socket->so_options & SO_ACCEPTCONN) == 0 &&
 		    ipsec6_in_reject(m, in6p)) {
 			ipsec6stat.in_polvio++;
@@ -1494,6 +1498,8 @@
 				switch (af) {
 #ifdef INET
 				case AF_INET:
+					if (!ipsec_havespd)
+						break;
 					if (ipsec4_in_reject_so(m, so)) {
 						ipsecstat.in_polvio++;
 						tp = NULL;
@@ -1503,6 +1509,8 @@
 #endif
 #ifdef INET6
 				case AF_INET6:
+					if (!ipsec_havespd)
+						break;
 					if (ipsec6_in_reject_so(m, so)) {
 						ipsec6stat.in_polvio++;
 						tp = NULL;
@@ -3701,23 +3709,25 @@
 #endif
 
 #if defined(IPSEC) || defined(FAST_IPSEC)
+	if (ipsec_havespd) {
 	/*
 	 * we make a copy of policy, instead of sharing the policy,
 	 * for better behavior in terms of SA lookup and dead SA removal.
 	 */
-	if (inp) {
+	if (inp && sotoinpcb(oso)->inp_sp) {
 		/* copy old policy into new socket's */
 		if (ipsec_copy_pcbpolicy(sotoinpcb(oso)->inp_sp, inp->inp_sp))
 			printf("tcp_input: could not copy policy\n");
 	}
 #ifdef INET6
-	else if (in6p) {
+	else if (in6p && sotoin6pcb(oso)->in6p_sp) {
 		/* copy old policy into new socket's */
 		if (ipsec_copy_pcbpolicy(sotoin6pcb(oso)->in6p_sp,
 		    in6p->in6p_sp))
 			printf("tcp_input: could not copy policy\n");
 	}
 #endif
+	}
 #endif
 
 	/*
Index: netinet/tcp_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/tcp_output.c,v
retrieving revision 1.141
diff -u -r1.141 tcp_output.c
--- netinet/tcp_output.c	24 Dec 2005 20:45:09 -0000	1.141
+++ netinet/tcp_output.c	22 Feb 2006 02:06:07 -0000
@@ -351,7 +351,8 @@
 #ifdef INET
 	if (inp) {
 #if defined(IPSEC) || defined(FAST_IPSEC)
-		if (! IPSEC_PCB_SKIP_IPSEC(inp->inp_sp, IPSEC_DIR_OUTBOUND))
+		if (ipsec_havespd && inp->inp_sp &&
+		    ! IPSEC_PCB_SKIP_IPSEC(inp->inp_sp, IPSEC_DIR_OUTBOUND))
 			optlen += ipsec4_hdrsiz_tcp(tp);
 #endif
 		optlen += ip_optlen(inp);
@@ -361,7 +362,8 @@
 #ifdef INET
 	if (in6p && tp->t_family == AF_INET) {
 #if defined(IPSEC) || defined(FAST_IPSEC)
-		if (! IPSEC_PCB_SKIP_IPSEC(in6p->in6p_sp, IPSEC_DIR_OUTBOUND))
+		if (ipsec_havespd && in6p->in6p_sp &&
+		    ! IPSEC_PCB_SKIP_IPSEC(in6p->in6p_sp, IPSEC_DIR_OUTBOUND))
 			optlen += ipsec4_hdrsiz_tcp(tp);
 #endif
 		/* XXX size -= ip_optlen(in6p); */
@@ -369,7 +371,8 @@
 #endif
 	if (in6p && tp->t_family == AF_INET6) {
 #ifdef IPSEC
-		if (! IPSEC_PCB_SKIP_IPSEC(in6p->in6p_sp, IPSEC_DIR_OUTBOUND))
+		if (ipsec_havespd && in6p->in6p_sp &&
+		    ! IPSEC_PCB_SKIP_IPSEC(in6p->in6p_sp, IPSEC_DIR_OUTBOUND))
 			optlen += ipsec6_hdrsiz_tcp(tp);
 #endif
 		optlen += ip6_optlen(in6p);
@@ -615,10 +618,11 @@
 	 * - If there is not an IPsec policy that prevents it
 	 * - If the interface can do it
 	 */
-	has_tso = tp->t_inpcb != NULL &&
+	has_tso = tp->t_inpcb != NULL && 
 #if defined(IPSEC) || defined(FAST_IPSEC)
-		  IPSEC_PCB_SKIP_IPSEC(tp->t_inpcb->inp_sp,
-		  		       IPSEC_DIR_OUTBOUND) &&
+		  ipsec_havespd && tp->t_inpcb->inp_sp &&
+		      IPSEC_PCB_SKIP_IPSEC(tp->t_inpcb->inp_sp,
+		      IPSEC_DIR_OUTBOUND) &&
 #endif
 		  tp->t_inpcb->inp_route.ro_rt != NULL &&
 		  (tp->t_inpcb->inp_route.ro_rt->rt_ifp->if_capenable &
Index: netinet/udp_usrreq.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.146
diff -u -r1.146 udp_usrreq.c
--- netinet/udp_usrreq.c	21 Jan 2006 00:15:36 -0000	1.146
+++ netinet/udp_usrreq.c	22 Feb 2006 02:06:08 -0000
@@ -630,7 +630,7 @@
 
 #if defined(IPSEC) || defined(FAST_IPSEC)
 	/* check AH/ESP integrity. */
-	if (so != NULL && ipsec4_in_reject_so(m, so)) {
+	if (so != NULL && ipsec_havespd && ipsec4_in_reject_so(m, so)) {
 		ipsecstat.in_polvio++;
 		if ((n = m_copy(m, 0, M_COPYALL)) != NULL)
 			icmp_error(n, ICMP_UNREACH, ICMP_UNREACH_ADMIN_PROHIBIT,
@@ -677,7 +677,7 @@
 
 #if defined(IPSEC) || defined(FAST_IPSEC)
 	/* check AH/ESP integrity. */
-	if (so != NULL && ipsec6_in_reject_so(m, so)) {
+	if (so != NULL && ipsec_havespd && ipsec6_in_reject_so(m, so)) {
 		ipsec6stat.in_polvio++;
 		if ((n = m_copy(m, 0, M_COPYALL)) != NULL)
 			icmp6_error(n, ICMP6_DST_UNREACH,
Index: netinet6/ipsec.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ipsec.c,v
retrieving revision 1.106
diff -u -r1.106 ipsec.c
--- netinet6/ipsec.c	21 Jan 2006 00:15:36 -0000	1.106
+++ netinet6/ipsec.c	22 Feb 2006 02:06:12 -0000
@@ -119,6 +119,7 @@
 #endif /* INET6 */
 
 u_int ipsec_spdgen = 1;		/* SPD generation # */
+int ipsec_havespd = 0;		/* if we have SPD's */
 
 #ifdef SADB_X_EXT_TAG
 static struct pf_tag *ipsec_get_tag __P((struct mbuf *));
@@ -348,6 +349,8 @@
 		ipsec_spdgen = 1;
 	else
 		ipsec_spdgen++;
+	ipsec_havespd = (key_havesp(IPSEC_DIR_INBOUND) ? IPSEC_SPD_INBOUND : 0)
+	    | (key_havesp(IPSEC_DIR_OUTBOUND) ? IPSEC_SPD_OUTBOUND : 0);
 }
 
 #ifdef SADB_X_EXT_TAG
@@ -409,10 +412,8 @@
 		panic("ipsec4_getpolicybysock: unsupported address family");
 	}
 
-#ifdef DIAGNOSTIC
 	if (pcbsp == NULL)
-		panic("ipsec4_getpolicybysock: pcbsp is NULL.");
-#endif
+		return NULL;
 
 #ifdef SADB_X_EXT_TAG
 	t = ipsec_get_tag(m);
Index: netinet6/ipsec.h
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ipsec.h,v
retrieving revision 1.45
diff -u -r1.45 ipsec.h
--- netinet6/ipsec.h	10 Dec 2005 23:39:56 -0000	1.45
+++ netinet6/ipsec.h	22 Feb 2006 02:06:12 -0000
@@ -349,6 +349,9 @@
 extern void ipsec_invalpcbcacheall __P((void));
 
 extern u_int ipsec_spdgen;
+extern int ipsec_havespd;
+#define	IPSEC_SPD_INBOUND	1
+#define	IPSEC_SPD_OUTBOUND	2
 
 extern struct secpolicy *ipsec4_getpolicybysock
 	__P((struct mbuf *, u_int, struct socket *, int *));
Index: netipsec/ipsec.c
===================================================================
RCS file: /cvsroot/src/sys/netipsec/ipsec.c,v
retrieving revision 1.19
diff -u -r1.19 ipsec.c
--- netipsec/ipsec.c	11 Dec 2005 12:25:05 -0000	1.19
+++ netipsec/ipsec.c	22 Feb 2006 02:06:16 -0000
@@ -107,6 +107,7 @@
 #else
 int ipsec_debug = 0;
 #endif
+int ipsec_havespd = 0;		/* if we have SPD's */
 
 /* NB: name changed so netstat doesn't use it */
 struct newipsecstat newipsecstat;
@@ -401,6 +402,8 @@
 		ipsec_spdgen = 1;
 	else
 		ipsec_spdgen++;
+	ipsec_havespd = (key_havesp(IPSEC_DIR_INBOUND) ? IPSEC_SPD_INBOUND : 0)
+	    | (key_havesp(IPSEC_DIR_OUTBOUND) ? IPSEC_SPD_OUTBOUND : 0);
 }
 #endif /* __NetBSD__ */
 
@@ -499,7 +502,10 @@
 		("ipsec_getpolicybysock: unexpected protocol family %u", af));
 
 #ifdef __NetBSD__
-	IPSEC_ASSERT(inp->inph_sp != NULL, ("null PCB policy cache"));
+	if (inp->inph_sp == NULL) {
+		*error = EINVAL;
+		return NULL;
+	}
 	/* 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);
@@ -535,7 +541,9 @@
 	if (*error)
 		return NULL;
 
-	IPSEC_ASSERT(pcbsp != NULL, ("ipsec_getpolicybysock: null pcbsp"));
+	if (pcbsp == NULL)
+		return NULL;
+
 	switch (dir) {
 	case IPSEC_DIR_INBOUND:
 		currsp = pcbsp->sp_in;
@@ -714,7 +722,8 @@
 	int error;
 
 	IPSEC_ASSERT(pcb != NULL, ("ipsec4_setspidx_inpcb: null pcb"));
-	IPSEC_ASSERT(pcb->inp_sp != NULL, ("ipsec4_setspidx_inpcb: null inp_sp"));
+	if (pcb->inp_sp == NULL)
+		return EINVAL;
 	IPSEC_ASSERT(pcb->inp_sp->sp_out != NULL && pcb->inp_sp->sp_in != NULL,
 		("ipsec4_setspidx_inpcb: null sp_in || sp_out"));
 
@@ -742,7 +751,8 @@
 	int error;
 
 	IPSEC_ASSERT(pcb != NULL, ("ipsec6_setspidx_in6pcb: null pcb"));
-	IPSEC_ASSERT(pcb->in6p_sp != NULL, ("ipsec6_setspidx_in6pcb: null inp_sp"));
+	if (pcb->in6p_sp == NULL)
+		return EINVAL;
 	IPSEC_ASSERT(pcb->in6p_sp->sp_out != NULL && pcb->in6p_sp->sp_in != NULL,
 		("ipsec6_setspidx_in6pcb: null sp_in || sp_out"));
 
@@ -1299,11 +1309,10 @@
 		return EINVAL;
 	if (len < sizeof(*xpl))
 		return EINVAL;
+	if (inp->inp_sp == NULL)
+		return EINVAL;
 	xpl = (struct sadb_x_policy *)request;
 
-	IPSEC_ASSERT(inp->inp_sp != NULL,
-		     ("ipsec4_set_policy(): null inp->in_sp"));
-
 	/* select direction */
 	switch (xpl->sadb_x_policy_dir) {
 	case IPSEC_DIR_INBOUND:
@@ -1334,7 +1343,8 @@
 	/* sanity check. */
 	if (inp == NULL || request == NULL || mp == NULL)
 		return EINVAL;
-	IPSEC_ASSERT(inp->inp_sp != NULL, ("ipsec4_get_policy: null inp_sp"));
+	if (inp->inp_sp != NULL)
+		return EINVAL;
 	if (len < sizeof(*xpl))
 		return EINVAL;
 	xpl = (struct sadb_x_policy *)request;
@@ -1395,6 +1405,8 @@
 		return EINVAL;
 	if (len < sizeof(*xpl))
 		return EINVAL;
+	if (in6p->in6p_sp == NULL)
+		return EINVAL;
 	xpl = (struct sadb_x_policy *)request;
 
 	/* select direction */
@@ -1427,9 +1439,10 @@
 	/* sanity check. */
 	if (in6p == NULL || request == NULL || mp == NULL)
 		return EINVAL;
-	IPSEC_ASSERT(in6p->in6p_sp != NULL, ("ipsec6_get_policy: null in6p_sp"));
 	if (len < sizeof(*xpl))
 		return EINVAL;
+	if (in6p->in6p_sp == NULL)
+		return EINVAL;
 	xpl = (struct sadb_x_policy *)request;
 
 	/* select direction */
Index: netipsec/ipsec.h
===================================================================
RCS file: /cvsroot/src/sys/netipsec/ipsec.h,v
retrieving revision 1.14
diff -u -r1.14 ipsec.h
--- netipsec/ipsec.h	16 Feb 2006 20:17:20 -0000	1.14
+++ netipsec/ipsec.h	22 Feb 2006 02:06:16 -0000
@@ -219,6 +219,9 @@
 };
 
 extern int ipsec_debug;
+extern int ipsec_havespd;
+#define	IPSEC_SPD_INBOUND	1
+#define	IPSEC_SPD_OUTBOUND	2
 
 extern struct newipsecstat newipsecstat;
 extern struct secpolicy ip4_def_policy;
Index: netkey/key.c
===================================================================
RCS file: /cvsroot/src/sys/netkey/key.c,v
retrieving revision 1.139
diff -u -r1.139 key.c
--- netkey/key.c	25 Jan 2006 15:12:05 -0000	1.139
+++ netkey/key.c	22 Feb 2006 02:06:35 -0000
@@ -475,6 +475,18 @@
 static struct mbuf *key_alloc_mbuf __P((int));
 struct callout key_timehandler_ch;
 
+/*
+ * Return 0 when there are known to be no SP's for the specified
+ * direction.  Otherwise return 1.  This is used by IPsec code
+ * to optimize performance.
+ */
+int
+key_havesp(u_int dir)
+{
+	return (dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND ?
+		LIST_FIRST(&sptree[dir]) != NULL : 1);
+}
+
 /* %%% IPsec policy management */
 /*
  * allocating a SP for OUTBOUND or INBOUND packet.
Index: netkey/key.h
===================================================================
RCS file: /cvsroot/src/sys/netkey/key.h,v
retrieving revision 1.20
diff -u -r1.20 key.h
--- netkey/key.h	11 Dec 2005 00:02:28 -0000	1.20
+++ netkey/key.h	22 Feb 2006 02:06:35 -0000
@@ -55,6 +55,7 @@
 struct sadb_msg;
 struct sadb_x_policy;
 
+extern int key_havesp __P((u_int));
 extern struct secpolicy *key_allocsp __P((u_int16_t, struct secpolicyindex *,
 	u_int));
 extern int key_checkrequest