Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1628

kernel-2.6.18-238.el5.src.rpm

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Sun, 6 Jan 2008 16:09:35 +1100
Subject: [ipsec] add async resume support on output
Message-id: E1JBNlP-0001C6-00@gondolin.me.apana.org.au
O-Subject: [PATCH 20/32] [IPSEC]: Add async resume support on output
Bugzilla: 253051

[IPSEC]: Add async resume support on output

This patch adds support for async resumptions on output.  To do so,
the transform would return -EINPROGRESS and subsequently invoke the
function xfrm_output_resume to resume processing.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Acked-by: "David S. Miller" <davem@redhat.com>

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index e98006e..b5541af 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -927,6 +927,7 @@ extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
 extern int xfrm_init_state(struct xfrm_state *x);
 extern int xfrm4_rcv(struct sk_buff *skb);
 extern int xfrm4_output(struct sk_buff *skb);
+extern int xfrm4_output_resume(struct sk_buff *skb, int err);
 extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler);
 extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler);
 extern int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi);
@@ -937,6 +938,7 @@ extern u32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr);
 extern void xfrm6_tunnel_free_spi(xfrm_address_t *saddr);
 extern u32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr);
 extern int xfrm6_output(struct sk_buff *skb);
+extern int xfrm6_output_resume(struct sk_buff *skb, int err);
 
 #ifdef CONFIG_XFRM
 extern int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type);
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index f66049e..c3d9bfa 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -11,6 +11,9 @@ obj-y     := route.o inetpeer.o protocol.o \
 	     datagram.o raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \
 	     sysctl_net_ipv4.o fib_frontend.o fib_semantics.o
 
+xfrm4_esp-objs += xfrm4_noutput.o
+obj-$(CONFIG_INET_ESP) += xfrm4_esp.o
+
 obj-$(CONFIG_IP_FIB_HASH) += fib_hash.o
 obj-$(CONFIG_IP_FIB_TRIE) += fib_trie.o
 obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/net/ipv4/xfrm4_noutput.c b/net/ipv4/xfrm4_noutput.c
new file mode 100644
index 0000000..5b2c3dc
--- /dev/null
+++ b/net/ipv4/xfrm4_noutput.c
@@ -0,0 +1,141 @@
+/*
+ * xfrm4_noutput.c - Common IPsec encapsulation code for IPv4.
+ * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/xfrm.h>
+
+static int xfrm4_output_finish2(struct sk_buff *skb);
+
+static int xfrm4_tunnel_check_size(struct sk_buff *skb)
+{
+	int mtu, ret = 0;
+	struct dst_entry *dst;
+	struct iphdr *iph = skb->nh.iph;
+
+	if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
+		goto out;
+
+	IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
+	
+	if (!(iph->frag_off & htons(IP_DF)) || skb->local_df)
+		goto out;
+
+	dst = skb->dst;
+	mtu = dst_mtu(dst);
+	if (skb->len > mtu) {
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+		ret = -EMSGSIZE;
+	}
+out:
+	return ret;
+}
+
+static int xfrm4_output_one(struct sk_buff *skb, int err)
+{
+	struct dst_entry *dst = skb->dst;
+	struct xfrm_state *x = dst->xfrm;
+	
+	if (skb->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(skb, 0);
+		if (err)
+			goto error_nolock;
+	}
+
+	if (x->props.mode) {
+		err = xfrm4_tunnel_check_size(skb);
+		if (err)
+			goto error_nolock;
+	}
+
+	do {
+		spin_lock_bh(&x->lock);
+		err = xfrm_state_check(x, skb);
+		if (err)
+			goto error;
+
+		err = x->mode->output(skb);
+		if (err)
+			goto error;
+
+		err = x->type->output(x, skb);
+		if (err)
+			goto error;
+
+		x->curlft.bytes += skb->len;
+		x->curlft.packets++;
+
+		spin_unlock_bh(&x->lock);
+	
+		if (!(skb->dst = dst_pop(dst))) {
+			err = -EHOSTUNREACH;
+			goto error_nolock;
+		}
+		dst = skb->dst;
+		x = dst->xfrm;
+	} while (x && !x->props.mode);
+
+	IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
+	err = 0;
+
+out_exit:
+	return err;
+error:
+	spin_unlock_bh(&x->lock);
+	if (err == -EINPROGRESS)
+		goto out_exit;
+error_nolock:
+	kfree_skb(skb);
+	goto out_exit;
+}
+
+int xfrm4_output_resume(struct sk_buff *skb, int err)
+{
+	while (likely((err = xfrm4_output_one(skb, err)) == 0)) {
+		nf_reset(skb);
+
+		err = nf_hook(PF_INET, NF_IP_LOCAL_OUT, &skb, NULL,
+			      skb->dst->dev, dst_output);
+		if (unlikely(err != 1))
+			goto out;
+
+		if (!skb->dst->xfrm)
+			return dst_output(skb);
+
+		err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL,
+			      skb->dst->dev, xfrm4_output_finish2);
+		if (unlikely(err != 1))
+			goto out;
+	}
+
+	if (err == -EINPROGRESS)
+		err = 0;
+
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(xfrm4_output_resume);
+
+static int xfrm4_output_finish2(struct sk_buff *skb)
+{
+	return xfrm4_output_resume(skb, 1);
+}
+
+static void xfrm4_noutput_exit(void)
+{
+}
+
+module_exit(xfrm4_noutput_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 9eebf60..31a9c61 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -16,6 +16,9 @@ ipv6-$(CONFIG_NETFILTER) += netfilter.o
 ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
 ipv6-objs += $(ipv6-y)
 
+xfrm6_esp-objs += xfrm6_noutput.o
+obj-$(CONFIG_INET6_ESP) += xfrm6_esp.o
+
 obj-$(CONFIG_INET6_AH) += ah6.o
 obj-$(CONFIG_INET6_ESP) += esp6.o
 obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
diff --git a/net/ipv6/xfrm6_noutput.c b/net/ipv6/xfrm6_noutput.c
new file mode 100644
index 0000000..00c9fb1
--- /dev/null
+++ b/net/ipv6/xfrm6_noutput.c
@@ -0,0 +1,137 @@
+/*
+ * xfrm6_noutput.c - Common IPsec encapsulation code for IPv6.
+ * Copyright (C) 2002 USAGI/WIDE Project
+ * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/icmpv6.h>
+#include <linux/module.h>
+#include <linux/netfilter_ipv6.h>
+#include <net/ipv6.h>
+#include <net/xfrm.h>
+
+static int xfrm6_output_finish2(struct sk_buff *skb);
+
+static int xfrm6_tunnel_check_size(struct sk_buff *skb)
+{
+	int mtu, ret = 0;
+	struct dst_entry *dst = skb->dst;
+
+	mtu = dst_mtu(dst);
+	if (mtu < IPV6_MIN_MTU)
+		mtu = IPV6_MIN_MTU;
+
+	if (skb->len > mtu) {
+		skb->dev = dst->dev;
+		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
+		ret = -EMSGSIZE;
+	}
+
+	return ret;
+}
+
+static int xfrm6_output_one(struct sk_buff *skb, int err)
+{
+	struct dst_entry *dst = skb->dst;
+	struct xfrm_state *x = dst->xfrm;
+	
+	if (skb->ip_summed == CHECKSUM_HW) {
+		err = skb_checksum_help(skb, 0);
+		if (err)
+			goto error_nolock;
+	}
+
+	if (x->props.mode) {
+		err = xfrm6_tunnel_check_size(skb);
+		if (err)
+			goto error_nolock;
+	}
+
+	do {
+		spin_lock_bh(&x->lock);
+		err = xfrm_state_check(x, skb);
+		if (err)
+			goto error;
+
+		err = x->mode->output(skb);
+		if (err)
+			goto error;
+
+		err = x->type->output(x, skb);
+		if (err)
+			goto error;
+
+		x->curlft.bytes += skb->len;
+		x->curlft.packets++;
+
+		spin_unlock_bh(&x->lock);
+
+		skb->nh.raw = skb->data;
+		
+		if (!(skb->dst = dst_pop(dst))) {
+			err = -EHOSTUNREACH;
+			goto error_nolock;
+		}
+		dst = skb->dst;
+		x = dst->xfrm;
+	} while (x && !x->props.mode);
+
+	IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
+	err = 0;
+
+out_exit:
+	return err;
+error:
+	spin_unlock_bh(&x->lock);
+	if (err == -EINPROGRESS)
+		goto out_exit;
+error_nolock:
+	kfree_skb(skb);
+	goto out_exit;
+}
+
+int xfrm6_output_resume(struct sk_buff *skb, int err)
+{
+	while (likely((err = xfrm6_output_one(skb, err)) == 0)) {
+		nf_reset(skb);
+	
+		err = nf_hook(PF_INET6, NF_IP6_LOCAL_OUT, &skb, NULL,
+			      skb->dst->dev, dst_output);
+		if (unlikely(err != 1))
+			goto out;
+
+		if (!skb->dst->xfrm)
+			return dst_output(skb);
+
+		err = nf_hook(PF_INET6, NF_IP6_POST_ROUTING, &skb, NULL,
+			      skb->dst->dev, xfrm6_output_finish2);
+		if (unlikely(err != 1))
+			goto out;
+	}
+
+	if (err == -EINPROGRESS)
+		err = 0;
+
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(xfrm6_output_resume);
+
+static int xfrm6_output_finish2(struct sk_buff *skb)
+{
+	return xfrm6_output_resume(skb, 1);
+}
+
+static void xfrm6_noutput_exit(void)
+{
+}
+
+module_exit(xfrm6_noutput_exit);
+MODULE_LICENSE("GPL");