diff -pruN -x .config linux-2.6.8.1/drivers/net/Kconfig linux-2.6.8.1-xfrm_dev/drivers/net/Kconfig
--- linux-2.6.8.1/drivers/net/Kconfig	2004-08-14 12:56:00.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/drivers/net/Kconfig	2004-09-08 17:33:55.000000000 +0200
@@ -41,6 +41,10 @@ config DUMMY
 	  Instead of 'dummy', the devices will then be called 'dummy0',
 	  'dummy1' etc.
 
+config XFRM_DEV
+	tristate "XFRM net driver support (EXPERIMENTAL)"
+	depends on NETDEVICES && EXPERIMENTAL
+
 config BONDING
 	tristate "Bonding driver support"
 	depends on NETDEVICES
diff -pruN -x .config linux-2.6.8.1/drivers/net/Makefile linux-2.6.8.1-xfrm_dev/drivers/net/Makefile
--- linux-2.6.8.1/drivers/net/Makefile	2004-08-14 12:55:09.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/drivers/net/Makefile	2004-09-08 17:32:17.000000000 +0200
@@ -108,6 +108,7 @@ ifeq ($(CONFIG_SLIP_COMPRESSED),y)
 endif
 
 obj-$(CONFIG_DUMMY) += dummy.o
+obj-$(CONFIG_XFRM_DEV) += xfrm_dev.o
 obj-$(CONFIG_DE600) += de600.o
 obj-$(CONFIG_DE620) += de620.o
 obj-$(CONFIG_LANCE) += lance.o
diff -pruN -x .config linux-2.6.8.1/drivers/net/xfrm_dev.c linux-2.6.8.1-xfrm_dev/drivers/net/xfrm_dev.c
--- linux-2.6.8.1/drivers/net/xfrm_dev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8.1-xfrm_dev/drivers/net/xfrm_dev.c	2004-09-16 14:33:11.000000000 +0200
@@ -0,0 +1,129 @@
+/* xfrm_dev.c: a XFRM virtual net device
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/inetdevice.h>
+
+MODULE_LICENSE("GPL");
+
+static int xfrm_dev_count = 8;
+
+static struct net_device **xfrm_devs;
+
+/* Maximum number of xfrm devices to be set up by this module. */
+module_param(xfrm_dev_count, int, 0);
+MODULE_PARM_DESC(xfrm_dev_count, "Number of XFRM pseudo devices");
+
+static int xfrm_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *xfrm_dev_get_stats(struct net_device *dev);
+
+static int xfrm_dev_set_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *sa = p;
+
+	if (!is_valid_ether_addr(sa->sa_data)) 
+		return -EADDRNOTAVAIL;
+		
+	memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+	return 0;
+}
+
+/* fake multicast ability */
+static void set_multicast_list(struct net_device *dev)
+{
+}
+
+static void xfrm_dev_setup(struct net_device *dev)
+{
+	/* Initialize the device structure. */
+	dev->get_stats = xfrm_dev_get_stats;
+	dev->hard_start_xmit = xfrm_dev_xmit;
+	dev->set_multicast_list = set_multicast_list;
+	dev->set_mac_address = xfrm_dev_set_address;
+
+	/* Fill in device structure with ethernet-generic values. */
+	ether_setup(dev);
+	dev->tx_queue_len = 0;
+	dev->change_mtu = NULL;
+	dev->flags |= IFF_NOARP;
+	dev->flags &= ~IFF_MULTICAST;
+	SET_MODULE_OWNER(dev);
+}
+
+static int xfrm_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_device_stats *stats = dev->priv;
+
+	stats->tx_packets++;
+	stats->tx_bytes+=skb->len;
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static struct net_device_stats *xfrm_dev_get_stats(struct net_device *dev)
+{
+	return dev->priv;
+}
+
+static int __init xfrm_dev_init_one(int index)
+{
+	struct net_device *dev_xfrm_dev;
+	int err;
+
+	dev_xfrm_dev = alloc_netdev(sizeof(struct net_device_stats),
+				    "xfrm%d", xfrm_dev_setup);
+
+	if (!dev_xfrm_dev)
+		return -ENOMEM;
+
+	if ((err = register_netdev(dev_xfrm_dev))) {
+		free_netdev(dev_xfrm_dev);
+		dev_xfrm_dev = NULL;
+	} else {
+		xfrm_devs[index] = dev_xfrm_dev; 
+	}
+
+	return err;
+}
+
+static void xfrm_dev_free_one(int index)
+{
+	unregister_netdev(xfrm_devs[index]);
+	free_netdev(xfrm_devs[index]);
+} 
+
+static int __init xfrm_dev_init_module(void)
+{ 
+	int i,err = 0;
+	
+	xfrm_devs = kmalloc(xfrm_dev_count * sizeof(void *), GFP_KERNEL); 
+	if (!xfrm_devs)
+		return -ENOMEM; 
+	for (i = 0; i < xfrm_dev_count && !err; i++)
+		err = xfrm_dev_init_one(i); 
+	if (err) { 
+		while (--i >= 0)
+			xfrm_dev_free_one(i);
+	}
+	return 0;
+} 
+
+static void __exit xfrm_dev_cleanup_module(void)
+{
+	int i;
+	for (i = 0; i < xfrm_dev_count; i++) 
+		xfrm_dev_free_one(i); 
+	kfree(xfrm_devs);	
+}
+
+module_init(xfrm_dev_init_module);
+module_exit(xfrm_dev_cleanup_module);
+MODULE_AUTHOR("Sebastian Classen");
+MODULE_DESCRIPTION("Provides a virtual interface for tunnels using XFRM.");
diff -pruN -x .config linux-2.6.8.1/include/net/xfrm.h linux-2.6.8.1-xfrm_dev/include/net/xfrm.h
--- linux-2.6.8.1/include/net/xfrm.h	2004-08-14 12:56:00.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/include/net/xfrm.h	2004-09-13 12:07:59.000000000 +0200
@@ -139,6 +139,10 @@ struct xfrm_state
 	struct xfrm_lifetime_cur curlft;
 	struct timer_list	timer;
 
+	/* XFRM device name and reference */
+	char 			*xfrm_dev_name;
+	struct net_device	*xfrm_dev;
+
 	/* Reference to data common to all the instances of this
 	 * transformer. */
 	struct xfrm_type	*type;
diff -pruN -x .config linux-2.6.8.1/net/ipv4/xfrm4_input.c linux-2.6.8.1-xfrm_dev/net/ipv4/xfrm4_input.c
--- linux-2.6.8.1/net/ipv4/xfrm4_input.c	2004-08-14 12:54:50.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/net/ipv4/xfrm4_input.c	2004-09-16 13:34:51.000000000 +0200
@@ -138,6 +138,16 @@ int xfrm4_rcv_encap(struct sk_buff *skb,
 			dst_release(skb->dst);
 			skb->dst = NULL;
 		}
+		if (!x->xfrm_dev)
+		    if (x->xfrm_dev_name) {
+			x->xfrm_dev = dev_get_by_name(x->xfrm_dev_name);
+			printk ("xfrm4_input: Finde Device:   %s --> 0x%p\n", x->xfrm_dev_name, x->xfrm_dev);
+		    }
+		if (x->xfrm_dev) {
+			printk ("xfrm4_input: Device vorher:  %s (0x%p)\n", skb->dev->name, skb->dev);
+			skb->dev = x->xfrm_dev;
+			printk ("xfrm4_input: Device nachher: %s (0x%p)\n", skb->dev->name, skb->dev);
+		}
 		netif_rx(skb);
 		return 0;
 	} else {
diff -pruN -x .config linux-2.6.8.1/net/ipv4/xfrm4_output.c linux-2.6.8.1-xfrm_dev/net/ipv4/xfrm4_output.c
--- linux-2.6.8.1/net/ipv4/xfrm4_output.c	2004-08-14 12:55:09.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/net/ipv4/xfrm4_output.c	2004-09-16 13:06:21.000000000 +0200
@@ -14,6 +14,7 @@
 #include <net/ip.h>
 #include <net/xfrm.h>
 #include <net/icmp.h>
+#include <linux/netfilter_ipv4.h>
 
 /* Add encapsulation header.
  *
@@ -92,6 +93,8 @@ out:
 	return ret;
 }
 
+int xfrm4_nfhook(struct sk_buff *skb) { return 0; }
+
 int xfrm4_output(struct sk_buff **pskb)
 {
 	struct sk_buff *skb = *pskb;
@@ -106,6 +109,14 @@ int xfrm4_output(struct sk_buff **pskb)
 			goto error_nolock;
 	}
 
+	printk("xfrm4_output: dev=%s input_dev=%s real_dev=%s\n", skb->dev->name, skb->input_dev->name, skb->real_dev->name);
+	printk("xfrm4_output: dst->dev=%s rt->u.dst.dev=%s\n", skb->dst->dev->name, ((struct rtable*)(skb->dst))->u.dst.dev->name);
+	printk("xfrm4_output: xfrm_dev=%s (0x%p)\n", x->xfrm_dev_name, x->xfrm_dev);
+
+	err = NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev, xfrm4_nfhook);
+	if (err)
+		goto error_nolock;
+		
 	spin_lock_bh(&x->lock);
 	err = xfrm_state_check(x, skb);
 	if (err)
@@ -128,11 +139,17 @@ int xfrm4_output(struct sk_buff **pskb)
 	x->curlft.packets++;
 
 	spin_unlock_bh(&x->lock);
-	
+
 	if (!(skb->dst = dst_pop(dst))) {
 		err = -EHOSTUNREACH;
 		goto error_nolock;
 	}
+
+	nf_reset(skb);
+	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dst->dev, xfrm4_nfhook);
+	if (err)
+		goto error_nolock;
+
 	err = NET_XMIT_BYPASS;
 
 out_exit:
diff -pruN -x .config linux-2.6.8.1/net/ipv4/xfrm4_policy.c linux-2.6.8.1-xfrm_dev/net/ipv4/xfrm4_policy.c
--- linux-2.6.8.1/net/ipv4/xfrm4_policy.c	2004-08-14 12:55:48.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/net/ipv4/xfrm4_policy.c	2004-09-14 14:36:39.000000000 +0200
@@ -125,7 +125,16 @@ __xfrm4_bundle_create(struct xfrm_policy
 		x->u.rt.fl = *fl;
 
 		dst_prev->xfrm = xfrm[i++];
-		dst_prev->dev = rt->u.dst.dev;
+		if (!dst_prev->xfrm->xfrm_dev)
+		    if (dst_prev->xfrm->xfrm_dev_name) {
+			dst_prev->xfrm->xfrm_dev = dev_get_by_name(dst_prev->xfrm->xfrm_dev_name);
+			printk ("xfrm4_policy: Finde Device:   %s --> 0x%p\n", dst_prev->xfrm->xfrm_dev_name, dst_prev->xfrm->xfrm_dev);
+		    }
+		if (dst_prev->xfrm->xfrm_dev)
+			dst_prev->dev = dst_prev->xfrm->xfrm_dev;
+		else
+			dst_prev->dev = rt->u.dst.dev;
+		printk ("xfrm4_policy: Device: %s (0x%p)\n", dst_prev->dev->name, dst_prev->dev);
 		if (rt->u.dst.dev)
 			dev_hold(rt->u.dst.dev);
 		dst_prev->obsolete	= -1;
diff -pruN -x .config linux-2.6.8.1/net/xfrm/xfrm_state.c linux-2.6.8.1-xfrm_dev/net/xfrm/xfrm_state.c
--- linux-2.6.8.1/net/xfrm/xfrm_state.c	2004-08-14 12:55:48.000000000 +0200
+++ linux-2.6.8.1-xfrm_dev/net/xfrm/xfrm_state.c	2004-09-13 12:13:52.000000000 +0200
@@ -184,6 +184,9 @@ struct xfrm_state *xfrm_state_alloc(void
 		x->lft.hard_byte_limit = XFRM_INF;
 		x->lft.hard_packet_limit = XFRM_INF;
 		x->lock = SPIN_LOCK_UNLOCKED;
+		printk("xfrm_state_alloc: 0x%p\n",x);
+		x->xfrm_dev_name = "xfrm0";
+		x->xfrm_dev = NULL;
 	}
 	return x;
 }
