diff -NurpP --minimal linux-2.6.8-rc1-vs1.9.2.2-ngn02/drivers/net/Kconfig linux-2.6.8-rc1-vs1.9.2.2-ngn03/drivers/net/Kconfig
--- linux-2.6.8-rc1-vs1.9.2.2-ngn02/drivers/net/Kconfig	2004-07-12 14:46:51.000000000 +0200
+++ linux-2.6.8-rc1-vs1.9.2.2-ngn03/drivers/net/Kconfig	2004-07-29 03:09:26.000000000 +0200
@@ -104,6 +104,12 @@ config TUN
 
 	  If you don't know what to use this for, you don't need it.
 
+config VNET
+	tristate "Virtual Network device driver support"
+	depends on NETDEVICES
+	---help---
+	  VNET provides packet reception and transmission
+
 config ETHERTAP
 	tristate "Ethertap network tap"
 	depends on NETDEVICES && EXPERIMENTAL && NETLINK_DEV
diff -NurpP --minimal linux-2.6.8-rc1-vs1.9.2.2-ngn02/drivers/net/Makefile linux-2.6.8-rc1-vs1.9.2.2-ngn03/drivers/net/Makefile
--- linux-2.6.8-rc1-vs1.9.2.2-ngn02/drivers/net/Makefile	2004-07-12 14:46:51.000000000 +0200
+++ linux-2.6.8-rc1-vs1.9.2.2-ngn03/drivers/net/Makefile	2004-07-29 03:09:26.000000000 +0200
@@ -170,6 +170,7 @@ obj-$(CONFIG_MACSONIC) += macsonic.o
 obj-$(CONFIG_MACMACE) += macmace.o
 obj-$(CONFIG_MAC89x0) += mac89x0.o
 obj-$(CONFIG_TUN) += tun.o
+obj-$(CONFIG_VNET) += vnet.o
 obj-$(CONFIG_DL2K) += dl2k.o
 obj-$(CONFIG_R8169) += r8169.o
 obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
diff -NurpP --minimal linux-2.6.8-rc1-vs1.9.2.2-ngn02/drivers/net/vnet.c linux-2.6.8-rc1-vs1.9.2.2-ngn03/drivers/net/vnet.c
--- linux-2.6.8-rc1-vs1.9.2.2-ngn02/drivers/net/vnet.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.8-rc1-vs1.9.2.2-ngn03/drivers/net/vnet.c	2004-07-29 03:14:19.000000000 +0200
@@ -0,0 +1,581 @@
+/*
+ *  VNET device driver.
+ *  Copyright 2003-2004 by Herbert Pötzl <herbert@13thfloor.at>
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/vs_base.h>
+/*
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/random.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_tun.h>
+*/
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+
+struct vnet_struct {
+	struct list_head list;
+
+	int attached;
+
+	struct net_device_stats stats;
+	struct sk_buff_head tx_queue;
+	struct net_device *dev;
+
+};
+
+
+
+/* Network device part of the driver */
+
+static LIST_HEAD(vnet_dev_list);
+
+/* Net device open. */
+static int vnet_net_open(struct net_device *dev)
+{
+	/* check if configured */
+	vxdprintk("net_open(%p)\n", dev);
+	// netif_start_queue(dev);
+	return 0;
+}
+
+/* Net device close. */
+static int vnet_net_close(struct net_device *dev)
+{
+	vxdprintk("net_close(%p)\n", dev);
+	// netif_stop_queue(dev);
+	// vnetdev_flush_queue(vnetdev);
+	return 0;
+}
+
+/* Net device stop. */
+static int vnet_net_stop(struct net_device *dev)
+{
+	vxdprintk("net_stop(%p)\n", dev);
+	// netif_stop_queue(dev);
+	// vnetdev_flush_queue(vnetdev);
+	return 0;
+}
+
+
+/* Net device start xmit */
+static int vnet_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct vnet_struct *vnet = (struct vnet_struct *)dev->priv;
+
+	vxdprintk("net_xmit(%p[%d],%p[%s])\n", skb, skb->len, dev, vnet->dev->name);
+
+	/* Drop packet if interface is not attached */
+	if (!vnet->attached)
+		goto drop;
+
+#if 0
+	/* Queue packet */
+	if (!(vnet->flags & TUN_ONE_QUEUE)) {
+		/* Normal queueing mode.
+		 * Packet scheduler handles dropping. */
+		if (skb_queue_len(&vnet->readq) >= TUN_READQ_SIZE)
+			netif_stop_queue(dev);
+	} else {
+		/* Single queue mode.
+		 * Driver handles dropping itself. */
+		if (skb_queue_len(&vnet->readq) >= dev->tx_queue_len)
+			goto drop;
+	}
+	skb_queue_tail(&vnet->readq, skb);
+
+	/* Notify and wake up reader process */
+	if (tun->flags & TUN_FASYNC)
+		kill_fasync(&tun->fasync, SIGIO, POLL_IN);
+	wake_up_interruptible(&tun->read_wait);
+#endif
+	return 0;
+
+drop:
+	vnet->stats.tx_dropped++;
+	kfree_skb(skb);
+	return 0;
+}
+
+static void vnet_net_mclist(struct net_device *dev)
+{
+	/* Nothing to do for multicast filters.
+	 * We always accept all frames. */
+	return;
+}
+
+static struct net_device_stats *vnet_net_stats(struct net_device *dev)
+{
+	struct vnet_struct *vnet = (struct vnet_struct *)dev->priv;
+	return &vnet->stats;
+}
+
+/* Initialize net device. */
+static void vnet_net_init(struct net_device *dev)
+{
+	struct vnet_struct *vnet = (struct vnet_struct *)dev->priv;
+
+	skb_queue_head_init(&vnet->tx_queue);
+	dev->tx_queue_len = 100;
+
+	dev->set_mac_address = NULL;
+	dev->open = vnet_net_open;
+	dev->stop = vnet_net_stop;
+	dev->priv_flags |= IFF_VIRTUAL;
+	
+#if 0
+	switch (tun->flags & TUN_TYPE_MASK) {
+	case TUN_TUN_DEV:
+		/* Point-to-Point TUN Device */
+		dev->hard_header_len = 0;
+		dev->addr_len = 0;
+		dev->mtu = 1500;
+
+		/* Type PPP seems most suitable */
+		dev->type = ARPHRD_PPP;
+		dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+		dev->tx_queue_len = 10;
+		break;
+
+	case TUN_TAP_DEV:
+		/* Ethernet TAP Device */
+		dev->set_multicast_list = tun_net_mclist;
+
+		/* Generate random Ethernet address.  */
+		*(u16 *)dev->dev_addr = htons(0x00FF);
+		get_random_bytes(dev->dev_addr + sizeof(u16), 4);
+
+		ether_setup(dev);
+		break;
+	}
+#endif
+}
+
+static void x_vnet_setup(struct net_device *dev)
+{
+	struct vnet_struct *vnet = (struct vnet_struct *)dev->priv;
+
+	// skb_queue_head_init(&vnet->readq);
+	// init_waitqueue_head(&vnet->read_wait);
+
+	// vnet->owner = -1;
+
+	SET_MODULE_OWNER(dev);
+	dev->open = vnet_net_open;
+	dev->hard_start_xmit = vnet_net_xmit;
+	dev->stop = vnet_net_close;
+	dev->get_stats = vnet_net_stats;
+	dev->destructor = free_netdev;
+}
+
+static struct vnet_struct *vnet_get_by_name(const char *name)
+{
+	struct vnet_struct *vnet;
+
+	// ASSERT_RTNL();
+	list_for_each_entry(vnet, &vnet_dev_list, list) {
+		if (!strncmp(vnet->dev->name, name, IFNAMSIZ))
+		    return vnet;
+	}
+
+	return NULL;
+}
+
+#if 0
+static int tun_set_iff(struct file *file, struct ifreq *ifr)
+{
+	struct tun_struct *tun;
+	struct net_device *dev;
+	int err;
+
+	tun = tun_get_by_name(ifr->ifr_name);
+	if (tun) {
+		if (tun->attached)
+			return -EBUSY;
+
+		/* Check permissions */
+		if (tun->owner != -1 &&
+		    current->euid != tun->owner && !capable(CAP_NET_ADMIN))
+			return -EPERM;
+	}
+	else if (__dev_get_by_name(ifr->ifr_name))
+		return -EINVAL;
+	else {
+		char *name;
+		unsigned long flags = 0;
+
+		err = -EINVAL;
+
+		/* Set dev type */
+		if (ifr->ifr_flags & IFF_TUN) {
+			/* TUN device */
+			flags |= TUN_TUN_DEV;
+			name = "tun%d";
+		} else if (ifr->ifr_flags & IFF_TAP) {
+			/* TAP device */
+			flags |= TUN_TAP_DEV;
+			name = "tap%d";
+		} else
+			goto failed;
+
+		if (*ifr->ifr_name)
+			name = ifr->ifr_name;
+
+		dev = alloc_netdev(sizeof(struct tun_struct), name,
+				   tun_setup);
+		if (!dev)
+			return -ENOMEM;
+
+		tun = dev->priv;
+		tun->dev = dev;
+		tun->flags = flags;
+
+		tun_net_init(dev);
+
+		if (strchr(dev->name, '%')) {
+			err = dev_alloc_name(dev, dev->name);
+			if (err < 0)
+				goto err_free_dev;
+		}
+
+		err = register_netdevice(tun->dev);
+		if (err < 0)
+			goto err_free_dev;
+	
+		list_add(&tun->list, &tun_dev_list);
+	}
+
+	DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name);
+
+	if (ifr->ifr_flags & IFF_NO_PI)
+		tun->flags |= TUN_NO_PI;
+
+	if (ifr->ifr_flags & IFF_ONE_QUEUE)
+		tun->flags |= TUN_ONE_QUEUE;
+
+	file->private_data = tun;
+	tun->attached = 1;
+
+	strcpy(ifr->ifr_name, tun->dev->name);
+	return 0;
+
+ err_free_dev:
+	free_netdev(dev);
+ failed:
+	return err;
+}
+
+static int tun_chr_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct tun_struct *tun = (struct tun_struct *)file->private_data;
+
+	if (cmd == TUNSETIFF && !tun) {
+		struct ifreq ifr;
+		int err;
+
+		if (copy_from_user(&ifr, (void *)arg, sizeof(ifr)))
+			return -EFAULT;
+		ifr.ifr_name[IFNAMSIZ-1] = '\0';
+
+		rtnl_lock();
+		err = tun_set_iff(file, &ifr);
+		rtnl_unlock();
+
+		if (err)
+			return err;
+
+		if (copy_to_user((void *)arg, &ifr, sizeof(ifr)))
+			return -EFAULT;
+		return 0;
+	}
+
+	if (!tun)
+		return -EBADFD;
+
+	DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd);
+
+	switch (cmd) {
+	case TUNSETNOCSUM:
+		/* Disable/Enable checksum */
+		if (arg)
+			tun->flags |= TUN_NOCHECKSUM;
+		else
+			tun->flags &= ~TUN_NOCHECKSUM;
+
+		DBG(KERN_INFO "%s: checksum %s\n",
+		    tun->dev->name, arg ? "disabled" : "enabled");
+		break;
+
+	case TUNSETPERSIST:
+		/* Disable/Enable persist mode */
+		if (arg)
+			tun->flags |= TUN_PERSIST;
+		else
+			tun->flags &= ~TUN_PERSIST;
+
+		DBG(KERN_INFO "%s: persist %s\n",
+		    tun->dev->name, arg ? "disabled" : "enabled");
+		break;
+
+	case TUNSETOWNER:
+		/* Set owner of the device */
+		tun->owner = (uid_t) arg;
+
+		DBG(KERN_INFO "%s: owner set to %d\n", tun->dev->name, tun->owner);
+		break;
+
+#ifdef TUN_DEBUG
+	case TUNSETDEBUG:
+		tun->debug = arg;
+		break;
+#endif
+
+	default:
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
+static int tun_chr_fasync(int fd, struct file *file, int on)
+{
+	struct tun_struct *tun = (struct tun_struct *)file->private_data;
+	int ret;
+
+	if (!tun)
+		return -EBADFD;
+
+	DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on);
+
+	if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0)
+		return ret;
+
+	if (on) {
+		ret = f_setown(file, current->pid, 0);
+		if (ret)
+			return ret;
+		tun->flags |= TUN_FASYNC;
+	} else
+		tun->flags &= ~TUN_FASYNC;
+
+	return 0;
+}
+
+static int tun_chr_open(struct inode *inode, struct file * file)
+{
+	DBG1(KERN_INFO "tunX: tun_chr_open\n");
+	file->private_data = NULL;
+	return 0;
+}
+
+static int tun_chr_close(struct inode *inode, struct file *file)
+{
+	struct tun_struct *tun = (struct tun_struct *)file->private_data;
+
+	if (!tun)
+		return 0;
+
+	DBG(KERN_INFO "%s: tun_chr_close\n", tun->dev->name);
+
+	tun_chr_fasync(-1, file, 0);
+
+	rtnl_lock();
+
+	/* Detach from net device */
+	file->private_data = NULL;
+	tun->attached = 0;
+
+	/* Drop read queue */
+	skb_queue_purge(&tun->readq);
+
+	if (!(tun->flags & TUN_PERSIST)) {
+		list_del(&tun->list);
+		unregister_netdevice(tun->dev);
+	}
+
+	rtnl_unlock();
+
+	return 0;
+}
+
+static struct file_operations vnet_fops = {
+	.owner	= THIS_MODULE,	
+	.llseek = no_llseek,
+/*
+	.read	= tun_chr_read,
+	.readv	= tun_chr_readv,
+	.write	= tun_chr_write,
+	.writev = tun_chr_writev,
+	.poll	= tun_chr_poll,
+	.ioctl	= tun_chr_ioctl,
+	.open	= tun_chr_open,
+	.release = tun_chr_close,
+	.fasync = tun_chr_fasync		
+*/
+};
+
+#endif
+
+#define VNET_MINOR     201
+
+static struct miscdevice vnet_miscdev = {
+	.minor = VNET_MINOR,
+	.name = "vnet",
+//	.fops = &vnet_fops
+};
+
+int __init vnet_init(void)
+{
+	printk(KERN_INFO "VNET device driver "
+	       "(C)2003-2004 Herbert Poetzl\n" );
+	printk("VNET device driver "
+	       "(C)2003-2004 Herbert Poetzl\n" );
+
+	if (misc_register(&vnet_miscdev)) {
+		printk(KERN_ERR "vnet: Can't register misc device %d\n", VNET_MINOR);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#if 0
+void vnet_cleanup(void)
+{
+	struct tun_struct *tun, *nxt;
+
+	misc_deregister(&tun_miscdev);
+
+	rtnl_lock();
+	list_for_each_entry_safe(tun, nxt, &tun_dev_list, list) {
+		DBG(KERN_INFO "%s cleaned up\n", tun->dev->name);
+		unregister_netdevice(tun->dev);
+	}
+	rtnl_unlock();
+	
+}
+#endif
+
+module_init(vnet_init);
+// module_exit(vnet_cleanup);
+MODULE_LICENSE("GPL");
+
+static int vnet_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 *vnet_get_stats(struct net_device *dev)
+{
+	return dev->priv;
+}
+
+
+static void __init vnet_setup(struct net_device *dev)
+{
+	/* Initialize the device structure. */
+	dev->get_stats = vnet_get_stats;
+	dev->hard_start_xmit = vnet_xmit;
+//	dev->set_multicast_list = set_multicast_list;
+#ifdef CONFIG_NET_FASTROUTE
+//	dev->accept_fastpath = dummy_accept_fastpath;
+#endif
+
+	/* Fill in device structure with ethernet-generic values. */
+	ether_setup(dev);
+	dev->tx_queue_len = 0;
+	dev->flags |= IFF_NOARP;
+	dev->flags &= ~IFF_MULTICAST;
+	dev->priv_flags |= IFF_VIRTUAL;
+	SET_MODULE_OWNER(dev);
+}
+
+/* Number of dummy devices to be set up by this module. */
+
+static int __init vnet_init_one(void)
+{
+	struct net_device *dev_vnet;
+	int err;
+
+	dev_vnet = alloc_netdev(sizeof(struct net_device_stats),
+				 "vnet%d", vnet_setup);
+
+	if (!dev_vnet)
+		return -ENOMEM;
+
+	if ((err = register_netdev(dev_vnet))) {
+		free_netdev(dev_vnet);
+		dev_vnet = NULL;
+	} else {
+	//	dummies[index] = dev_dummy;
+	}
+
+	return err;
+}
+
+/*
+
+static void dummy_free_one(int index)
+{
+	unregister_netdev(dummies[index]);
+	free_netdev(dummies[index]);
+}
+
+*/
+
+static int __init vnet_init_module(void)
+{
+	int err = 0;
+
+	err = vnet_init_one();
+	return err;
+}
+
+/*
+
+static void __exit dummy_cleanup_module(void)
+{
+	int i;
+	for (i = 0; i < numdummies; i++)
+		dummy_free_one(i);
+	kfree(dummies);	
+}
+*/
+
+module_init(vnet_init_module);
+
+// module_exit(dummy_cleanup_module);
diff -NurpP --minimal linux-2.6.8-rc1-vs1.9.2.2-ngn02/include/linux/if.h linux-2.6.8-rc1-vs1.9.2.2-ngn03/include/linux/if.h
--- linux-2.6.8-rc1-vs1.9.2.2-ngn02/include/linux/if.h	2004-07-12 14:47:13.000000000 +0200
+++ linux-2.6.8-rc1-vs1.9.2.2-ngn03/include/linux/if.h	2004-07-29 03:09:26.000000000 +0200
@@ -53,6 +53,8 @@
 #define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
 #define IFF_EBRIDGE	0x2		/* Ethernet bridging device.	*/
 
+#define	IFF_VIRTUAL	0x8000		/* Virtual device, check ip	*/
+
 #define IF_GET_IFACE	0x0001		/* for querying only */
 #define IF_GET_PROTO	0x0002
 
diff -NurpP --minimal linux-2.6.8-rc1-vs1.9.2.2-ngn02/net/core/dev.c linux-2.6.8-rc1-vs1.9.2.2-ngn03/net/core/dev.c
--- linux-2.6.8-rc1-vs1.9.2.2-ngn02/net/core/dev.c	2004-07-20 19:09:08.000000000 +0200
+++ linux-2.6.8-rc1-vs1.9.2.2-ngn03/net/core/dev.c	2004-07-29 03:09:26.000000000 +0200
@@ -1137,6 +1137,16 @@ int call_netdevice_notifiers(unsigned lo
 	return notifier_call_chain(&netdev_chain, val, v);
 }
 
+
+static inline int ip_vnet_match(struct sk_buff *skb, struct net_device *dev)
+{
+	if (!dev)
+		return 0;
+	if (!(dev->priv_flags & IFF_VIRTUAL))
+		return 0;
+	return 1;
+}
+
 /*
  *	Support routine. Sends outgoing frames to any network
  *	taps currently in use.
@@ -1152,7 +1162,7 @@ void dev_queue_xmit_nit(struct sk_buff *
 		/* Never send packets back to the socket
 		 * they originated from - MvS (miquels@drinkel.ow.org)
 		 */
-		if ((ptype->dev == dev || !ptype->dev) &&
+		if ((ptype->dev == dev || !ptype->dev || ip_vnet_match(skb, ptype->dev)) &&
 		    (ptype->af_packet_priv == NULL ||
 		     (struct sock *)ptype->af_packet_priv != skb->sk)) {
 			struct sk_buff *skb2= skb_clone(skb, GFP_ATOMIC);
