Making KLIPS 1 asynchronous to support H/W crypto

Arthur van Leeuwen <[email protected]> Mon, 3 Mar 2003 15:44:06 +0100 (CET)
Newsgroups gmane.network.freeswan.devel
Message-ID <[email protected]>
Hello,

in the search for the elusive hardware encryption for FreeS/WAN I have been
trying to redo the Colubris hardware encryption patch for FreeS/WAN 1.91
with FreeS/WAN 2.00(-rc1).

I decided to try and do it `the right way'. This involved basically making
KLIPS 1 support asynchronous encryption operations, and then making the 
minimal changes necessary to offload the actual encryption to Martin
Gadbois' cryptolib.

The idea of making KLIPS 1 support asynchronous encryption is that of
breaking up loops and basic blocks in which encryption can take place and
allow re-entry from a callback.

This is accomplished by keeping the decrypting function's state not on the
stack but on the heap and passing pointers to it around.

Fortunately, for ipsec_rcv in linux/net/ipsec/ipsec_rcv.c this is already
partially done by encapsulating almost all necessary state in an
ipsec_rcv_state struct. All that needs to be done is break the function at
the correct place so that a callback can re-enter the decryption from an
interrupt handler for a crypto-board and allocate the ipsec_rcv_state struct
on the heap.

Note that it isn't quite as easy, as the code *does* take locks, and these
need to be released at appropriate times.

For ipsec_start_xmit the situation is not that easy. It has not been
refactored yet.

The ipsec_start_xmit code is one huge function of some 1600 lines. The first
343 lines of these, up to the comment /* start encapsulation loop here XXX */ 
are not directly involved with encrypting. The next 1094 lines *are*. 
The last 132 lines are involved with actually sending the packet and
cleaning up.

Now, the 1094 lines that are directly involved with encrypting form 1 single
loop. In this loop there is code that sets up the encryption steps and a
loop that then does the actual encryption.

This loop ought to be split off, so that entry into the loop can be done
asynchronously. This means that every loop nesting level from the outer loop
down to the actual encryption call needs to be factored out into a seperate
function. The trouble is in a few issues:

- what state does the loop keep, and what state should therefore be carried
  over the asynchronous call?
- what locks does the loop hold and what locks can and can not be released over
  the asynchronous call?

Accompanied are my patch to FreeS/WAN 2.00-rc1 (afaik it should also patch
cleanly to 2.00-rc2, but I haven't tested that), and my version of the
cryptolib that applies cleanly to a RedHat 2.4.18-24 kernel.
(Took me a bit to figure out: Martin's cryptolib actually also supports
synchronous encryption, so I first tried that, but most hardware doesn't, so
it didn't actually work :), and KLIPS 1 is *monstrous* to say the very
least. I think I can pinpoint at least 1 bug just by reading the code... I
still need to verify that though.)

Now, what I want to know: am I completely off the rocker on my assessment?
Is this approach reasonable? Are there obvious pitfalls? I am concerned
about possible information leaks due to for instance releasing the tdb_lock 
when asynchronously encrypting a transmit request: the ipsec_sa table may
get modified in the meantime. Fortunately releasing the lock when encrypting
upon receive seems to merely result in a spurious drop, and I don't feel
the need to deal with those, higher level protocols as TCP can do that. IP
doesn't guarantee reception of data anyway.

Comments?

Doei, Arthur.

-- 
+- Arthur van Leeuwen, Systems Consultant, [email protected] -+
|  X/OS Experts in Open Systems BV                          |
|  Kruislaan 419                     Phone: +31 20 6938364  |
|  1098 VA  Amsterdam                Fax:   +31 20 6948204  |
freeswan-2.00-rc1-cryptolib.patch (text/plain, 27.5 KB)
diff -Naur --exclude '*.swp' freeswan-2.00-rc1/linux/include/freeswan/ipsec_sa.h freeswan-2.00-rc1-cryptolib/linux/include/freeswan/ipsec_sa.h
--- freeswan-2.00-rc1/linux/include/freeswan/ipsec_sa.h	Thu Jan 30 03:31:52 2003
+++ freeswan-2.00-rc1-cryptolib/linux/include/freeswan/ipsec_sa.h	Fri Feb 28 11:28:16 2003
@@ -177,6 +177,18 @@
 	caddr_t		ips_key_e;		/* encryption key */
 	caddr_t	        ips_iv;			/* Initialisation Vector */
 
+	/*** AVL XXX random and key data for cryptolib ***/
+#if 1
+#define IPSEC_SA_RANDOM_STRING_LEN 64
+	__u8		*ips_random_str;	/* random numbers for iv */
+	__u8		ips_random_ctr;		/* where we are in random string */
+
+	void		*ips_key_raw_a;		/* raw authentication key */
+	void		*ips_key_raw_e;		/* raw encryption key */
+	__u16		ips_key_raw_a_size;
+	__u16		ips_key_raw_e_size;
+#endif
+
 	struct ident	ips_ident_s;		/* identity src */
 	struct ident	ips_ident_d;		/* identity dst */
 
diff -Naur --exclude '*.swp' freeswan-2.00-rc1/linux/net/ipsec/ipsec_rcv.c freeswan-2.00-rc1-cryptolib/linux/net/ipsec/ipsec_rcv.c
--- freeswan-2.00-rc1/linux/net/ipsec/ipsec_rcv.c	Fri Dec 13 21:58:03 2002
+++ freeswan-2.00-rc1-cryptolib/linux/net/ipsec/ipsec_rcv.c	Fri Feb 28 11:22:18 2003
@@ -54,6 +54,8 @@
 #include <asm/checksum.h>
 #include <net/ip.h>
 
+#include <linux/if_crypto.h>	/* cryptolib crypto offloading */
+
 #include "freeswan/radij.h"
 #include "freeswan/ipsec_encap.h"
 #include "freeswan/ipsec_sa.h"
@@ -220,6 +222,8 @@
 #endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
 
 enum ipsec_rcv_value {
+	IPSEC_RCV_PENDING=3,
+	IPSEC_RCV_CONTINUE=2,
 	IPSEC_RCV_LASTPROTO=1,
 	IPSEC_RCV_OK=0,
 	IPSEC_RCV_BADPROTO=-1,
@@ -262,6 +266,9 @@
 	__u8 *ictx;
 	int ictx_len;
 	int octx_len;
+	unsigned char * authenticator;
+	int replay;
+	struct crypto_request *cryptreq;
 	union {
 		struct {
 			struct esp *espp;
@@ -330,6 +337,48 @@
 }
 
 enum ipsec_rcv_value
+ipsec_rcv_esp_decrypt_bh(struct ipsec_rcv_state *irs);
+
+enum ipsec_rcv_value
+ipsec_rcv_decap_once_bh(struct ipsec_rcv_state *irs, enum ipsec_rcv_value bh_stat);
+
+int ipsec_rcv_bh(struct ipsec_rcv_state * irs, enum ipsec_rcv_value bh_stat);
+
+void ipsec_rcv_esp_decrypt_done(struct crypto_request *cryptreq) {
+	struct ipsec_rcv_state *irs = (struct ipsec_rcv_state *)cryptreq->priv;
+	struct crypto_operation *op;
+	enum ipsec_rcv_value retval;
+	int i;
+	
+	for (op = &req->op; op != NULL; op=op->next) {
+		if (op->cmd & (CRYPT_MD5 | CRYPT_SHA)) {
+			for (i=0;i<(op->u.hash.reslen<AH_AMAX?op->u.hash.reslen:AH_AMAX);i++) {
+				irs->hash[i] = op->u.hash.result[i];
+			}
+			break;
+		}
+	}
+
+	/* We don't need the crypto request itself anymore, free it */
+	op = &req->op.next;
+	while (op!=NULL) {
+		struct crypto_operation *del;
+		del=op;
+		op=op->next;
+		kfree(del);
+	}
+	crypto_free_request(cryptreq);
+	irs->cryptreq=NULL;
+
+	/* Re-acquire the lock before going into bottom halves */
+	spin_lock(&tdb_lock);
+
+	retval = ipsec_rcv_esp_decrypt_bh(irs);
+	retval = ipsec_rcv_decap_once_bh(irs,retval);
+	ipsec_rcv_bh(irs,retval);
+}
+
+enum ipsec_rcv_value
 ipsec_rcv_esp_decrypt_setup(struct ipsec_rcv_state *irs,
 			    struct sk_buff *skb,
 			    __u32          *replay,
@@ -365,24 +414,50 @@
 		SHA1_CTX	sha1;
 	} tctx;
 
-	aa = irs->authfuncs;
+	switch (irs->ipsp->ips_authalg) {
+#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
+	case AH_MD5:
+		irs->cryptreq = crypto_alloc_request(GFP_ATOMIC);
 
-	/* copy the initialized keying material */
-	memcpy(&tctx, irs->ictx, irs->ictx_len);
+		irs->cryptreq->inbuf = skb->data + irs->iphlen;
+		irs->cryptreq->inlen = skb->len - irs->iphlen;
+		irs->cryptreq->outbuf = irs->cryptreq->inbuf;
+		irs->cryptreq->outlen = irs->cryptreq->inlen;
+		irs->cryptreq->flags = CRYPT_DATA;
+		irs->cryptreq->priv = irs;
+		irs->cryptreq->callback = ipsec_rcv_esp_decrypt_done;
+
+		irs->cryptreq->op->cmd = CRYPT_DECODE | CRYPT_MD5 | CRYPT_HMAC | CRYPT_96;
+		irs->cryptreq->op->offset = 0;
+		irs->cryptreq->op->len = skb->len - irs->iphlen - irs->authlen;
+		memcpy(irs->cryptreq->u.hash.hmac_key,irs->ipsp->ips_key_raw_a,irs->ipsp->ips_key_raw_a_size);
+		irs->cryptreq->op->u.hash.hmac_key_len=irs->ipsp->ips_key_raw_a_size;
+		break;
+#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
+#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
+	case AH_SHA:
+		aa = irs->authfuncs;
 
-	(*aa->update)((void *)&tctx, (caddr_t)espp, irs->ilen);
+		/* copy the initialized keying material */
+		memcpy(&tctx, irs->ictx, irs->ictx_len);
 
-	(*aa->final)(irs->hash, (void *)&tctx);
+		(*aa->update)((void *)&tctx, (caddr_t)espp, irs->ilen);
 
-	memcpy(&tctx, irs->octx, irs->octx_len);
+		(*aa->final)(irs->hash, (void *)&tctx);
 
-	(*aa->update)((void *)&tctx, irs->hash, AHMD596_ALEN);
-	(*aa->final)(irs->hash, (void *)&tctx);
+		memcpy(&tctx, irs->octx, irs->octx_len);
+
+		(*aa->update)((void *)&tctx, irs->hash, AHMD596_ALEN);
+		(*aa->final)(irs->hash, (void *)&tctx);
+		break;
+#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
+	case AH_NONE:
+		break;
+	}
 
 	return IPSEC_RCV_OK;
 }
 
-
 enum ipsec_rcv_value
 ipsec_rcv_esp_decrypt(struct ipsec_rcv_state *irs)
 {
@@ -393,8 +468,9 @@
 	__u32 iv[2];
 	int pad = 0, padlen;
 	int badpad = 0;
-	int i;
+	int i, err;
 	struct sk_buff *skb;
+	struct crypto_operation *op;
 
 	skb=irs->skb;
 
@@ -430,6 +506,36 @@
 			}
 			return IPSEC_RCV_3DES_BADBLOCKING;
 		}
+		if (irs->cryptreq == NULL) {
+			irs->cryptreq = crypto_alloc_request(GFP_ATOMIC);
+
+			irs->cryptreq->inbuf = skb->data + irs->iphlen;
+			irs->cryptreq->inlen = skb->len - irs->iphlen;
+			irs->cryptreq->outbuf = irs->cryptreq->inbuf;
+			irs->cryptreq->outlen = irs->cryptreq->inlen;
+			irs->cryptreq->flags = CRYPT_DATA;
+			irs->cryptreq->priv = irs;
+			irs->cryptreq->callback = ipsec_rcv_esp_decrypt_done;
+			op = &(irs->cryptreq->op);
+		} else {
+			irs->cryptreq->op->next = kmalloc(sizeof *op,GFP_ATOMIC);
+			op = irs->cryptreq->op->next;
+			if (op == NULL) {
+				KLIPS_PRINT(debug_rcv,
+					    "klips_debug:ipsec_rcv: "
+					    "memory allocation error (crypto op)\n");
+				return IPSEC_RCV_ESP_DECAPFAIL;
+			}
+		}
+
+		op->cmd = CRYPT_DECODE | CRYPT_3DES | CRYPT_CBC;
+		op->offset = esphlen;
+		op->len = skb->len - irs->iphlen - irs->authlen - esphlen;
+		memcpy(op->u.des.key, irs->ipsp->ips_key_raw_e,3*sizeof(des_cblock));
+		op->u.des.ivec=(des_cblock *)espp->esp_iv;
+		op->next = NULL;
+
+#if 0
 		des_ede3_cbc_encrypt((des_cblock *)idat,
 				     (des_cblock *)idat,
 				     irs->ilen,
@@ -437,9 +543,38 @@
 				     ((struct des_eks *)(ipsp->ips_key_e))[1].ks,
 				     ((struct des_eks *)(ipsp->ips_key_e))[2].ks,
 				     (des_cblock *)iv, 0);
+#endif
+		err = crypto_execute(irs->cryptreq);
+		if (err == CRYPT_OK || err == CRYPT_READY) {
+			spin_unlock(&tdb_lock);
+			return (IPSEC_RCV_PENDING);
+		} else {
+			return (IPSEC_RCV_ESP_DECAPFAIL);
+		}
+
 		break;
 	}
 
+	return ipsec_rcv_esp_decrypt_bh(irs);
+}
+
+enum ipsec_rcv_value
+ipsec_rcv_esp_decrypt_bh(struct ipsec_rcv_state *irs)
+{
+	struct ipsec_sa *ipsp = irs->ipsp;
+	struct esp *espp = irs->protostuff.espstuff.espp;
+	int esphlen = sizeof(struct esp);
+	__u8 *idat;	/* pointer to content to be decrypted/authenticated */
+	__u32 iv[2];
+	int pad = 0, padlen;
+	int badpad = 0;
+	int i;
+	struct sk_buff *skb;
+
+	skb=irs->skb;
+
+	idat = skb->data + irs->iphlen + esphlen;
+
 	rcv_dmp("postdecrypt", skb->data, skb->len);
 
 	irs->next_header = idat[irs->ilen - 1];
@@ -851,7 +986,6 @@
 	__u8 proto;
 	struct in_addr ipsaddr;
 	struct in_addr ipdaddr;
-	int replay = 0;	/* replay value in AH or ESP packet */
 	struct ipsec_sa* ipsnext = NULL;	/* next SA towards inside of packet */
 #ifdef INBOUND_POLICY_CHECK_eroute
 	struct sockaddr_encap matcher;	/* eroute search key */
@@ -865,6 +999,8 @@
 	struct iphdr *ipp;
 	struct sk_buff *skb;
 
+	irs->replay = 0;	/* replay value in AH or ESP packet */
+	irs->authenticator = NULL;
 	skb = irs->skb;
 	irs->len = skb->len;
 	dat = skb->data;
@@ -1092,7 +1228,6 @@
 	}
 
 	if(irs->authfuncs) {
-		unsigned char *authenticator = NULL;
 
 		irs->ilen = irs->len - iphlen - irs->authlen;
 		if(irs->ilen <= 0) {
@@ -1109,14 +1244,14 @@
 		if(proto_funcs->setup_auth) {
 			enum ipsec_rcv_value retval
 			    = (*proto_funcs->setup_auth)(irs, skb,
-							 &replay,
-							 &authenticator);
+							 &(irs->replay),
+							 &(irs->authenticator));
 			if(retval < 0) {
 				return retval;
 			}
 		}
 
-		if(!authenticator) {
+		if(!(irs->authenticator)) {
 			irs->ipsp->ips_errs.ips_auth_errs += 1;
 			if(irs->stats) {
 				irs->stats->rx_dropped++;
@@ -1124,7 +1259,7 @@
 			return IPSEC_RCV_BADAUTH;
 		}
 
-		if(!ipsec_checkreplaywindow(irs->ipsp, replay)) {
+		if(!ipsec_checkreplaywindow(irs->ipsp, irs->replay)) {
 			irs->ipsp->ips_errs.ips_replaywin_errs += 1;
 			KLIPS_PRINT(debug_rcv & DB_RX_REPLAY,
 				    "klips_debug:ipsec_rcv: "
@@ -1152,7 +1287,35 @@
 		}
 		(*proto_funcs->calc_auth)(irs, skb);
 
-		if (memcmp(irs->hash, authenticator, irs->authlen)) {
+	}
+
+	if(proto_funcs->decrypt) {
+		enum ipsec_rcv_value retval =
+		  (*proto_funcs->decrypt)(irs);
+
+		if(retval != IPSEC_RCV_OK) {
+			return retval;
+		}
+	}
+
+	return ipsec_rcv_decap_once_bh(irs);
+}
+
+enum ipsec_rcv_value
+ipsec_rcv_decap_once_bh(struct ipsec_rcv_state *irs, enum ipsec_rcv_value bh_stat)
+{
+	struct in_addr ipsaddr;
+	struct in_addr ipdaddr;
+	struct ipsec_sa *ipsnext = NULL:
+	struct iphdr *ipp;
+	struct sk_buff *skb;
+
+	if(bh_stat != IPSEC_RCV_OK) {
+		return bh_stat;
+	}
+
+	if(irs->authfuncs) {
+		if (memcmp(irs->hash, irs->authenticator, irs->authlen)) {
 			irs->ipsp->ips_errs.ips_auth_errs += 1;
 			KLIPS_PRINT(debug_rcv & DB_RX_INAU,
 				    "klips_debug:ipsec_rcv: "
@@ -1161,9 +1324,9 @@
 				    ntohl(*(__u32*)&irs->hash[0]),
 				    ntohl(*(__u32*)&irs->hash[4]),
 				    ntohl(*(__u32*)&irs->hash[8]),
-				    ntohl(*(__u32*)authenticator),
-				    ntohl(*((__u32*)authenticator + 1)),
-				    ntohl(*((__u32*)authenticator + 2)));
+				    ntohl(*(__u32*)irs->authenticator),
+				    ntohl(*((__u32*)irs->authenticator + 1)),
+				    ntohl(*((__u32*)irs->authenticator + 2)));
 			if(irs->stats) {
 				irs->stats->rx_dropped++;
 			}
@@ -1180,7 +1343,7 @@
 		memset(irs->hash, 0, irs->authlen);
 
 		/* If the sequence number == 0, expire SA, it had rolled */
-		if(irs->ipsp->ips_replaywin && !replay /* !irs->ipsp->ips_replaywin_lastseq */) {
+		if(irs->ipsp->ips_replaywin && !(irs->replay) /* !irs->ipsp->ips_replaywin_lastseq */) {
 			ipsec_sa_delchain(irs->ipsp);
 			KLIPS_PRINT(debug_rcv,
 				    "klips_debug:ipsec_rcv: "
@@ -1192,7 +1355,7 @@
 		}
 
 		/* now update the replay counter */
-		if (!ipsec_updatereplaywindow(irs->ipsp, replay)) {
+		if (!ipsec_updatereplaywindow(irs->ipsp, irs->replay)) {
 			irs->ipsp->ips_errs.ips_replaywin_errs += 1;
 			KLIPS_PRINT(debug_rcv & DB_RX_REPLAY,
 				    "klips_debug:ipsec_rcv: "
@@ -1204,16 +1367,6 @@
 			return IPSEC_RCV_REPLAYROLLED;
 		}
 	}
-
-	if(proto_funcs->decrypt) {
-		enum ipsec_rcv_value retval =
-		  (*proto_funcs->decrypt)(irs);
-
-		if(retval != IPSEC_RCV_OK) {
-			return retval;
-		}
-	}
-
 	/*
 	 *	Adjust pointers
 	 */
@@ -1244,7 +1397,7 @@
 	ipp->protocol = irs->next_header;
 
 	ipp->check = 0;	/* NOTE: this will be included in checksum */
-	ipp->check = ip_fast_csum((unsigned char *)dat, iphlen >> 2);
+	ipp->check = ip_fast_csum((unsigned char *)dat, (irs->iphlen) >> 2);
 
 	KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
 		    "klips_debug:ipsec_rcv: "
@@ -1310,13 +1463,13 @@
 	irs->ipsp->ips_life.ipl_packets.ipl_count += 1;
 
 #ifdef CONFIG_NETFILTER
-	if(proto == IPPROTO_ESP || proto == IPPROTO_AH) {
+	if(irs->said.proto == IPPROTO_ESP || irs->said.proto == IPPROTO_AH) {
 		skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK))))
 			| IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp));
 		KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
 			    "klips_debug:ipsec_rcv: "
 			    "%s SA sets skb->nfmark=0x%x.\n",
-			    proto == IPPROTO_ESP ? "ESP" : "AH",
+			    irs->said.proto == IPPROTO_ESP ? "ESP" : "AH",
 			    (unsigned)skb->nfmark);
 	}
 #endif /* CONFIG_NETFILTER */
@@ -1324,7 +1477,6 @@
 	return IPSEC_RCV_OK;
 }
 
-
 int
 #ifdef PROTO_HANDLER_SINGLE_PARM
 ipsec_rcv(struct sk_buff *skb)
@@ -1365,12 +1517,18 @@
 	struct sockaddr_encap policy_eaddr;
 	struct sockaddr_encap policy_emask;
 #endif /* INBOUND_POLICY_CHECK_eroute */
-	struct ipsec_rcv_state irs;
+	struct ipsec_rcv_state * irs;
 
 	/* Don't unlink in the middle of a turnaround */
 	MOD_INC_USE_COUNT;
 
-	memset(&irs, 0, sizeof(struct ipsec_rcv_state));
+	if ((irs = kmalloc(sizeof(struct ipsec_rcv_state,GFP_ATOMIC)))==NULL) {
+		KLIPS_PRINT(debug_rcv,
+			   "klips_debug:ipsec_rcv: "
+			   "memory allocation error, dropping packet\n");
+		goto rcvleave;
+	}
+	memset(irs, 0, sizeof(struct ipsec_rcv_state));
 
 	if (skb == NULL) {
 		KLIPS_PRINT(debug_rcv,
@@ -1395,9 +1553,9 @@
 #endif /* IPH_is_SKB_PULLED */
 
 	/* dev->hard_header_len is unreliable and should not be used */
-	irs.hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0;
-	if((irs.hard_header_len < 0) || (irs.hard_header_len > skb_headroom(skb)))
-		irs.hard_header_len = 0;
+	irs->hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0;
+	if((irs->hard_header_len < 0) || (irs->hard_header_len > skb_headroom(skb)))
+		irs->hard_header_len = 0;
 
 #ifdef NET_21
 	/* if skb was cloned (most likely due to a packet sniffer such as
@@ -1405,14 +1563,14 @@
 	   a copy of our own to modify */
 	if(skb_cloned(skb)) {
 		/* include any mac header while copying.. */
-		if(skb_headroom(skb) < irs.hard_header_len) {
+		if(skb_headroom(skb) < irs->hard_header_len) {
 			printk(KERN_WARNING "klips_error:ipsec_rcv: "
 			       "tried to skb_push hhlen=%d, %d available.  This should never happen, please report.\n",
-			       irs.hard_header_len,
+			       irs->hard_header_len,
 			       skb_headroom(skb));
 			goto rcvleave;
 		}
-		skb_push(skb, irs.hard_header_len);
+		skb_push(skb, irs->hard_header_len);
 		if
 #ifdef SKB_COW_NEW
 		  (skb_cow(skb, skb_headroom(skb)) != 0)
@@ -1422,14 +1580,14 @@
 		{
 			goto rcvleave;
 		}
-		if(skb->len < irs.hard_header_len) {
+		if(skb->len < irs->hard_header_len) {
 			printk(KERN_WARNING "klips_error:ipsec_rcv: "
 			       "tried to skb_pull hhlen=%d, %d available.  This should never happen, please report.\n",
-			       irs.hard_header_len,
+			       irs->hard_header_len,
 			       skb->len);
 			goto rcvleave;
 		}
-		skb_pull(skb, irs.hard_header_len);
+		skb_pull(skb, irs->hard_header_len);
 	}
 
 #endif /* NET_21 */
@@ -1448,10 +1606,10 @@
 
 	ipp = skb->nh.iph;
 	ipsaddr.s_addr = ipp->saddr;
-	addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt));
+	addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
 	ipdaddr.s_addr = ipp->daddr;
-	addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt));
-	irs.iphlen = ipp->ihl << 2;
+	addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
+	irs->iphlen = ipp->ihl << 2;
 
 	KLIPS_PRINT(debug_rcv,
 		    "klips_debug:ipsec_rcv: "
@@ -1549,8 +1707,6 @@
 
 	KLIPS_IP_PRINT(debug_rcv, ipp);
 
-	/* begin decapsulating loop here */
-
 	/*
 	  The spinlock is to prevent any other process from
 	  accessing or deleting the ipsec_sa hash table or any of the
@@ -1563,21 +1719,81 @@
 	  in KLIPS1 unless a volunteer contributes it, but will be
 	  designed into KLIPS2.
 	*/
+
 	spin_lock(&tdb_lock);
 
 	/* set up for decap loop */
-	irs.stats= stats;
-	irs.ipp  = ipp;
-	irs.ipsp = NULL;
-	irs.ilen = 0;
-	irs.authlen=0;
-	irs.authfuncs=NULL;
-	irs.skb = skb;
+	irs->stats= stats;
+	irs->ipp  = ipp;
+	irs->ipsp = NULL;
+	irs->ilen = 0;
+	irs->authlen=0;
+	irs->authfuncs=NULL;
+	irs->skb = skb;
+
+	return ipsec_rcv_bh(irs,IPSEC_RCV_CONTINUE);
+
+ rcvleave:
+	if(skb) {
+#ifdef NET_21
+		kfree_skb(skb);
+#else /* NET_21 */
+		kfree_skb(skb, FREE_WRITE);
+#endif /* NET_21 */
+	}
+
+	kfree(irs);
+
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+int
+ipsec_rcv_bh(struct ipsec_rcv_state * irs, enum ipsec_rcv_value bh_stat)
+{
+	struct sk_buf *skb = irs->skb
+	struct iphdr *ipp;
+	struct ipsec_sa *ipsp = NULL;
+	struct ipsec_sa *ipsnext = NULL;
+	struct net_device_stats *stats = irs->stats;
+	struct in_addr ipsaddr;
+	struct in_addr ipdaddr;
+#ifdef INBOUND_POLICY_CHECK_eroute
+	struct sockaddr_encap matcher;	/* eroute search key */
+	struct eroute *er;
+	struct sa_id policy_said;
+	struct sockaddr_encap policy_eaddr;
+	struct sockaddr_encap policy_emask;
+#endif /* INBOUND_POLICY_CHECK_eroute */
+
+
+	/* begin decapsulating loop here */
+
+	if (bh_stat != IPSEC_RCV_OK && bh_stat != IPSEC_RCV_CONTINUE) {
+		spin_unlock(&tdb_lock);
+		KLIPS_PRINT(debug_rcv,
+			    "klips_debug:ipsec_rcv: decap_once (async) failed: %d\n",
+			    bh_stat);
+		goto rcvleave;
+	}
+
+	if(   (irs->ipp->protocol != IPPROTO_ESP )
+	   && (irs->ipp->protocol != IPPROTO_AH  )
+#ifdef CONFIG_IPSEC_IPCOMP
+	   && (irs->ipp->protocol != IPPROTO_COMP)
+#endif /* CONFIG_IPSEC_IPCOMP */
+	  ) {
+		goto decapdone;
+	}
 
 	do {
 	        int decap_stat;
 
-	        decap_stat = ipsec_rcv_decap_once(&irs);
+	        decap_stat = ipsec_rcv_decap_once(irs);
+
+		if(decap_stat == IPSEC_RCV_PENDING) {
+			return(0);
+		}
 
 		if(decap_stat != IPSEC_RCV_OK) {
 			spin_unlock(&tdb_lock);
@@ -1588,18 +1804,20 @@
 			goto rcvleave;
 		}
 	/* end decapsulation loop here */
-	} while(   (irs.ipp->protocol == IPPROTO_ESP )
-		|| (irs.ipp->protocol == IPPROTO_AH  )
+	} while(   (irs->ipp->protocol == IPPROTO_ESP )
+		|| (irs->ipp->protocol == IPPROTO_AH  )
 #ifdef CONFIG_IPSEC_IPCOMP
-		|| (irs.ipp->protocol == IPPROTO_COMP)
+		|| (irs->ipp->protocol == IPPROTO_COMP)
 #endif /* CONFIG_IPSEC_IPCOMP */
 		);
 
+ decapdone:
+
 	/* set up for decap loop */
-	ipp  =irs.ipp;
-	ipsp =irs.ipsp;
+	ipp  =irs->ipp;
+	ipsp =irs->ipsp;
 	ipsnext = ipsp->ips_inext;
-	skb = irs.skb;
+	skb = irs->skb;
 
 	/* if there is an IPCOMP, but we don't have an IPPROTO_COMP,
 	 * then we can just skip it
@@ -1618,13 +1836,13 @@
 	 */
 	if(ipsnext) {
 		ipsp = ipsnext;
-		irs.sa_len = satoa(irs.said, 0, irs.sa, SATOA_BUF);
+		irs->sa_len = satoa(irs->said, 0, irs->sa, SATOA_BUF);
 		if(ipp->protocol != IPPROTO_IPIP) {
 			spin_unlock(&tdb_lock);
 			KLIPS_PRINT(debug_rcv,
 				    "klips_debug:ipsec_rcv: "
 				    "SA:%s, Hey!  How did this get through?  Dropped.\n",
-				    irs.sa_len ? irs.sa : " (error)");
+				    irs->sa_len ? irs->sa : " (error)");
 			if(stats) {
 				stats->rx_dropped++;
 			}
@@ -1640,7 +1858,7 @@
 					    "klips_debug:ipsec_rcv: "
 					    "unexpected SA:%s after IPIP SA:%s\n",
 					    sa_len2 ? sa2 : " (error)",
-					    irs.sa_len ? irs.sa : " (error)");
+					    irs->sa_len ? irs->sa : " (error)");
 				if(stats) {
 					stats->rx_dropped++;
 				}
@@ -1651,8 +1869,8 @@
 				KLIPS_PRINT(debug_rcv,
 					    "klips_debug:ipsec_rcv: "
 					    "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n",
-					    irs.sa_len ? irs.sa : " (error)",
-					    irs.ipsaddr_txt);
+					    irs->sa_len ? irs->sa : " (error)",
+					    irs->ipsaddr_txt);
 				if(stats) {
 					stats->rx_dropped++;
 				}
@@ -1674,16 +1892,16 @@
 		ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
 		ipsp->ips_life.ipl_packets.ipl_count += 1;
 
-		if(skb->len < irs.iphlen) {
+		if(skb->len < irs->iphlen) {
 			spin_unlock(&tdb_lock);
 			printk(KERN_WARNING "klips_debug:ipsec_rcv: "
 			       "tried to skb_pull iphlen=%d, %d available.  This should never happen, please report.\n",
-			       irs.iphlen,
+			       irs->iphlen,
 			       (int)(skb->len));
 
 			goto rcvleave;
 		}
-		skb_pull(skb, irs.iphlen);
+		skb_pull(skb, irs->iphlen);
 
 #ifdef NET_21
 		ipp = (struct iphdr *)skb->nh.raw = skb->data;
@@ -1696,9 +1914,9 @@
 		memset(skb->proto_priv, 0, sizeof(struct options));
 #endif /* NET_21 */
 		ipsaddr.s_addr = ipp->saddr;
-		addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt));
+		addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
 		ipdaddr.s_addr = ipp->daddr;
-		addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt));
+		addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
 
 		skb->protocol = htons(ETH_P_IP);
 		skb->ip_summed = 0;
@@ -1732,11 +1950,11 @@
 			KLIPS_PRINT(debug_rcv,
 				    "klips_debug:ipsec_rcv: "
 				    "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n",
-				    irs.sa_len ? irs.sa : " (error)",
+				    irs->sa_len ? irs->sa : " (error)",
 				    sflow_txt,
 				    dflow_txt,
-				    irs.ipsaddr_txt,
-				    irs.ipdaddr_txt);
+				    irs->ipsaddr_txt,
+				    irs->ipdaddr_txt);
 			if(stats) {
 				stats->rx_dropped++;
 			}
@@ -1854,12 +2072,12 @@
 		skb->dst = NULL;
 	}
 	skb->pkt_type = PACKET_HOST;
-	if(irs.hard_header_len &&
-	   (skb->mac.raw != (skb->data - irs.hard_header_len)) &&
-	   (irs.hard_header_len <= skb_headroom(skb))) {
+	if(irs->hard_header_len &&
+	   (skb->mac.raw != (skb->data - irs->hard_header_len)) &&
+	   (irs->hard_header_len <= skb_headroom(skb))) {
 		/* copy back original MAC header */
-		memmove(skb->data - irs.hard_header_len, skb->mac.raw, irs.hard_header_len);
-		skb->mac.raw = skb->data - irs.hard_header_len;
+		memmove(skb->data - irs->hard_header_len, skb->mac.raw, irs->hard_header_len);
+		skb->mac.raw = skb->data - irs->hard_header_len;
 	}
 #endif /* NET_21 */
 
@@ -1906,6 +2124,8 @@
 		    "netif_rx() called.\n");
 	netif_rx(skb);
 
+	kfree(irs);
+
 	MOD_DEC_USE_COUNT;
 	return(0);
 
@@ -1918,6 +2138,8 @@
 #endif /* NET_21 */
 	}
 
+	kfree(irs);
+
 	MOD_DEC_USE_COUNT;
 	return(0);
 }
diff -Naur --exclude '*.swp' freeswan-2.00-rc1/linux/net/ipsec/ipsec_sa.c freeswan-2.00-rc1-cryptolib/linux/net/ipsec/ipsec_sa.c
--- freeswan-2.00-rc1/linux/net/ipsec/ipsec_sa.c	Thu Jan 30 03:32:22 2003
+++ freeswan-2.00-rc1-cryptolib/linux/net/ipsec/ipsec_sa.c	Fri Feb 28 11:55:12 2003
@@ -999,6 +999,31 @@
 		kfree(ips->ips_ident_d.data);
         }
 	ips->ips_ident_d.data = NULL;
+
+	/*** AVL FIXME hw crypto ***/
+#if 1
+	if(ips->ips_random_str != NULL) {
+		memset((caddr_t)(ips->ips_random_str),
+			0,
+			IPSEC_SA_RANDOM_STRING_LEN);
+		kfree(ips->ips_random_str);
+	}
+	ips->ips_random_str = NULL;
+
+	if(ips->ips_key_raw_a != NULL) {
+		memset((caddr_t)(ips->ips_key_raw_a),
+			0,
+			ips->ips_key_raw_a_size);
+		kfree(ips->ips_key_raw_a);
+	}
+
+	if(ips->ips_key_raw_e != NULL) {
+		memset((caddr_t)(ips->ips_key_raw_e),
+			0,
+			ips->ips_key_raw_e_size);
+		kfree(ips->ips_key_raw_e);
+	}
+#endif
 	
 	memset((caddr_t)ips, 0, sizeof(*ips));
 	kfree(ips);
diff -Naur --exclude '*.swp' freeswan-2.00-rc1/linux/net/ipsec/ipsec_tunnel.c freeswan-2.00-rc1-cryptolib/linux/net/ipsec/ipsec_tunnel.c
--- freeswan-2.00-rc1/linux/net/ipsec/ipsec_tunnel.c	Fri Dec  6 03:24:02 2002
+++ freeswan-2.00-rc1-cryptolib/linux/net/ipsec/ipsec_tunnel.c	Fri Feb 28 16:44:37 2003
@@ -684,6 +684,8 @@
 	 * the eroute while we are using and updating it.
 	 */
 	spin_lock(&eroute_lock);
+
+	/*** AVL XXX allcation of crypt req here? ***/
 	
 	er = ipsec_findroute(&matcher);
 
@@ -1570,6 +1572,8 @@
 #ifdef CONFIG_IPSEC_ENC_3DES
 				case ESP_3DES:
 #endif /* CONFIG_IPSEC_ENC_3DES */
+					/* AVL XXX will possibly work 
+					 * async now: see below */
 					iv[0] = *((__u32*)&(espp->esp_iv)    ) =
 						((__u32*)(ipsp->ips_iv))[0];
 					iv[1] = *((__u32*)&(espp->esp_iv) + 1) =
@@ -1599,6 +1603,7 @@
 				switch(ipsp->ips_encalg) {
 #ifdef CONFIG_IPSEC_ENC_3DES
 				case ESP_3DES:
+					/* AVL XXX BUILD ASYNC Crypto call */
 					des_ede3_cbc_encrypt((des_cblock *)idat,
 							     (des_cblock *)idat,
 							     ilen,
@@ -1620,6 +1625,7 @@
 				case ESP_3DES:
 #endif /* CONFIG_IPSEC_ENC_3DES */
 					/* XXX update IV with the last 8 octets of the encryption */
+					/* AVL XXX This should be in BH */
 #if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK
                                        ((__u32*)(ipsp->ips_iv))[0] =
                                                ((__u32 *)(idat))[(ilen >> 2) - 2];
@@ -1639,6 +1645,7 @@
 				switch(ipsp->ips_authalg) {
 #ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
 				case AH_MD5:
+					/* AVL XXX Expand ASYNC Crypto call */
 					dmp("espp", (char*)espp, len - iphlen - authlen);
 					tctx.md5 = ((struct md5_ctx*)(ipsp->ips_key_a))->ictx;
 					dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
diff -Naur --exclude '*.swp' freeswan-2.00-rc1/linux/net/ipsec/pfkey_v2_parser.c freeswan-2.00-rc1-cryptolib/linux/net/ipsec/pfkey_v2_parser.c
--- freeswan-2.00-rc1/linux/net/ipsec/pfkey_v2_parser.c	Thu Jan 30 03:32:44 2003
+++ freeswan-2.00-rc1-cryptolib/linux/net/ipsec/pfkey_v2_parser.c	Fri Feb 28 11:43:03 2003
@@ -376,6 +376,14 @@
 			}
 			prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, EMT_ESPDES_IV_SZ);
 			ipsp->ips_iv_bits = ipsp->ips_iv_size * 8;
+			/*** AVL XXX tdb_random_str (ipsp->ips_random_str) ***/
+# if 1
+			if((ipsp->ips_random_str=(caddr_t) kmalloc(IPSEC_SA_RANDOM_STRING_LEN,GFP_ATOMIC)) == NULL) {
+				SENDERR(ENOMEM);
+			}
+			get_random_bytes((void *)ipsp->ips_random_str, IPSEC_SA_RANDOM_STRING_LEN);
+			ipsp->ips_random_ctr = 0;
+#endif
 			break;
 # endif /* defined(CONFIG_IPSEC_ENC_3DES) */
 		case ESP_NONE:
@@ -402,6 +410,12 @@
 			/* save encryption key pointer */
 			ekp = ipsp->ips_key_e;
 			eks = ipsp->ips_key_e_size;
+			/*** AVL XXX ips_key_raw_e ***/
+#if 1
+			/* Remember raw key, before S/W processing */
+			ipsp->ips_key_raw_e = ekp;
+			ipsp->ips_key_raw_e_size = eks;
+#endif
 			
 			KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
 			            "klips_debug:pfkey_ipsec_sa_init: "
@@ -443,9 +457,9 @@
 				}
 			}
 
-			/* paranoid */
-			memset(ekp, 0, eks);
-			kfree(ekp);
+			/* paranoid */  /*** AVL XXX we still need the data */
+/*			memset(ekp, 0, eks);
+			kfree(ekp); */
 			break;
 # endif /* CONFIG_IPSEC_ENC_3DES */
 		case ESP_NONE:
@@ -487,6 +501,11 @@
 			/* save the pointer to the key material */
 			akp = ipsp->ips_key_a;
 			aks = ipsp->ips_key_a_size;
+			/*** AVL XXX key_raw_a ***/
+#if 1
+			ipsp->ips_key_raw_a = akp;
+			ipsp->ips_key_raw_a_size = aks;
+#endif
 			
 			KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
 			            "klips_debug:pfkey_ipsec_sa_init: "
@@ -531,9 +550,9 @@
 				    ((__u32*)octx)[2],
 				    ((__u32*)octx)[3] );
 #  endif /* KLIPS_DIVULGE_HMAC_KEY */
-			/* paranoid */
-			memset(akp, 0, aks);
-			kfree(akp);
+			/* paranoid */ /*** AVL XXX but we need this data! */
+/*			memset(akp, 0, aks);
+			kfree(akp); */
 			break;
 		}
 # endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
@@ -565,6 +584,11 @@
 			/* save the pointer to the key material */
 			akp = ipsp->ips_key_a;
 			aks = ipsp->ips_key_a_size;
+			/*** AVL XXX key_raw ***/
+#if 0
+			ipsp->ips_key_raw_a = akp;
+			ipsp->ips_key_raw_a_size = aks;
+#endif
 
 			KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
 			            "klips_debug:pfkey_ipsec_sa_init: "
@@ -609,8 +633,10 @@
 				    ((__u32*)octx)[2],
 				    ((__u32*)octx)[3] );
 #  endif /* KLIPS_DIVULGE_HMAC_KEY */
-			memset(akp, 0, aks);
+#if 0
+			memset(akp, 0, aks); /*** AVL XXX we still need this */
 			kfree(akp);
+#endif
 			break;
 		}
 # endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
linux-2.4.18-cryptolib.patch (text/plain, 520.3 KB) - not displayed