diff -Naur -X exclude-files ac_cur/Documentation/Configure.help ac/Documentation/Configure.help
--- ac_cur/Documentation/Configure.help	Sat Sep 15 15:56:05 2001
+++ ac/Documentation/Configure.help	Sat Sep 15 20:29:05 2001
@@ -13191,6 +13191,16 @@
   more than one without conflict.  If you don't need UML networking,
   say N.
 
+TUN/TAP transport
+CONFIG_UML_NET_TUNTAP
+  The UML TUN/TAP network transport allows a UML instance to exchange
+  packets with the host over a TUN/TAP device.  This option will only
+  work with a 2.4 host, unless you've applied the TUN/TAP patch to
+  your 2.2 host kernel.
+
+  To use this transport, your host kernel must have support for TUN/TAP
+  devices, either built-in or as a module.
+
 Multicast transport
 CONFIG_UML_NET_MCAST
   This Multicast User-Mode Linux network transport allows multiple
diff -Naur -X exclude-files ac_cur/arch/um/config.in ac/arch/um/config.in
--- ac_cur/arch/um/config.in	Sat Sep 15 19:20:55 2001
+++ ac/arch/um/config.in	Sat Sep 15 20:29:05 2001
@@ -85,6 +85,7 @@
       bool '  Virtual network device' CONFIG_UML_NET
       if [ "$CONFIG_UML_NET" != "n" ]; then
 	 bool '    Ethertap transport' CONFIG_UML_NET_ETHERTAP
+	 bool '    TUN/TAP transport' CONFIG_UML_NET_TUNTAP
 	 bool '    SLIP transport' CONFIG_UML_NET_SLIP
          bool '    Daemon transport' CONFIG_UML_NET_DAEMON
          bool '    Multicast transport' CONFIG_UML_NET_MCAST
diff -Naur -X exclude-files ac_cur/arch/um/defconfig ac/arch/um/defconfig
--- ac_cur/arch/um/defconfig	Sat Sep 15 19:20:55 2001
+++ ac/arch/um/defconfig	Sat Sep 15 20:29:05 2001
@@ -161,6 +161,7 @@
 CONFIG_NETDEVICES=y
 CONFIG_UML_NET=y
 CONFIG_UML_NET_ETHERTAP=y
+CONFIG_UML_NET_TUNTAP=y
 CONFIG_UML_NET_SLIP=y
 CONFIG_UML_NET_DAEMON=y
 CONFIG_UML_NET_MCAST=y
diff -Naur -X exclude-files ac_cur/arch/um/drivers/Makefile ac/arch/um/drivers/Makefile
--- ac_cur/arch/um/drivers/Makefile	Sat Sep 15 16:46:07 2001
+++ ac/arch/um/drivers/Makefile	Sat Sep 15 20:29:05 2001
@@ -19,9 +19,12 @@
 CFLAGS_slip_user.o := $(USER_CFLAGS)
 CFLAGS_ethertap_kern.o := $(CFLAGS)
 CFLAGS_ethertap_user.o := $(USER_CFLAGS)
+CFLAGS_tuntap_kern.o := $(CFLAGS)
+CFLAGS_tuntap_user.o := $(USER_CFLAGS)
 CFLAGS_daemon_kern.o := $(CFLAGS)
 CFLAGS_daemon_user.o := $(USER_CFLAGS)
 CFLAGS_net_kern.o := $(CFLAGS)
+CFLAGS_net_user.o := $(USER_CFLAGS)
 CFLAGS_mcast_user.o := $(USER_CFLAGS)
 CFLAGS_mcast_kern.o := $(CFLAGS)
 CFLAGS_mconsole_kern.o := $(CFLAGS)
@@ -33,9 +36,10 @@
 obj-$(CONFIG_SSL) += chan_user.o chan_kern.o
 obj-$(CONFIG_STDIO_CONSOLE) += chan_user.o chan_kern.o
 obj-$(CONFIG_BLK_DEV_UBD) += ubd.o ubd_user.o
-obj-$(CONFIG_UML_NET) += net_kern.o 
+obj-$(CONFIG_UML_NET) += net_kern.o net_user.o 
 obj-$(CONFIG_UML_NET_SLIP) += slip_user.o slip_kern.o
 obj-$(CONFIG_UML_NET_ETHERTAP) += ethertap_user.o ethertap_kern.o 
+obj-$(CONFIG_UML_NET_TUNTAP) += tuntap_user.o tuntap_kern.o 
 obj-$(CONFIG_UML_NET_DAEMON) += daemon_kern.o daemon_user.o
 obj-$(CONFIG_UML_NET_MCAST) += mcast_user.o mcast_kern.o
 obj-$(CONFIG_MCONSOLE) += mconsole_kern.o mconsole_user.o
diff -Naur -X exclude-files ac_cur/arch/um/drivers/daemon_kern.c ac/arch/um/drivers/daemon_kern.c
--- ac_cur/arch/um/drivers/daemon_kern.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/daemon_kern.c	Sat Sep 15 20:29:05 2001
@@ -71,10 +71,10 @@
 				(struct daemon_data *) &lp->user));
 }
 
-static int daemon_write(int fd, struct sk_buff *skb,
+static int daemon_write(int fd, struct sk_buff **skb,
 			struct uml_net_private *lp)
 {
-	return(daemon_user_write(fd, skb->data, skb->len, 
+	return(daemon_user_write(fd, (*skb)->data, (*skb)->len, 
 				 (struct daemon_data *) &lp->user));
 }
 
diff -Naur -X exclude-files ac_cur/arch/um/drivers/etap_kern.h ac/arch/um/drivers/etap_kern.h
--- ac_cur/arch/um/drivers/etap_kern.h	Sat Sep 15 20:23:31 2001
+++ ac/arch/um/drivers/etap_kern.h	Sat Sep 15 20:39:52 2001
@@ -1,3 +1,8 @@
+/* 
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
 #ifndef __UM_ETHERTAP_KERN_H
 #define __UM_ETHERTAP_KERN_H
 
@@ -6,3 +11,14 @@
 extern void ethertap_setup(char *arg, struct uml_net *dev);
 
 #endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -Naur -X exclude-files ac_cur/arch/um/drivers/ethertap_kern.c ac/arch/um/drivers/ethertap_kern.c
--- ac_cur/arch/um/drivers/ethertap_kern.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/ethertap_kern.c	Sat Sep 15 20:31:21 2001
@@ -21,17 +21,11 @@
 };
 
 struct ethertap_setup ethertap_priv[MAX_UML_NETDEV] = { 
-	{
-		dev_name:	"tap0",
-		hw_addr:	{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
-		hw_setup:	0,
-		gate_addr:	NULL,
-	},
-	[ 1 ... MAX_UML_NETDEV - 1 ] =
+	[ 0 ... MAX_UML_NETDEV - 1 ] =
 	{
 		dev_name:	NULL,
 		hw_addr:	{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
-		hw_setup:	1,
+		hw_setup:	0,
 		gate_addr:	NULL,
 	}
 };
@@ -51,7 +45,7 @@
 	memcpy(dev->dev_addr, ethertap_priv[index].hw_addr, ETH_ALEN);
 	memcpy(epri->hw_addr, ethertap_priv[index].hw_addr, 
 	       sizeof(epri->hw_addr));
-	printk("TAP backend - %s", epri->dev_name);
+	printk("ethertap backend - %s", epri->dev_name);
 	if(epri->gate_addr != NULL) printk(", IP = %s", epri->gate_addr);
 	epri->hw_setup = ethertap_priv[index].hw_setup;
 	if(epri->hw_setup)
@@ -94,18 +88,18 @@
 	return(len);
 }
 
-static int etap_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
+static int etap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp)
 {
-	if(skb_headroom(skb) < 2){
+	if(skb_headroom(*skb) < 2){
 	  	struct sk_buff *skb2;
 
-		skb2 = skb_realloc_headroom(skb, 2);
-		dev_kfree_skb(skb);
+		skb2 = skb_realloc_headroom(*skb, 2);
+		dev_kfree_skb(*skb);
 		if (skb2 == NULL) return(-ENOMEM);
-		skb = skb2;
+		*skb = skb2;
 	}
-	skb_push(skb, 2);
-	return(etap_user_write(fd, skb->data, skb->len, 
+	skb_push(*skb, 2);
+	return(etap_user_write(fd, (*skb)->data, (*skb)->len, 
 			       (struct ethertap_data *) &lp->user));
 }
 
@@ -121,29 +115,15 @@
 
 void ethertap_setup(char *str, struct uml_net *dev)
 {
-	int err, n = ethertap_count;
+ 	struct ethertap_setup *pri;
 
 	dev->user = &ethertap_user_info;
 	dev->kern = &ethertap_kern_info;
 	dev->private_size = sizeof(struct ethertap_data);
+ 	pri = &ethertap_priv[ethertap_count];
 	dev->transport_index = ethertap_count++;
-	if(*str != ','){
-		printk("ethertap_setup: expected ',' after 'ethertap'\n");
-		return;
-	}
-	str++;
-	if(*str != ',') ethertap_priv[n].dev_name = str;
-	str = strchr(str, ',');
-	if(str == NULL) return;
-	*str++ = '\0';
-	if(*str != ','){
-		err = setup_etheraddr(str, ethertap_priv[n].hw_addr);
-		if(!err) ethertap_priv[n].hw_setup = 1;
-	}
-	str = strchr(str, ',');
-	if(str == NULL) return;
-	*str++ = '\0';
-	if(*str != '\0') ethertap_priv[n].gate_addr = str;
+ 	tap_setup_common(str, "ethertap", &pri->dev_name, pri->hw_addr,  
+ 			 &pri->hw_setup, &pri->gate_addr);
 }
 
 /*
diff -Naur -X exclude-files ac_cur/arch/um/drivers/ethertap_user.c ac/arch/um/drivers/ethertap_user.c
--- ac_cur/arch/um/drivers/ethertap_user.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/ethertap_user.c	Sat Sep 15 20:29:05 2001
@@ -14,6 +14,7 @@
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <sys/un.h>
+#include <net/if.h>
 #include "user.h"
 #include "kern_util.h"
 #include "net_user.h"
@@ -31,7 +32,6 @@
 struct etap_open_data {
 	char *name;
 	char *gate;
-	char *addr;
 	int data_remote;
 	int data_me;
 	int control_remote;
@@ -39,18 +39,45 @@
 	int err;
 };
 
-void etap_tramp(void *arg)
+struct addr_change {
+	enum { ADD_ADDR, DEL_ADDR } what;
+	unsigned char addr[4];
+};
+
+static void etap_open_addr(unsigned char *addr, void *arg)
+{
+	int fd = *((int *) arg);
+	struct addr_change change;
+
+	change.what = ADD_ADDR;
+	memcpy(change.addr, addr, sizeof(change.addr));
+	if(write(fd, &change, sizeof(change)) != sizeof(change))
+		printk("etap_add_addr - request failed, errno = %d\n",
+		       errno);
+}
+
+static void etap_close_addr(unsigned char *addr, void *arg)
+{
+	int fd = *((int *) arg);
+	struct addr_change change;
+
+	change.what = DEL_ADDR;
+	memcpy(change.addr, addr, sizeof(change.addr));
+	if(write(fd, &change, sizeof(change)) != sizeof(change))
+		printk("etap_close_addr - request failed, errno = %d\n",
+		       errno);
+}
+
+static void etap_tramp(void *arg)
 {
 	struct etap_open_data *data = arg;
 	int pid, status, n;
 	char version_buf[sizeof("nnnnn\0")];
 	char data_fd_buf[sizeof("nnnnnn\0")];
 	char control_fd_buf[sizeof("nnnnnn\0")];
-	char addr_buf[sizeof("nnn.nnn.nnn.nnn\0")];
 	char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")], c;
 	char *setup_args[] = { "uml_net", version_buf, "ethertap", data->name,
-			       data_fd_buf, control_fd_buf, gate_buf, 
-			       addr_buf, NULL };
+			       data_fd_buf, control_fd_buf, gate_buf, NULL };
 	char *nosetup_args[] = { "uml_net", version_buf, "ethertap", 
 				 data->name, data_fd_buf, control_fd_buf, 
 				 NULL };
@@ -60,14 +87,13 @@
 	sprintf(control_fd_buf, "%d", data->control_remote);
 	sprintf(version_buf, "%d", UML_NET_VERSION);
 	if(data->gate != NULL){
-		strcpy(addr_buf, data->addr);
 		strcpy(gate_buf, data->gate);
 		args = setup_args;
 	}
 	else args = nosetup_args;
 	data->err = 0;
 	if((pid = fork()) == 0){
-		char zero = 0;
+		char zero = 0; /* XXX Not necessary */
 
 		close(data->data_me);
 		close(data->control_me);
@@ -77,6 +103,8 @@
 		exit(errno);
 	}
 	else if(pid < 0) data->err = errno;
+	close(data->data_remote);
+	close(data->control_remote);
 	n = read(data->control_me, &c, sizeof(c));
 	if(n != sizeof(c)){
 		printk("etap_open - failed to read response from helper : "
@@ -91,78 +119,15 @@
 	else if(c != 1) data->err = EINVAL;
 }
 
-struct addr_change {
-	enum { ADD_ADDR, DEL_ADDR } what;
-	unsigned char addr[4];
-};
-
-static void etap_open_addr(unsigned char *addr, void *arg)
-{
-	int fd = *((int *) arg);
-	struct addr_change change;
-
-	change.what = ADD_ADDR;
-	memcpy(change.addr, addr, sizeof(change.addr));
-	if(write(fd, &change, sizeof(change)) != sizeof(change))
-		printk("etap_add_addr - request failed, errno = %d\n",
-		       errno);
-}
-
-static void etap_close_addr(unsigned char *addr, void *arg)
-{
-	int fd = *((int *) arg);
-	struct addr_change change;
-
-	change.what = DEL_ADDR;
-	memcpy(change.addr, addr, sizeof(change.addr));
-	if(write(fd, &change, sizeof(change)) != sizeof(change))
-		printk("etap_close_addr - request failed, errno = %d\n",
-		       errno);
-}
-
 static int etap_open(void *data)
 {
 	struct ethertap_data *pri = data;
 	struct etap_open_data tap_data;
-	int data_fds[2], control_fds[2];
-	char addr[sizeof("255.255.255.255\0")], ether[6];
+	int data_fds[2], control_fds[2], err;
+
+	err = tap_open_common(pri->dev, pri->hw_setup, pri->gate_addr);
+	if(err) return(err);
 
-	if((pri->gate_addr != NULL) || !pri->hw_setup){
-		ether[0] = 0xfe;
-		ether[1] = 0xfd;
-		ether[2] = 0x0;
-		ether[3] = 0x0;
-		ether[4] = 0x0;
-		ether[5] = 0x0;
-		dev_ip_addr(pri->dev, addr, &ether[2]);
-	}
-	if(pri->gate_addr != NULL){
-		int uml_addr[4], tap_addr[4];
-		if(sscanf(addr, "%d.%d.%d.%d", &uml_addr[0], &uml_addr[1], 
-			  &uml_addr[2], &uml_addr[3]) != 4){
-			printk("Invalid UML IP address - '%s'\n", addr);
-			return(-EINVAL);
-		}
-		if(sscanf(pri->gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
-			  &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){
-			printk("Invalid tap IP address - '%s'\n", 
-			       pri->gate_addr);
-			return(-EINVAL);
-		}
-		if((uml_addr[0] == tap_addr[0]) && 
-		   (uml_addr[1] == tap_addr[1]) && 
-		   (uml_addr[2] == tap_addr[2]) && 
-		   (uml_addr[3] == tap_addr[3])){
-			printk("The tap IP address and the UML eth IP address"
-			       " must be different\n");
-			return(-EINVAL);
-		}
-	}
-	if(!pri->hw_setup){
-		ether[0] = 0xfe;
-		ether[1] = 0xfd;
-		set_ether_mac(pri->dev, ether);
-	}
 	tap_data.name = pri->dev_name;
 
 	if(socketpair(PF_UNIX, SOCK_DGRAM, 0, data_fds) < 0){
@@ -180,7 +145,6 @@
 	tap_data.control_me = control_fds[0];
 	
 	tap_data.gate = pri->gate_addr;
-	tap_data.addr = addr;
 	tracing_cb(etap_tramp, &tap_data);
 	if(tap_data.err != 0){
 		printk("etap_tramp failed - errno = %d\n", tap_data.err);
@@ -209,10 +173,8 @@
 
 	while(((n = recvfrom(fd,  buf,  len, 0, NULL, NULL)) < 0) && 
 	      (errno == EINTR)) ;
-	if(n < 0){
-		if(errno == EAGAIN) return(0);
-		return(-errno);
-	}
+	if(errno == EAGAIN) return(0);
+	if(n < 0) return(-errno);
 	return(n);
 }
 
diff -Naur -X exclude-files ac_cur/arch/um/drivers/mcast_kern.c ac/arch/um/drivers/mcast_kern.c
--- ac_cur/arch/um/drivers/mcast_kern.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/mcast_kern.c	Sat Sep 15 20:29:05 2001
@@ -83,10 +83,10 @@
 			       (struct mcast_data *) &lp->user);
 }
 
-static int mcast_write(int fd, struct sk_buff *skb,
+static int mcast_write(int fd, struct sk_buff **skb,
 			struct uml_net_private *lp)
 {
-	return mcast_user_write(fd, skb->data, skb->len, 
+	return mcast_user_write(fd, (*skb)->data, (*skb)->len, 
 				 (struct mcast_data *) &lp->user);
 }
 
diff -Naur -X exclude-files ac_cur/arch/um/drivers/net_kern.c ac/arch/um/drivers/net_kern.c
--- ac_cur/arch/um/drivers/net_kern.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/net_kern.c	Sat Sep 15 20:29:05 2001
@@ -24,6 +24,8 @@
 #include "slip_kern.h"
 #include "etap.h"
 #include "etap_kern.h"
+#include "tuntap.h"
+#include "tuntap_kern.h"
 #include "daemon.h"
 #include "daemon_kern.h"
 #include "mcast.h"
@@ -74,6 +76,12 @@
 		return(0);
 	}
 #endif
+#ifdef CONFIG_UML_NET_TUNTAP
+	if(!strncmp(str, "tuntap", strlen("tuntap"))){
+		tuntap_setup(&str[strlen("tuntap")], &devices[n]);
+		return(0);
+	}
+#endif
 #ifdef CONFIG_UML_NET_DAEMON
 	if(!strncmp(str, "daemon", strlen("daemon"))){
 		daemon_setup(&str[strlen("daemon")], &devices[n]);
@@ -221,7 +229,7 @@
 
 	spin_lock_irqsave(&lp->lock, flags);
 
-	len = (*lp->write)(lp->fd, skb, lp);
+	len = (*lp->write)(lp->fd, &skb, lp);
 
 	if(len == skb->len) {
 		lp->stats.tx_packets++;
@@ -567,6 +575,45 @@
 	}
 }
 
+void *get_output_buffer(int *len_out)
+{
+	void *ret;
+
+	ret = (void *) __get_free_pages(GFP_KERNEL, 0);
+	if(ret) *len_out = PAGE_SIZE;
+	else *len_out = 0;
+	return(ret);
+}
+
+void free_output_buffer(void *buffer)
+{
+	free_pages((unsigned long) buffer, 0);
+}
+
+void tap_setup_common(char *str, char *type, char **dev_name, char *hw_addr, 
+		      int *hw_setup, char **gate_addr)
+{
+	int err;
+
+	if(*str != ','){
+		printk(KERN_ERR 
+		       "ethertap_setup: expected ',' after '%s'\n", type);
+		return;
+	}
+	str++;
+	if(*str != ',') *dev_name = str;
+	str = strchr(str, ',');
+	if(str == NULL) return;
+	*str++ = '\0';
+	if(*str != ','){
+		err = setup_etheraddr(str, hw_addr);
+		if(!err) *hw_setup = 1;
+	}
+	str = strchr(str, ',');
+	if(str == NULL) return;
+	*str++ = '\0';
+	if(*str != '\0') *gate_addr = str;
+}
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -Naur -X exclude-files ac_cur/arch/um/drivers/net_kern.h ac/arch/um/drivers/net_kern.h
--- ac_cur/arch/um/drivers/net_kern.h	Sat Sep 15 20:23:31 2001
+++ ac/arch/um/drivers/net_kern.h	Sat Sep 15 20:39:52 2001
@@ -28,7 +28,7 @@
 	int (*open)(void *);
 	void (*close)(int, void *);
 	int (*read)(int, struct sk_buff **skb, struct uml_net_private *);
-	int (*write)(int, struct sk_buff *skb, struct uml_net_private *);
+	int (*write)(int, struct sk_buff **skb, struct uml_net_private *);
 	
 	void (*add_address)(unsigned char *, void *);
 	void (*delete_address)(unsigned char *, void *);
@@ -41,7 +41,7 @@
 	unsigned short (*protocol)(struct sk_buff *);
 	int (*set_mac)(struct sockaddr *hwaddr, void *);
 	int (*read)(int, struct sk_buff **skb, struct uml_net_private *);
-	int (*write)(int, struct sk_buff *skb, struct uml_net_private *);
+	int (*write)(int, struct sk_buff **skb, struct uml_net_private *);
 };
 
 extern struct net_device *ether_init(int);
@@ -49,6 +49,8 @@
 extern int ether_set_mac(struct sockaddr *hwaddr, void *);
 extern int setup_etheraddr(char *str, unsigned char *addr);
 extern struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra);
+extern void tap_setup_common(char *str, char *type, char **dev_name, 
+			     char *hw_addr, int *hw_setup, char **gate_addr);
 
 #endif
 
diff -Naur -X exclude-files ac_cur/arch/um/drivers/net_user.c ac/arch/um/drivers/net_user.c
--- ac_cur/arch/um/drivers/net_user.c	Wed Dec 31 19:00:00 1969
+++ ac/arch/um/drivers/net_user.c	Sat Sep 15 20:29:05 2001
@@ -0,0 +1,65 @@
+/* 
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include "user.h"
+#include "net_user.h"
+
+int tap_open_common(void *dev, int hw_setup, char *gate_addr)
+{
+	char addr[sizeof("255.255.255.255\0")];
+	char ether[ETH_ADDR_LEN];
+
+	if((gate_addr != NULL) || !hw_setup){
+		ether[0] = 0xfe;
+		ether[1] = 0xfd;
+		ether[2] = 0x0;
+		ether[3] = 0x0;
+		ether[4] = 0x0;
+		ether[5] = 0x0;
+		dev_ip_addr(dev, addr, &ether[2]);
+	}
+	if(gate_addr != NULL){
+		int uml_addr[4], tap_addr[4];
+		if(sscanf(addr, "%d.%d.%d.%d", &uml_addr[0], &uml_addr[1], 
+			  &uml_addr[2], &uml_addr[3]) != 4){
+			printk("Invalid UML IP address - '%s'\n", addr);
+			return(-EINVAL);
+		}
+		if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
+			  &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){
+			printk("Invalid tap IP address - '%s'\n", 
+			       gate_addr);
+			return(-EINVAL);
+		}
+		if((uml_addr[0] == tap_addr[0]) && 
+		   (uml_addr[1] == tap_addr[1]) && 
+		   (uml_addr[2] == tap_addr[2]) && 
+		   (uml_addr[3] == tap_addr[3])){
+			printk("The tap IP address and the UML eth IP address"
+			       " must be different\n");
+			return(-EINVAL);
+		}
+	}
+	if(!hw_setup){
+		ether[0] = 0xfe;
+		ether[1] = 0xfd;
+		set_ether_mac(dev, ether);
+	}
+	return(0);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -Naur -X exclude-files ac_cur/arch/um/drivers/net_user.h ac/arch/um/drivers/net_user.h
--- ac_cur/arch/um/drivers/net_user.h	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/net_user.h	Sat Sep 15 20:29:05 2001
@@ -6,7 +6,7 @@
 #define ETH_HEADER_OTHER (14)
 #define ETH_MAX_PACKET (1500)
 
-#define UML_NET_VERSION (1)
+#define UML_NET_VERSION (2)
 
 struct net_user_info {
 	void (*init)(void *, void *);
@@ -23,6 +23,11 @@
 extern void set_ether_mac(void *d, unsigned char *addr);
 extern void iter_addresses(void *d, void (*cb)(unsigned char *, void *), 
 			   void *arg);
+
+extern void *get_output_buffer(int *len_out);
+extern void free_output_buffer(void *buffer);
+
+extern int tap_open_common(void *dev, int hw_setup, char *gate_addr);
 
 #endif
 
diff -Naur -X exclude-files ac_cur/arch/um/drivers/slip_kern.c ac/arch/um/drivers/slip_kern.c
--- ac_cur/arch/um/drivers/slip_kern.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/drivers/slip_kern.c	Sat Sep 15 20:29:05 2001
@@ -63,10 +63,10 @@
 			      (struct slip_data *) &lp->user));
 }
 
-static int slip_write(int fd, struct sk_buff *skb,
+static int slip_write(int fd, struct sk_buff **skb,
 		      struct uml_net_private *lp)
 {
-	return(slip_user_write(fd, skb->data, skb->len, 
+	return(slip_user_write(fd, (*skb)->data, (*skb)->len, 
 			       (struct slip_data *) &lp->user));
 }
 
diff -Naur -X exclude-files ac_cur/arch/um/drivers/tuntap.h ac/arch/um/drivers/tuntap.h
--- ac_cur/arch/um/drivers/tuntap.h	Wed Dec 31 19:00:00 1969
+++ ac/arch/um/drivers/tuntap.h	Sat Sep 15 20:29:05 2001
@@ -0,0 +1,38 @@
+/* 
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_TUNTAP_H
+#define __UM_TUNTAP_H
+
+#include "net_user.h"
+
+struct tuntap_data {
+	char *dev_name;
+	char *gate_addr;
+	int fd;
+	void *dev;
+	unsigned char hw_addr[ETH_ADDR_LEN];
+	int hw_setup;
+};
+
+extern struct net_user_info tuntap_user_info;
+
+extern int tuntap_user_write(int fd, void *buf, int len, 
+			     struct tuntap_data *pri);
+extern int tuntap_user_read(int fd, void *buf, int len, 
+			    struct tuntap_data *pri);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -Naur -X exclude-files ac_cur/arch/um/drivers/tuntap_kern.c ac/arch/um/drivers/tuntap_kern.c
--- ac_cur/arch/um/drivers/tuntap_kern.c	Wed Dec 31 19:00:00 1969
+++ ac/arch/um/drivers/tuntap_kern.c	Sat Sep 15 20:29:05 2001
@@ -0,0 +1,130 @@
+/* 
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/netdevice.h"
+#include "linux/etherdevice.h"
+#include "linux/skbuff.h"
+#include "asm/errno.h"
+#include "net_kern.h"
+#include "net_user.h"
+#include "tuntap.h"
+
+struct tuntap_setup {
+	char *dev_name;
+	unsigned char hw_addr[ETH_ALEN];
+	int hw_setup;
+	char *gate_addr;
+};
+
+struct tuntap_setup tuntap_priv[MAX_UML_NETDEV] = { 
+	[ 0 ... MAX_UML_NETDEV - 1 ] =
+	{
+		dev_name:	NULL,
+		hw_addr:	{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+		hw_setup:	0,
+		gate_addr:	NULL,
+	}
+};
+
+struct net_device *tuntap_init(int private_size, int index)
+{
+	struct net_device *dev;
+	struct uml_net_private *pri;
+	struct tuntap_data *tpri;
+
+	dev = init_etherdev(NULL, private_size);
+	if(dev == NULL) return(NULL);
+	pri = dev->priv;
+	tpri = (struct tuntap_data *) pri->user;
+	tpri->dev_name = tuntap_priv[index].dev_name;
+	tpri->gate_addr = tuntap_priv[index].gate_addr;
+	memcpy(dev->dev_addr, tuntap_priv[index].hw_addr, ETH_ALEN);
+	memcpy(tpri->hw_addr, tuntap_priv[index].hw_addr, 
+	       sizeof(tpri->hw_addr));
+	printk("TUN/TAP backend - ");
+	if(tpri->gate_addr != NULL) 
+		printk("IP = %s", tpri->gate_addr);
+	tpri->hw_setup = tuntap_priv[index].hw_setup;
+	if(tpri->hw_setup)
+		printk(" ether = %x:%x:%x:%x:%x:%x",
+		       tpri->hw_addr[0], tpri->hw_addr[1], tpri->hw_addr[2],
+		       tpri->hw_addr[3], tpri->hw_addr[4], tpri->hw_addr[5]);
+	printk("\n");
+	tpri->fd = -1;
+	return(dev);
+}
+
+static unsigned short tuntap_protocol(struct sk_buff *skb)
+{
+	return(eth_type_trans(skb, skb->dev));
+}
+
+static int tuntap_set_mac(struct sockaddr *addr, void *data)
+{
+	struct tuntap_data *pri = data;
+	struct sockaddr *hwaddr = addr;
+
+	memcpy(pri->hw_addr, hwaddr->sa_data, ETH_ALEN);
+
+	return 0;
+}
+
+static int tuntap_read(int fd, struct sk_buff **skb, 
+		       struct uml_net_private *lp)
+{
+	int len;
+
+	*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
+	if(*skb == NULL) return(-ENOMEM);
+	len = tuntap_user_read(fd, (*skb)->mac.raw, 
+			       (*skb)->dev->mtu + ETH_HEADER_OTHER, 
+			       (struct tuntap_data *) &lp->user);
+	if(len <= 0) return(len);
+	return(len);
+}
+
+static int tuntap_write(int fd, struct sk_buff **skb, 
+			struct uml_net_private *lp)
+{
+	return(tuntap_user_write(fd, (*skb)->data, (*skb)->len, 
+				 (struct tuntap_data *) &lp->user));
+}
+
+struct net_kern_info tuntap_kern_info = {
+	init:			tuntap_init,
+	protocol:		tuntap_protocol,
+	set_mac:		tuntap_set_mac,
+	read:			tuntap_read,
+	write: 			tuntap_write,
+};
+
+static int tuntap_count = 0;
+
+void tuntap_setup(char *str, struct uml_net *dev)
+{
+	struct tuntap_setup *pri;
+
+	dev->user = &tuntap_user_info;
+	dev->kern = &tuntap_kern_info;
+	dev->user = &tuntap_user_info;
+	dev->kern = &tuntap_kern_info;
+	dev->private_size = sizeof(struct tuntap_data);
+	pri = &tuntap_priv[tuntap_count];
+	dev->transport_index = tuntap_count++;
+	tap_setup_common(str, "tuntap", &pri->dev_name, pri->hw_addr,  
+			 &pri->hw_setup, &pri->gate_addr);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -Naur -X exclude-files ac_cur/arch/um/drivers/tuntap_kern.h ac/arch/um/drivers/tuntap_kern.h
--- ac_cur/arch/um/drivers/tuntap_kern.h	Wed Dec 31 19:00:00 1969
+++ ac/arch/um/drivers/tuntap_kern.h	Sat Sep 15 20:39:52 2001
@@ -0,0 +1,24 @@
+/* 
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_TUNTAP_KERN_H
+#define __UM_TUNTAP_KERN_H
+
+#include "net_kern.h"
+
+extern void tuntap_setup(char *arg, struct uml_net *dev);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -Naur -X exclude-files ac_cur/arch/um/drivers/tuntap_user.c ac/arch/um/drivers/tuntap_user.c
--- ac_cur/arch/um/drivers/tuntap_user.c	Wed Dec 31 19:00:00 1969
+++ ac/arch/um/drivers/tuntap_user.c	Sat Sep 15 20:29:05 2001
@@ -0,0 +1,264 @@
+/* 
+ * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include "net_user.h"
+#include "tuntap.h"
+#include "user.h"
+#include "kern_util.h"
+
+#define MAX_PACKET ETH_MAX_PACKET
+
+void tuntap_user_init(void *data, void *dev)
+{
+	struct tuntap_data *pri = data;
+
+	pri->dev = dev;
+}
+
+struct tuntap_open_data {
+	char *name;
+	char *gate;
+	int data_fd;
+	int remote;
+	int me;
+	int err;
+	char *buffer;
+	int len;
+	int used;
+};
+
+static void tuntap_open_tramp(void *arg)
+{
+	struct tuntap_open_data *data = arg;
+	char version_buf[sizeof("nnnnn\0")];
+	char fd_buf[sizeof("nnnnnn\0")];
+	char *args[] = { "uml_net", version_buf, "tuntap", "up", "", fd_buf,
+			 data->gate, NULL };
+	char buf[CMSG_SPACE(sizeof(data->data_fd))];
+	struct msghdr msg;
+	struct cmsghdr *cmsg;
+	struct iovec iov;
+	int pid, n;
+
+	sprintf(version_buf, "%d", UML_NET_VERSION);
+	sprintf(fd_buf, "%d", data->remote);
+	data->err = 0;
+	if((pid = fork()) == 0){
+		close(data->me);
+		execvp(args[0], args);
+		printk("Exec of '%s' failed - errno = %d\n", args[0], errno);
+		exit(1);		
+	}	
+	else if(pid < 0) data->err = errno;
+	close(data->remote);
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	if(data->buffer != NULL){
+		iov = ((struct iovec) { data->buffer, data->len });
+		msg.msg_iov = &iov;
+		msg.msg_iovlen = 1;
+	}
+	else {
+		msg.msg_iov = NULL;
+		msg.msg_iovlen = 0;
+	}
+	msg.msg_control = buf;
+	msg.msg_controllen = sizeof(buf);
+	msg.msg_flags = 0;
+	n = recvmsg(data->me, &msg, 0);
+	data->used = n;
+	if(n < 0){
+		printk("tuntap_open_tramp : recvmsg failed - errno = %d\n", 
+		       errno);
+		data->err = errno;
+		return;
+	}
+	waitpid(pid, NULL, 0);
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if(cmsg == NULL){
+		printk("tuntap_open_tramp : didn't receive a message\n");
+		data->err = EINVAL;
+		return;
+	}
+	if((cmsg->cmsg_level != SOL_SOCKET) || 
+	   (cmsg->cmsg_type != SCM_RIGHTS)){
+		printk("tuntap_open_tramp : didn't receive a descriptor\n");
+		data->err = EINVAL;
+		return;
+	}
+	data->data_fd = ((int *) CMSG_DATA(cmsg))[0];
+}
+
+struct tuntap_change_data {
+	char *dev;
+	char *what;
+	char *address;
+};
+
+static void tuntap_change_tramp(void *arg)
+{
+	int pid;
+	struct tuntap_change_data *data = arg;
+	char version[sizeof("nnnnn\0")];
+	char *argv[] = { "uml_net", version, "tuntap", data->what, data->dev, 
+			 data->address, NULL };
+
+	sprintf(version, "%d", UML_NET_VERSION);
+	if((pid = fork()) == 0){
+		execvp(argv[0], argv);
+		printk("Exec of '%s' failed - errno = %d\n", argv[0], errno);
+		exit(1);		
+	}	
+	waitpid(pid, NULL, 0);
+}
+
+static void tuntap_change(char *dev, char *what, unsigned char *addr)
+{
+	char addr_buf[sizeof("255.255.255.255\0")];
+	struct tuntap_change_data data;
+
+	data.dev = dev;
+	data.what = what;
+	sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
+	data.address = addr_buf;
+	tracing_cb(tuntap_change_tramp, &data);	
+}
+
+static void tuntap_open_addr(unsigned char *addr, void *arg)
+{
+	char *dev = arg;
+
+	tuntap_change(dev, "add", addr);
+}
+
+static void tuntap_close_addr(unsigned char *addr, void *arg)
+{
+	char *dev = arg;
+
+	tuntap_change(dev, "del", addr);
+}
+
+static void tuntap_add_addr(unsigned char *addr, void *data)
+{
+	struct tuntap_data *pri = data;
+
+	if(pri->fd == -1) return;
+	tuntap_open_addr(addr, pri->dev_name);
+}
+
+static void tuntap_del_addr(unsigned char *addr, void *data)
+{
+	struct tuntap_data *pri = data;
+
+	if(pri->fd == -1) return;
+	tuntap_close_addr(addr, pri->dev_name);
+}
+
+static int tuntap_open(void *data)
+{
+	struct tuntap_data *pri = data;
+	struct tuntap_open_data tap_data;
+	int err, fds[2];
+
+	err = tap_open_common(pri->dev, pri->hw_setup, pri->gate_addr);
+	if(err) return(err);
+
+	if(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) < 0){
+		printk("data socketpair failed - errno = %d\n", errno);
+		return(-errno);
+	}
+
+	tap_data.me = fds[0];
+	tap_data.remote = fds[1];
+	tap_data.data_fd = -1;
+	tap_data.gate = pri->gate_addr;
+	tap_data.buffer = get_output_buffer(&tap_data.len);
+	if(tap_data.buffer != NULL) tap_data.len--;
+	tap_data.used = 0;
+
+	tracing_cb(tuntap_open_tramp, &tap_data);
+	if(tap_data.buffer != NULL){
+		pri->dev_name = uml_strdup(tap_data.buffer);
+		tap_data.buffer[tap_data.used] = '\0';
+		printk(tap_data.buffer + IFNAMSIZ);
+		free_output_buffer(tap_data.buffer);
+	}
+	if(tap_data.err != 0){
+		printk("tuntap_open_tramp failed - errno = %d\n", 
+		       tap_data.err);
+		return(-tap_data.err);
+	}
+	close(fds[0]);
+
+	pri->fd = tap_data.data_fd;
+	iter_addresses(pri->dev, tuntap_open_addr, pri->dev_name);
+	return(tap_data.data_fd);	
+}
+
+static void tuntap_close(int fd, void *data)
+{
+	struct tuntap_data *pri = data;
+
+	iter_addresses(pri->dev, tuntap_close_addr, pri->dev_name);
+	close(fd);
+}
+
+int tuntap_user_read(int fd, void *buf, int len, struct tuntap_data *pri)
+{
+	int n;
+
+	while(((n = read(fd,  buf,  len)) < 0) && (errno == EINTR)) ;
+	if(errno == EAGAIN) return(0);
+	if(n < 0) return(-errno);
+	return(n);
+}
+
+int tuntap_user_write(int fd, void *buf, int len, struct tuntap_data *pri)
+{
+	int n;
+
+	while(((n = write(fd, buf, len)) < 0) && (errno == EINTR)) ;
+	if(errno == EAGAIN) n = 0;
+	if(n < 0) return(-errno);
+	return(n);
+}
+
+static int tuntap_set_mtu(int mtu, void *data)
+{
+	return(mtu);
+}
+
+struct net_user_info tuntap_user_info = {
+	init:		tuntap_user_init,
+	open:		tuntap_open,
+	close:	 	tuntap_close,
+	set_mtu:	tuntap_set_mtu,
+	add_address:	tuntap_add_addr,
+	delete_address: tuntap_del_addr,
+	max_packet:	MAX_PACKET
+};
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -Naur -X exclude-files ac_cur/arch/um/kernel/smp.c ac/arch/um/kernel/smp.c
--- ac_cur/arch/um/kernel/smp.c	Sat Sep 15 15:56:11 2001
+++ ac/arch/um/kernel/smp.c	Sat Sep 15 20:41:23 2001
@@ -3,10 +3,12 @@
  * Licensed under the GPL
  */
 
+#ifdef CONFIG_SMP
+
 #include "linux/config.h"
 #include "linux/sched.h"
 #include "linux/threads.h"
-#include "asm/smp.h"
+#include "linux/smp.h"
 #include "asm/processor.h"
 #include "asm/spinlock.h"
 #include "asm/softirq.h"
@@ -15,8 +17,6 @@
 #include "kern_util.h"
 #include "kern.h"
 
-#ifdef CONFIG_SMP
-
 /* Total count of live CPUs */
 int smp_num_cpus = 0;
 
@@ -108,8 +108,6 @@
 	return(0);
 }
 
-#endif
-
 int inited_cpus = 1;
 
 int pid_to_processor_id(int pid)
@@ -123,7 +121,6 @@
 	return(-1);
 }
 
-#ifdef CONFIG_SMP
 int hard_smp_processor_id(void)
 {
 	return(pid_to_processor_id(getpid()));
diff -Naur -X exclude-files ac_cur/include/asm-um/hardirq.h ac/include/asm-um/hardirq.h
--- ac_cur/include/asm-um/hardirq.h	Sat Sep 15 20:32:55 2001
+++ ac/include/asm-um/hardirq.h	Sat Sep 15 20:35:46 2001
@@ -2,7 +2,6 @@
 #define __UM_HARDIRQ_H
 
 #include "linux/spinlock.h"
-#include "asm/smp.h"
 #include "asm/arch/hardirq.h"
 
 #endif