diff -u --recursive --new-file v1.1.3/linux/Makefile linux/Makefile
--- v1.1.3/linux/Makefile	Wed Apr 13 18:37:54 1994
+++ linux/Makefile	Wed Apr 13 12:53:29 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 3
+SUBLEVEL = 4
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.3/linux/config.in linux/config.in
--- v1.1.3/linux/config.in	Wed Apr 13 18:37:54 1994
+++ linux/config.in	Wed Apr 13 18:57:47 1994
@@ -65,7 +65,7 @@
 #bool 'PPP (point-to-point) support' CONFIG_PPP n
 bool 'PLIP (parallel port) support' CONFIG_PLIP n
 bool 'NE2000/NE1000 support' CONFIG_NE2000 n
-bool 'WD80E3 support' CONFIG_WD80x3 y
+bool 'WD80*3 support' CONFIG_WD80x3 y
 bool 'SMC Ultra support' CONFIG_ULTRA n
 bool '3c501 support' CONFIG_EL1 n
 bool '3c503 support' CONFIG_EL2 n
diff -u --recursive --new-file v1.1.3/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.1.3/linux/drivers/net/3c501.c	Sun Mar  6 16:12:04 1994
+++ linux/drivers/net/3c501.c	Wed Apr 13 12:52:16 1994
@@ -37,10 +37,9 @@
 #include <asm/io.h>
 #include <errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #ifndef HAVE_AUTOIRQ
 /* From auto_irq.c, should be in a *.h file. */
@@ -49,11 +48,6 @@
 extern struct device *irq2dev_map[16];
 #endif
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
 
 /* Index to functions. */
 int el1_probe(struct device *dev);
@@ -64,9 +58,7 @@
 static void el_reset(struct device *dev);
 static int  el1_close(struct device *dev);
 static struct enet_statistics *el1_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 #define EL_NAME "EtherLink 3c501"
 
@@ -202,35 +194,9 @@
     dev->hard_start_xmit = &el_start_xmit;
     dev->stop = &el1_close;
     dev->get_stats = &el1_get_stats;
-#ifdef HAVE_MULTICAST
     dev->set_multicast_list = &set_multicast_list;
-#endif
-
-    /* Fill in the generic field of the device structure. */
-    for (i = 0; i < DEV_NUMBUFFS; i++)
-	dev->buffs[i] = NULL;
-
-    dev->hard_header	= eth_header;
-    dev->add_arp	= eth_add_arp;
-    dev->queue_xmit	= dev_queue_xmit;
-    dev->rebuild_header	= eth_rebuild_header;
-    dev->type_trans	= eth_type_trans;
-
-    dev->type		= ARPHRD_ETHER;
-    dev->hard_header_len = ETH_HLEN;
-    dev->mtu		= 1500; /* eth_mtu */
-    dev->addr_len	= ETH_ALEN;
-    for (i = 0; i < ETH_ALEN; i++) {
-	dev->broadcast[i]=0xff;
-    }
-
-    /* New-style flags. */
-    dev->flags		= IFF_BROADCAST;
-    dev->family		= AF_INET;
-    dev->pa_addr	= 0;
-    dev->pa_brdaddr	= 0;
-    dev->pa_mask	= 0;
-    dev->pa_alen	= sizeof(unsigned long);
+    /* Setup the generic properties */
+    ether_setup(dev);
 
     return 0;
 }
@@ -291,14 +257,6 @@
 	return 0;
     }
 
-    /* Fill in the ethernet header. */
-    if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-	skb->dev = dev;
-	arp_queue (skb);
-	return 0;
-    }
-    skb->arp=1;
-
     if (skb->len <= 0)
 	return 0;
 
@@ -431,7 +389,7 @@
 static void
 el_receive(struct device *dev)
 {
-    int sksize, pkt_len;
+    int pkt_len;
     struct sk_buff *skb;
 
     pkt_len = inw(RX_LOW);
@@ -447,31 +405,19 @@
     }
     outb(AX_SYS, AX_CMD);
 
-    sksize = sizeof(struct sk_buff) + pkt_len;
-    skb = alloc_skb(sksize, GFP_ATOMIC);
+    skb = alloc_skb(pkt_len, GFP_ATOMIC);
     outw(0x00, GP_LOW);
     if (skb == NULL) {
 	printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 	el_status.stats.rx_dropped++;
 	return;
     } else {
-	skb->mem_len = sksize;
-	skb->mem_addr = skb;
 	skb->len = pkt_len;
 	skb->dev = dev;
 
 	insb(DATAPORT, skb->data, pkt_len);
 
-#ifdef HAVE_NETIF_RX
-	    netif_rx(skb);
-#else
-	    skb->lock = 0;
-	    if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-		kfree_skbmem(skb, sksize);
-		lp->stats.rx_dropped++;
-		break;
-	    }
-#endif
+	netif_rx(skb);
 	el_status.stats.rx_packets++;
     }
     return;
@@ -526,7 +472,6 @@
     return &el_status.stats;
 }
 
-#ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
    num_addrs == -1	Promiscuous mode, receive all packets
    num_addrs == 0	Normal mode, clear multicast list
@@ -547,7 +492,6 @@
 	inb(RX_STATUS);
     }
 }
-#endif
 
 /*
  * Local variables:
diff -u --recursive --new-file v1.1.3/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v1.1.3/linux/drivers/net/3c503.c	Wed Apr 13 11:33:50 1994
+++ linux/drivers/net/3c503.c	Wed Apr 13 12:52:16 1994
@@ -26,7 +26,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
-#include "dev.h"
+#include <linux/netdevice.h>
 
 #include "8390.h"
 #include "3c503.h"
diff -u --recursive --new-file v1.1.3/linux/drivers/net/3c507.c linux/drivers/net/3c507.c
--- v1.1.3/linux/drivers/net/3c507.c	Wed Apr 13 11:33:50 1994
+++ linux/drivers/net/3c507.c	Wed Apr 13 12:52:16 1994
@@ -51,17 +51,10 @@
 #include <asm/dma.h>
 #include <linux/errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
-
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#else
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 #include <linux/malloc.h>
-#endif
 
 /* use 0 for production, 1 for verification, 2..7 for debug */
 #ifndef NET_DEBUG
@@ -409,32 +402,7 @@
 	dev->hard_start_xmit = el16_send_packet;
 	dev->get_stats	= el16_get_stats;
 
-	/* Fill in the fields of the device structure with ethernet-generic values.
-	   This should be in a common file instead of per-driver.  */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
-	dev->hard_header	= eth_header;
-	dev->add_arp	= eth_add_arp;
-	dev->queue_xmit = dev_queue_xmit;
-	dev->rebuild_header = eth_rebuild_header;
-	dev->type_trans = eth_type_trans;
-
-	dev->type		= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu		= 1500; /* eth_mtu */
-	dev->addr_len	= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
-
-	/* New-style flags. */
-	dev->flags		= IFF_BROADCAST;
-	dev->family		= AF_INET;
-	dev->pa_addr	= 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask	= 0;
-	dev->pa_alen	= sizeof(unsigned long);
+	ether_setup(dev);	/* Generic ethernet behaviour */
 
 	return 0;
 }
@@ -496,15 +464,6 @@
 		return 0;
 	}
 
-	/* For ethernet, fill in the header.  This should really be done by a
-	   higher level, rather than duplicated for each ethernet adaptor. */
-	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-	}
-	skb->arp=1;
-
 	/* Block a timer-based transmit from overlapping. */
 	if (set_bit(0, (void*)&dev->tbusy) != 0)
 		printk("%s: Transmitter access conflict.\n", dev->name);
@@ -855,35 +814,22 @@
 			if (frame_status & 0x0080) lp->stats.rx_length_errors++;
 		} else {
 			/* Malloc up new buffer. */
-			int sksize;
 			struct sk_buff *skb;
 
 			pkt_len &= 0x3fff;
-			sksize = sizeof(struct sk_buff) + pkt_len;
-			skb = alloc_skb(sksize, GFP_ATOMIC);
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->mem_len = sksize;
-			skb->mem_addr = skb;
 			skb->len = pkt_len;
 			skb->dev = dev;
 
 			/* 'skb->data' points to the start of sk_buff data area. */
 			memcpy(skb->data, data_frame + 5, pkt_len);
 		
-#ifdef HAVE_NETIF_RX
 			netif_rx(skb);
-#else
-			skb->lock = 0;
-			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_skbmem(skb, sksize);
-				lp->stats.rx_dropped++;
-				break;
-			}
-#endif
 			lp->stats.rx_packets++;
 		}
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.1.3/linux/drivers/net/3c509.c	Thu Mar 31 09:40:04 1994
+++ linux/drivers/net/3c509.c	Wed Apr 13 12:52:17 1994
@@ -28,14 +28,10 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#endif
 
 
 #ifdef EL3_DEBUG
@@ -95,20 +91,17 @@
 	/* First check for a board on the EISA bus. */
 	if (EISA_bus) {
 		for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
-			/* Check the standard EISA ID register for an encoded '3Com'. */
-			if (inw(ioaddr + 0xC80) != 0x6d50)
+			if (inw(ioaddr) != 0x6d50)
 				continue;
 
-			/* Change the register set to the configuration window 0. */
-			outw(0x0800, ioaddr + 0xC80 + EL3_CMD);
-
 			irq = inw(ioaddr + 8) >> 12;
 			if_port = inw(ioaddr + 6)>>14;
 			for (i = 0; i < 3; i++)
 				phys_addr[i] = htons(read_eeprom(ioaddr, i));
 
-			/* Restore the "Product ID" to the EEPROM read register. */
-			read_eeprom(ioaddr, 3);
+			/* Restore the "Manufacturer ID" to the EEPROM read register. */
+			/* The manual says to restore "Product ID" (reg. 3). !???! */
+			read_eeprom(ioaddr, 7);
 
 			/* Was the EISA code an add-on hack?  Nahhhhh... */
 			goto found;
@@ -219,31 +212,7 @@
 #endif
 
 	/* Fill in the generic fields of the device structure. */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
-	dev->hard_header	= eth_header;
-	dev->add_arp		= eth_add_arp;
-	dev->queue_xmit		= dev_queue_xmit;
-	dev->rebuild_header = eth_rebuild_header;
-	dev->type_trans		= eth_type_trans;
-
-	dev->type			= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu			= 1500; /* eth_mtu */
-	dev->addr_len		= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
-
-	/* New-style flags. */
-	dev->flags			= IFF_BROADCAST;
-	dev->family			= AF_INET;
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr		= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= sizeof(unsigned long);
-
+	ether_setup(dev);
 	return 0;
 }
 
@@ -368,14 +337,6 @@
 		return 0;
 	}
 
-	/* Fill in the ethernet header. */
-	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-	}
-	skb->arp=1;
-
 	if (skb->len <= 0)
 		return 0;
 
@@ -480,7 +441,7 @@
 		if (++i > 10) {
 			printk("%s: Infinite loop in interrupt, status %4.4x.\n",
 				   dev->name, status);
-			/* Clear all interrupts we have handled. */
+			/* Clear all interrupts we have handled */
 			outw(0x68FF, ioaddr + EL3_CMD);
 			break;
 		}
@@ -568,16 +529,13 @@
 		if ( (! (rx_status & 0x4000))
 			|| ! (rx_status & 0x1000)) { /* Dribble bits are OK. */
 			short pkt_len = rx_status & 0x7ff;
-			int sksize = sizeof(struct sk_buff) + pkt_len + 3;
 			struct sk_buff *skb;
 
-			skb = alloc_skb(sksize, GFP_ATOMIC);
+			skb = alloc_skb(pkt_len+3, GFP_ATOMIC);
 			if (el3_debug > 4)
-				printk("	   Receiving packet size %d status %4.4x.\n",
+				printk("Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
 			if (skb != NULL) {
-				skb->mem_len = sksize;
-				skb->mem_addr = skb;
 				skb->len = pkt_len;
 				skb->dev = dev;
 
@@ -606,12 +564,12 @@
 					continue;
 				} else {
 					printk("%s: receive buffers full.\n", dev->name);
-					kfree_s(skb, sksize);
+					kfree_s(skb, FREE_READ);
 				}
 #endif
 			} else if (el3_debug)
 				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
-					   dev->name, sksize);
+					   dev->name, pkt_len);
 		}
 		lp->stats.rx_dropped++;
 		outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
diff -u --recursive --new-file v1.1.3/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v1.1.3/linux/drivers/net/8390.c	Tue Mar 22 09:03:31 1994
+++ linux/drivers/net/8390.c	Wed Apr 13 13:13:53 1994
@@ -47,14 +47,9 @@
 #include <linux/in.h>
 #include <linux/interrupt.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #include "8390.h"
 
@@ -153,8 +148,8 @@
 		else {
 			/* The 8390 probably hasn't gotten on the cable yet. */
 			printk(KERN_DEBUG "%s: Possible network cable problem?\n", dev->name);
-			if (ei_local->stat.tx_packets == 0)
-				ei_local->interface_num ^= 1;   /* Try a different xcvr.  */
+			if(ei_local->stat.tx_packets==0)
+				ei_local->interface_num ^= 1; 	/* Try a different xcvr.  */
 		}
 		/* Try to restart the card.  Perhaps the user has fixed something. */
 		ei_reset_8390(dev);
@@ -169,13 +164,6 @@
 		dev_tint(dev);
 		return 0;
     }
-    /* Fill in the ethernet header. */
-    if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-    }
-    skb->arp=1;
     
     length = skb->len;
     if (skb->len <= 0)
@@ -449,19 +437,16 @@
 					   rx_frame.next);
 			ei_local->stat.rx_errors++;
 		} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
-			int sksize = sizeof(struct sk_buff) + pkt_len;
 			struct sk_buff *skb;
 			
-			skb = alloc_skb(sksize, GFP_ATOMIC);
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
 			if (skb == NULL) {
 				if (ei_debug > 1)
 					printk("%s: Couldn't allocate a sk_buff of size %d.\n",
-						   dev->name, sksize);
+						   dev->name, pkt_len);
 				ei_local->stat.rx_dropped++;
 				break;
 			} else {
-				skb->mem_len = sksize;
-				skb->mem_addr = skb;
 				skb->len = pkt_len;
 				skb->dev = dev;
 				
@@ -580,8 +565,6 @@
 /* Initialize the rest of the 8390 device structure. */
 int ethdev_init(struct device *dev)
 {
-    int i;
-    
     if (ei_debug > 1)
 		printk(version);
     
@@ -605,32 +588,9 @@
 #ifdef HAVE_MULTICAST
     dev->set_multicast_list = &set_multicast_list;
 #endif
-    
-    for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-    
-    dev->hard_header	= eth_header;
-    dev->add_arp		= eth_add_arp;
-    dev->queue_xmit		= dev_queue_xmit;
-    dev->rebuild_header	= eth_rebuild_header;
-    dev->type_trans		= eth_type_trans;
-    
-    dev->type		= ARPHRD_ETHER;
-    dev->hard_header_len = ETH_HLEN;
-    dev->mtu		= 1500; /* eth_mtu */
-    dev->addr_len	= ETH_ALEN;
-    for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-    }
-    
-    /* New-style flags. */
-    dev->flags		= IFF_BROADCAST;
-    dev->family		= AF_INET;
-    dev->pa_addr	= 0;
-    dev->pa_brdaddr	= 0;
-    dev->pa_mask	= 0;
-    dev->pa_alen	= sizeof(unsigned long);
-    
+
+    ether_setup(dev);
+        
     return 0;
 }
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.1.3/linux/drivers/net/Makefile	Tue Feb 22 08:38:04 1994
+++ linux/drivers/net/Makefile	Wed Apr 13 12:52:17 1994
@@ -7,7 +7,7 @@
 # are difficult for users to deal with.
 include CONFIG
 
-NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o)
+NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o) net.a(loopback.o)
 CFLAGS := $(CFLAGS) -I../../net/inet
 CPP := $(CPP) -I../../net/inet
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.1.3/linux/drivers/net/Space.c	Fri Feb 18 11:18:28 1994
+++ linux/drivers/net/Space.c	Wed Apr 13 12:52:17 1994
@@ -26,7 +26,7 @@
  */
 #include <linux/config.h>
 #include <linux/ddi.h>
-#include "dev.h"
+#include <linux/netdevice.h>
 
 #define LOOPBACK			/* always present, right?	*/
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/at1700.c linux/drivers/net/at1700.c
--- v1.1.3/linux/drivers/net/at1700.c	Sun Mar  6 16:13:56 1994
+++ linux/drivers/net/at1700.c	Wed Apr 13 12:52:17 1994
@@ -40,10 +40,9 @@
 #include <asm/dma.h>
 #include <errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #ifndef HAVE_AUTOIRQ
 /* From auto_irq.c, in ioport.h for later versions. */
@@ -54,11 +53,6 @@
 extern struct device *irq2dev_map[16];
 #endif
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
 /* use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
 #define NET_DEBUG 2
@@ -121,9 +115,7 @@
 static void net_rx(struct device *dev);
 static int net_close(struct device *dev);
 static struct enet_statistics *net_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 
 /* Check for a network adaptor of this type, and return '0' iff one exists.
@@ -258,37 +250,11 @@
 	dev->stop		= net_close;
 	dev->hard_start_xmit = net_send_packet;
 	dev->get_stats	= net_get_stats;
-#ifdef HAVE_MULTICAST
 	dev->set_multicast_list = &set_multicast_list;
-#endif
 
-	/* Fill in the fields of the device structure with ethernet-generic values.
-	   This should be in a common file instead of per-driver.  */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
-	dev->hard_header	= eth_header;
-	dev->add_arp		= eth_add_arp;
-	dev->queue_xmit		= dev_queue_xmit;
-	dev->rebuild_header	= eth_rebuild_header;
-	dev->type_trans		= eth_type_trans;
-
-	dev->type		= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu		= 1500; /* eth_mtu */
-	dev->addr_len	= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
-
-	/* New-style flags. */
-	dev->flags		= IFF_BROADCAST;
-	dev->family		= AF_INET;
-	dev->pa_addr	= 0;
-	dev->pa_brdaddr	= 0;
-	dev->pa_mask	= 0;
-	dev->pa_alen	= sizeof(unsigned long);
-
+	/* Fill in the fields of the device structure with ethernet-generic values. */
+	   
+	ether_setup(dev);
 	return 0;
 }
 
@@ -410,15 +376,6 @@
 		return 0;
 	}
 
-	/* For ethernet, fill in the header.  This should really be done by a
-	   higher level, rather than duplicated for each ethernet adaptor. */
-	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-	}
-	skb->arp=1;
-
 	/* Block a timer-based transmit from overlapping.  This could better be
 	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
 	if (set_bit(0, (void*)&dev->tbusy) != 0)
@@ -534,7 +491,6 @@
 		} else {
 			ushort pkt_len = inw(ioaddr + DATAPORT);
 			/* Malloc up new buffer. */
-			int sksize = sizeof(struct sk_buff) + pkt_len;
 			struct sk_buff *skb;
 
 			if (pkt_len > 1550) {
@@ -544,7 +500,7 @@
 				lp->stats.rx_errors++;
 				break;
 			}
-			skb = alloc_skb(sksize, GFP_ATOMIC);
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet (len %d).\n",
 					   dev->name, pkt_len);
@@ -552,8 +508,6 @@
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->mem_len = sksize;
-			skb->mem_addr = skb;
 			skb->len = pkt_len;
 			skb->dev = dev;
 
@@ -567,16 +521,7 @@
 				printk(".\n");
 			}
 
-#ifdef HAVE_NETIF_RX
 			netif_rx(skb);
-#else
-			skb->lock = 0;
-			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_s(skb, sksize);
-				lp->stats.rx_dropped++;
-				break;
-			}
-#endif
 			lp->stats.rx_packets++;
 		}
 		if (--boguscount <= 0)
@@ -637,7 +582,6 @@
 	return &lp->stats;
 }
 
-#ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
    num_addrs == -1	Promiscuous mode, receive all packets
    num_addrs == 0	Normal mode, clear multicast list
@@ -653,7 +597,6 @@
 	} else
 		outw(2, ioaddr + RX_MODE);	/* Disable promiscuous, use normal mode */
 }
-#endif
 
 /*
  * Local variables:
diff -u --recursive --new-file v1.1.3/linux/drivers/net/atp.c linux/drivers/net/atp.c
--- v1.1.3/linux/drivers/net/atp.c	Tue Mar  1 19:29:05 1994
+++ linux/drivers/net/atp.c	Wed Apr 13 12:52:17 1994
@@ -91,10 +91,9 @@
 #include <asm/dma.h>
 #include <errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #include "atp.h"
 
@@ -132,7 +131,6 @@
 extern int atp_probe(struct device *dev);
 
 static int atp_probe1(struct device *dev, short ioaddr);
-static void init_dev(struct device *dev);
 static void get_node_ID(struct device *dev);
 static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
 static int net_open(struct device *dev);
@@ -145,9 +143,7 @@
 static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
 static int net_close(struct device *dev);
 static struct enet_statistics *net_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 
 /* Check for a network adaptor of this type, and return '0' iff one exists.
@@ -232,7 +228,7 @@
 		printk(version);
 
 	/* Initialize the device structure. */
-	init_dev(dev);
+	ether_setup(dev);
 	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
 	memset(dev->priv, 0, sizeof(struct net_local));
 
@@ -251,45 +247,11 @@
 	dev->stop		= net_close;
 	dev->hard_start_xmit = net_send_packet;
 	dev->get_stats	= net_get_stats;
-#ifdef HAVE_MULTICAST
 	dev->set_multicast_list = &set_multicast_list;
-#endif
 
 	return 0;
 }
 
-/* Fill in the fields of the device structure with ethernet-generic values.
-   This should be in a common file instead of per-driver.  */
-static void init_dev(struct device *dev)
-{
-	int i;
-
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
-	dev->hard_header	= eth_header;
-	dev->add_arp		= eth_add_arp;
-	dev->queue_xmit		= dev_queue_xmit;
-	dev->rebuild_header = eth_rebuild_header;
-	dev->type_trans		= eth_type_trans;
-
-	dev->type			= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu			= 1500; /* eth_mtu */
-	dev->addr_len		= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
-
-	/* New-style flags. */
-	dev->flags			= IFF_BROADCAST;
-	dev->family			= AF_INET;
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr		= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= sizeof(unsigned long);
-}
-
 /* Read the station address PROM, usually a word-wide EEPROM. */
 static void get_node_ID(struct device *dev)
 {
@@ -475,15 +437,6 @@
 		return 0;
 	}
 
-	/* For ethernet, fill in the header.  This should really be done by a
-	   higher level, rather than duplicated for each ethernet adaptor. */
-	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-	}
-	skb->arp=1;
-
 	/* Block a timer-based transmit from overlapping.  This could better be
 	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
 	if (set_bit(0, (void*)&dev->tbusy) != 0)
@@ -674,17 +627,14 @@
 	} else {
 		/* Malloc up new buffer. */
 		int pkt_len = (rx_head.rx_count & 0x7ff) - 4; 		/* The "-4" is omits the FCS (CRC). */
-		int sksize = sizeof(struct sk_buff) + pkt_len;
 		struct sk_buff *skb;
 		
-		skb = alloc_skb(sksize, GFP_ATOMIC);
+		skb = alloc_skb(pkt_len, GFP_ATOMIC);
 		if (skb == NULL) {
 			printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 			lp->stats.rx_dropped++;
 			goto done;
 		}
-		skb->mem_len = sksize;
-		skb->mem_addr = skb;
 		skb->len = pkt_len;
 		skb->dev = dev;
 		
@@ -699,16 +649,7 @@
 				   data[12], data[13]);
 		}
 		
-#ifdef HAVE_NETIF_RX
 		netif_rx(skb);
-#else
-		skb->lock = 0;
-		if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-			kfree_s(skb, sksize);
-			lp->stats.rx_dropped++;
-			break;
-		}
-#endif
 		lp->stats.rx_packets++;
 	}
  done:
@@ -770,7 +711,6 @@
 	return &lp->stats;
 }
 
-#ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
    num_addrs == -1	Promiscuous mode, receive all packets
    num_addrs == 0	Normal mode, clear multicast list
@@ -785,7 +725,6 @@
 	lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
 	write_reg_high(ioaddr, CMR2, lp->addr_mode);
 }
-#endif
 
 /*
  * Local variables:
diff -u --recursive --new-file v1.1.3/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c
--- v1.1.3/linux/drivers/net/auto_irq.c	Wed Dec  1 14:44:15 1993
+++ linux/drivers/net/auto_irq.c	Wed Apr 13 12:52:17 1994
@@ -35,7 +35,7 @@
 #include <linux/sched.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include "dev.h"
+#include <linux/netdevice.h>
 /*#include <asm/system.h>*/
 
 struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */};
diff -u --recursive --new-file v1.1.3/linux/drivers/net/d_link.c linux/drivers/net/d_link.c
--- v1.1.3/linux/drivers/net/d_link.c	Wed Feb  2 17:30:03 1994
+++ linux/drivers/net/d_link.c	Wed Apr 13 12:52:17 1994
@@ -76,23 +76,13 @@
 #include <asm/system.h>
 #include <errno.h>
 
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #define netstats enet_statistics
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size,pri)	(struct sk_buff *)kmalloc(size,pri)
-#endif
-
 /**************************************************
  *                                                *
  * Definition of D-Link Ethernet Pocket adapter   *
@@ -334,8 +324,11 @@
 	 *
 	 * This fix is better than changing in tcp.h IMHO
 	 */
+#if 0	 
 	tcp_prot.rspace = d_link_rspace; /* was: sock_rspace */
+#endif
 
+
 	return 0;
 }
 
@@ -355,8 +348,9 @@
 	free_irq(D_LINK_IRQ);
 	irq2dev_map[D_LINK_IRQ] = NULL;
 	dev->start = 0;
+#if 0
 	tcp_prot.rspace = sock_rspace; /* see comment above! */
-
+#endif
 	return 0;
 }
 
@@ -399,15 +393,6 @@
 		return 0;
 	}
 
-	/* For ethernet, fill in the header (hardware addresses) with an arp. */
-	if (!skb->arp)
-		if(dev->rebuild_header(skb->data, dev)) {
-			skb->dev = dev;
-			arp_queue (skb);
-			return 0;
-		}
-	skb->arp = 1;
-
 	if (free_tx_pages <= 0) {	/* Do timeouts, to avoid hangs. */
 		tickssofar = jiffies - dev->trans_start;
 
@@ -559,7 +544,6 @@
 	int		i;
 	int		read_from;
 	int		size;
-	int		sksize;
 	register unsigned char	*buffer;
 
 	cli();
@@ -577,19 +561,16 @@
 	if ((size < 32)  ||  (size > 1535))
 		printk("%s: Bogus packet size %d.\n", dev->name, size);
 
-	sksize = sizeof(struct sk_buff) + size;
-	skb = alloc_skb(sksize, GFP_ATOMIC);
+	skb = alloc_skb(size, GFP_ATOMIC);
 	sti();
 	if (skb == NULL) {
 		printk("%s: Couldn't allocate a sk_buff of size %d.\n",
-			dev->name, sksize);
+			dev->name, size);
 		return;
 	}
 	/* else */
 
 	skb->lock = 0;
-	skb->mem_len = sksize;
-	skb->mem_addr = skb;
 	/* 'skb->data' points to the start of sk_buff data area. */
 	buffer = skb->data;
 
@@ -663,35 +644,14 @@
 	/* Initialize the device structure. */
 	dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);
 	memset(dev->priv, 0, sizeof(struct netstats));
-
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
 	dev->get_stats = get_stats;
-	dev->hard_header = eth_header;
-	dev->add_arp = eth_add_arp;
-	dev->queue_xmit = dev_queue_xmit;
-	dev->rebuild_header = eth_rebuild_header;
-	dev->type_trans = eth_type_trans;
 
 	dev->open = d_link_open;
 	dev->stop = d_link_close;
 	dev->hard_start_xmit = &d_link_start_xmit;
 
-	/* These are ethernet specific. */
-	dev->type = ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu = 1500; /* eth_mtu */
-	dev->addr_len	= ETH_ALEN;
-
-	/* New-style flags. */
-	dev->flags = IFF_BROADCAST;
-	dev->family = AF_INET;
-	dev->pa_addr = 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask = 0;
-	dev->pa_alen = sizeof(unsigned long);
-
+	ether_setup(dev);
+	
 	select_prn();
 	return 0;
 }
diff -u --recursive --new-file v1.1.3/linux/drivers/net/depca.c linux/drivers/net/depca.c
--- v1.1.3/linux/drivers/net/depca.c	Wed Apr 13 11:33:51 1994
+++ linux/drivers/net/depca.c	Wed Apr 13 12:52:18 1994
@@ -147,11 +147,10 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 
-#include "dev.h"
+#include <linux/netdevice.h>
 #include "iow.h"                    /* left in for pl13/14 compatibility... */
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 #include "depca.h"
 
 #ifdef DEPCA_DEBUG
@@ -216,11 +215,6 @@
 #endif /* CRC_POLYNOMIAL */
 #endif /* HAVE_MULTICAST */
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
-#endif  /* HAVE_ALLOC_SKB */
-
 /*
 ** The DEPCA Rx and Tx ring descriptors. 
 */
@@ -1066,17 +1060,14 @@
 	    if (status & R_BUFF) lp->stats.rx_fifo_errors++;
 	} else {	  /* Malloc up new buffer, compatible  with net-2e. */
 	    short pkt_len = lp->rx_ring[entry].msg_length;
-	    int sksize = sizeof(struct sk_buff) + pkt_len;
 	    struct sk_buff *skb;
 
-	    skb = alloc_skb(sksize, GFP_ATOMIC);
+	    skb = alloc_skb(pkt_len, GFP_ATOMIC);
 	    if (skb == NULL) {
 		printk("%s: Memory squeeze, deferring packet.\n", dev->name);
 		lp->stats.rx_dropped++;	/* Really, deferred. */
 		break;
 	    }
-	    skb->mem_len = sksize;
-	    skb->mem_addr = skb;
 	    skb->len = pkt_len;
 	    skb->dev = dev;
 	    memcpy(skb->data,
@@ -1086,16 +1077,7 @@
 	    ** Notify the upper protocol layers that there is another 
 	    ** packet to handle
 	    */
-#ifdef HAVE_NETIF_RX
 	    netif_rx(skb);
-#else
-	    skb->lock = 0;
-	    if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-		kfree_skbmem(skb, sksize);
-		lp->stats.rx_dropped++;
-		break;
-	    }
-#endif
 	    lp->stats.rx_packets++;
 	}
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.1.3/linux/drivers/net/eexpress.c	Wed Apr 13 11:33:51 1994
+++ linux/drivers/net/eexpress.c	Wed Apr 13 12:52:18 1994
@@ -1,3 +1,4 @@
+
 /* eexpress.c: Intel EtherExpress device driver for Linux. */
 /*
 	Written 1993 by Donald Becker.
@@ -41,25 +42,19 @@
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
 #include <linux/ioport.h>
-#include <linux/in.h>
 #include <linux/string.h>
+#include <linux/in.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <linux/errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#else
-/* This isn't quite right, but it's the best version define I can find right now. */
 #include <linux/malloc.h>
-#endif
 
 /* use 0 for production, 1 for verification, 2..7 for debug */
 #ifndef NET_DEBUG
@@ -292,9 +287,7 @@
 static void eexp_rx(struct device *dev);
 static int	eexp_close(struct device *dev);
 static struct enet_statistics *eexp_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 static int read_eeprom(int ioaddr, int location);
 static void hardware_send_packet(struct device *dev, void *buf, short length);
@@ -405,37 +398,12 @@
 	dev->stop		= eexp_close;
 	dev->hard_start_xmit = eexp_send_packet;
 	dev->get_stats	= eexp_get_stats;
-#ifdef HAVE_MULTICAST
 	dev->set_multicast_list = &set_multicast_list;
-#endif
-
-	/* Fill in the fields of the device structure with ethernet-generic values.
-	   This should be in a common file instead of per-driver.  */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
-	dev->hard_header	= eth_header;
-	dev->add_arp	= eth_add_arp;
-	dev->queue_xmit = dev_queue_xmit;
-	dev->rebuild_header = eth_rebuild_header;
-	dev->type_trans = eth_type_trans;
-
-	dev->type		= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu		= 1500; /* eth_mtu */
-	dev->addr_len	= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
-
-	/* New-style flags. */
-	dev->flags		= IFF_BROADCAST;
-	dev->family		= AF_INET;
-	dev->pa_addr	= 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask	= 0;
-	dev->pa_alen	= sizeof(unsigned long);
 
+	/* Fill in the fields of the device structure with ethernet-generic values. */
+	
+	ether_setup(dev);
+	
 	return 0;
 }
 
@@ -511,15 +479,6 @@
 		return 0;
 	}
 
-	/* For ethernet, fill in the header.  This should really be done by a
-	   higher level, rather than duplicated for each ethernet adaptor. */
-	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-	}
-	skb->arp=1;
-
 	/* Block a timer-based transmit from overlapping. */
 	if (set_bit(0, (void*)&dev->tbusy) != 0)
 		printk("%s: Transmitter access conflict.\n", dev->name);
@@ -704,7 +663,6 @@
 	return &lp->stats;
 }
 
-#ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
    num_addrs == -1	Promiscuous mode, receive all packets
    num_addrs == 0	Normal mode, clear multicast list
@@ -731,7 +689,6 @@
 		outw(99, ioaddr);		/* Disable promiscuous mode, use normal mode */
 	}
 }
-#endif
 
 /* The horrible routine to read a word from the serial EEPROM. */
 
@@ -964,19 +921,15 @@
 			if (frame_status & 0x0080) lp->stats.rx_length_errors++;
 		} else {
 			/* Malloc up new buffer. */
-			int sksize;
 			struct sk_buff *skb;
 
 			pkt_len &= 0x3fff;
-			sksize = sizeof(struct sk_buff) + pkt_len;
-			skb = alloc_skb(sksize, GFP_ATOMIC);
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->mem_len = sksize;
-			skb->mem_addr = skb;
 			skb->len = pkt_len;
 			skb->dev = dev;
 
@@ -984,16 +937,7 @@
 
 			insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
 		
-#ifdef HAVE_NETIF_RX
 			netif_rx(skb);
-#else
-			skb->lock = 0;
-			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_s(skb, sksize);
-				lp->stats.rx_dropped++;
-				break;
-			}
-#endif
 			lp->stats.rx_packets++;
 		}
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/hp.c linux/drivers/net/hp.c
--- v1.1.3/linux/drivers/net/hp.c	Sun Mar  6 16:12:05 1994
+++ linux/drivers/net/hp.c	Wed Apr 13 12:52:18 1994
@@ -23,7 +23,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 
-#include "dev.h"
+#include <linux/netdevice.h>
 #include "8390.h"
 
 #ifndef HAVE_PORTRESERVE
diff -u --recursive --new-file v1.1.3/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v1.1.3/linux/drivers/net/lance.c	Fri Mar  4 09:16:19 1994
+++ linux/drivers/net/lance.c	Wed Apr 13 12:52:18 1994
@@ -29,21 +29,15 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #ifndef HAVE_PORTRESERVE
 #define check_region(addr, size)	0
 #define snarf_region(addr, size)	do ; while(0)
 #endif
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
-#endif
-
 struct device *init_etherdev(struct device *dev, int sizeof_private,
 			     unsigned long *mem_startp);
 
@@ -359,9 +353,7 @@
     dev->hard_start_xmit = &lance_start_xmit;
     dev->stop = &lance_close;
     dev->get_stats = &lance_get_stats;
-#ifdef HAVE_MULTICAST
     dev->set_multicast_list = &set_multicast_list;
-#endif
 
     return mem_start;
 }
@@ -511,14 +503,6 @@
 	return 0;
     }
 
-    /* Fill in the ethernet header. */
-    if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-	skb->dev = dev;
-	arp_queue (skb);
-	return 0;
-    }
-    skb->arp=1;
-
     if (skb->len <= 0)
 	return 0;
 
@@ -720,32 +704,20 @@
 	} else {
 	    /* Malloc up new buffer, compatible with net-2e. */
 	    short pkt_len = lp->rx_ring[entry].msg_length;
-	    int sksize = sizeof(struct sk_buff) + pkt_len;
 	    struct sk_buff *skb;
 
-	    skb = alloc_skb(sksize, GFP_ATOMIC);
+	    skb = alloc_skb(pkt_len, GFP_ATOMIC);
 	    if (skb == NULL) {
 		printk("%s: Memory squeeze, deferring packet.\n", dev->name);
 		lp->stats.rx_dropped++;	/* Really, deferred. */
 		break;
 	    }
-	    skb->mem_len = sksize;
-	    skb->mem_addr = skb;
 	    skb->len = pkt_len;
 	    skb->dev = dev;
 	    memcpy(skb->data,
 		   (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
 		   pkt_len);
-#ifdef HAVE_NETIF_RX
 	    netif_rx(skb);
-#else
-	    skb->lock = 0;
-	    if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-		kfree_skbmem(skb, sksize);
-		lp->stats.rx_dropped++;
-		break;
-	    }
-#endif
 	    lp->stats.rx_packets++;
 	}
 
@@ -808,7 +780,6 @@
     return &lp->stats;
 }
 
-#ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
    num_addrs == -1	Promiscuous mode, receive all packets
    num_addrs == 0	Normal mode, clear multicast list
@@ -842,7 +813,6 @@
     outw(0, ioaddr+LANCE_ADDR);
     outw(0x0142, ioaddr+LANCE_DATA); /* Resume normal operation. */
 }
-#endif
 
 #ifdef HAVE_DEVLIST
 static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
diff -u --recursive --new-file v1.1.3/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v1.1.3/linux/drivers/net/loopback.c
+++ linux/drivers/net/loopback.c	Wed Apr 13 17:12:39 1994
@@ -0,0 +1,141 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Pseudo-driver for the loopback interface.
+ *
+ * Version:	@(#)loopback.c	1.0.4b	08/16/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@super.org>
+ *
+ *		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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>	/* For the statistics structure. */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+
+static int
+loopback_xmit(struct sk_buff *skb, struct device *dev)
+{
+  struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+  int done;
+
+  DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb));
+  if (skb == NULL || dev == NULL) return(0);
+
+  cli();
+  if (dev->tbusy != 0) {
+	sti();
+	printk("loopback error: called by %08lx\n",
+		((unsigned long *)&skb)[-1]);
+	stats->tx_errors++;
+	return(1);
+  }
+  dev->tbusy = 1;
+  sti();
+
+  start_bh_atomic();
+  done = dev_rint(skb->data, skb->len, 0, dev);
+  if (skb->free) kfree_skb(skb, FREE_WRITE);
+  end_bh_atomic();
+
+  while (done != 1) {
+  	start_bh_atomic();
+	done = dev_rint(NULL, 0, 0, dev);
+	end_bh_atomic();
+  }
+  stats->tx_packets++;
+
+  dev->tbusy = 0;
+
+#if 1
+	__asm__("cmpl $0,_intr_count\n\t"
+		"jne 1f\n\t"
+		"movl _bh_active,%%eax\n\t"
+		"testl _bh_mask,%%eax\n\t"
+		"je 1f\n\t"
+		"incl _intr_count\n\t"
+		"call _do_bottom_half\n\t"
+		"decl _intr_count\n"
+		"1:"
+		:
+		:
+		: "ax", "dx", "cx");
+#endif
+
+  return(0);
+}
+
+static struct enet_statistics *
+get_stats(struct device *dev)
+{
+    return (struct enet_statistics *)dev->priv;
+}
+
+/* Initialize the rest of the LOOPBACK device. */
+int
+loopback_init(struct device *dev)
+{
+  int i;
+
+  dev->mtu		= 2000;			/* MTU			*/
+  dev->tbusy		= 0;
+  dev->hard_start_xmit	= loopback_xmit;
+  dev->open		= NULL;
+#if 1
+  dev->hard_header	= eth_header;
+  dev->hard_header_len	= ETH_HLEN;		/* 14			*/
+  dev->addr_len		= ETH_ALEN;		/* 6			*/
+  dev->type		= ARPHRD_ETHER;		/* 0x0001		*/
+  dev->type_trans	= eth_type_trans;
+  dev->rebuild_header	= eth_rebuild_header;
+#else
+  dev->hard_header_length = 0;
+  dev->addr_len		= 0;
+  dev->type		= 0;			/* loopback_type (0)	*/
+  dev->hard_header	= NULL;
+  dev->type_trans	= NULL;
+  dev->rebuild_header	= NULL;
+#endif
+
+  /* New-style flags. */
+  dev->flags		= IFF_LOOPBACK;
+  dev->family		= AF_INET;
+  dev->pa_addr		= in_aton("127.0.0.1");
+  dev->pa_brdaddr	= in_aton("127.255.255.255");
+  dev->pa_mask		= in_aton("255.0.0.0");
+  dev->pa_alen		= sizeof(unsigned long);
+  dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+  memset(dev->priv, 0, sizeof(struct enet_statistics));
+  dev->get_stats = get_stats;
+
+  /* Fill in the generic fields of the device structure. */
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+	skb_queue_head_init(&dev->buffs[i]);
+  
+  return(0);
+};
diff -u --recursive --new-file v1.1.3/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v1.1.3/linux/drivers/net/ne.c	Sun Mar  6 16:12:04 1994
+++ linux/drivers/net/ne.c	Wed Apr 13 12:52:18 1994
@@ -26,7 +26,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 
-#include "dev.h"
+#include <linux/netdevice.h>
 #include "8390.h"
 
 #define NE_BASE	 (dev->base_addr)
diff -u --recursive --new-file v1.1.3/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v1.1.3/linux/drivers/net/net_init.c	Wed Apr 13 11:33:51 1994
+++ linux/drivers/net/net_init.c	Wed Apr 13 12:52:18 1994
@@ -24,8 +24,8 @@
 #include <linux/malloc.h>
 #include <linux/if_ether.h>
 #include <linux/string.h>
-#include "dev.h"
-#include "eth.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 
 /* The network devices currently exist only in the socket namespace, so these
    entries are unused.  The only ones that make sense are
@@ -38,26 +38,14 @@
 
    Given that almost all of these functions are handled in the current
    socket-based scheme, putting ethercard devices in /dev/ seems pointless.
+   
+   [Removed all support for /dev network devices. When someone adds streams then
+    by magic we get them, but otherwise they are un-needed and a space waste]
 */
 
 /* The next device number/name to assign: "eth0", "eth1", etc. */
 static int next_ethdev_number = 0;
 
-#ifdef NET_MAJOR_NUM
-static struct file_operations netcard_fops = {
-	NULL,		/* lseek */
-	NULL,		/* read */
-	NULL,		/* write */
-	NULL,		/* readdir */
-	NULL,		/* select */
-	NULL,		/* ioctl */
-	NULL,		/* mmap */
-	NULL,		/* open */
-	NULL,		/* release */
-	NULL		/* fsync */
-};
-#endif
-
 unsigned long lance_init(unsigned long mem_start, unsigned long mem_end);
 
 /*
@@ -69,12 +57,6 @@
 unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
 {
 
-#ifdef NET_MAJOR_NUM
-	if (register_chrdev(NET_MAJOR_NUM, "network",&netcard_fops))
-		printk("WARNING: Unable to get major %d for the network devices.\n",
-			   NET_MAJOR_NUM);
-#endif
-
 #if defined(CONFIG_LANCE)			/* Note this is _not_ CONFIG_AT1500. */
 	mem_start = lance_init(mem_start, mem_end);
 #endif
@@ -117,11 +99,9 @@
 		sprintf(dev->name, "eth%d", next_ethdev_number++);
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
+		skb_queue_head_init(&dev->buffs[i]);
 	
 	dev->hard_header	= eth_header;
-	dev->add_arp		= eth_add_arp;
-	dev->queue_xmit		= dev_queue_xmit;
 	dev->rebuild_header	= eth_rebuild_header;
 	dev->type_trans		= eth_type_trans;
 	
@@ -151,6 +131,36 @@
 	}
 	return dev;
 }
+
+void ether_setup(struct device *dev)
+{
+	int i;
+	/* Fill in the fields of the device structure with ethernet-generic values.
+	   This should be in a common file instead of per-driver.  */
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	dev->hard_header	= eth_header;
+	dev->rebuild_header = eth_rebuild_header;
+	dev->type_trans = eth_type_trans;
+
+	dev->type		= ARPHRD_ETHER;
+	dev->hard_header_len = ETH_HLEN;
+	dev->mtu		= 1500; /* eth_mtu */
+	dev->addr_len	= ETH_ALEN;
+	for (i = 0; i < ETH_ALEN; i++) {
+		dev->broadcast[i]=0xff;
+	}
+
+	/* New-style flags. */
+	dev->flags		= IFF_BROADCAST;
+	dev->family		= AF_INET;
+	dev->pa_addr	= 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask	= 0;
+	dev->pa_alen	= sizeof(unsigned long);
+}
+
 
 
 /*
diff -u --recursive --new-file v1.1.3/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.1.3/linux/drivers/net/plip.c	Wed Feb  2 17:30:05 1994
+++ linux/drivers/net/plip.c	Wed Apr 13 12:52:19 1994
@@ -1,45 +1,49 @@
-/* Plip.c: A parallel port "network" driver for linux. */
 /*
-    Written 1993 by Donald Becker and TANABE Hiroyasu.
-    This code is distributed under the GPL.
-    
-    The current author is reached as hiro@sanpo.t.u-tokyo.ac.jp .
-    For more information do 'whois -h whois.nic.ad.jp HT043JP'
-
-    The original author may be reached as becker@super.org or
-    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
-
-    This is parallel port packet pusher.  It's actually more general
-    than the "IP" in its name suggests -- but 'plip' is just such a
-    great name!
-
-    This driver was first developed by D. Becker, when he was inspired by
-    Russ Nelson's parallel port packet driver.  He also did the update
-    to 0.99.10.
-
-    It was further developed by Tommy Thorn (tthorn@daimi.aau.dk).
-
-    Recent versions were debugged and maintained by TANABE Hiroyasu.
+ *	 Plip.c: A parallel port "network" driver for linux. 
+ */
 
-    Updated for 0.99pl12 by Donald Becker.
-    
-    Changes even more Alan Cox <iiitac@pyr.swan.ac.uk>
-    Fixed: sets skb->arp=1, always claims success like ethernet, doesn't
-    free skb and then claim fail. Incorrect brackets causing reset problem
-    Attempting to make it work (works for me - email me if it does work)
-    
-    Bugs:
-    	Should be timer oriented state machine. 
-    	Should never use jiffies for timeouts.
-    	Protocol is buggy when broadcasts occur (Must ask Russ Nelson)
-    	Can hang forever on collisions (tough - you fix it!).
-    	I get 15K/second NFS throughput (about 20-25K second IP).
-    	Change the protocol back.
-    	
-*/
+/*
+ *	Developement History:
+ *
+ *	Original version and the name 'PLIP' from Donald Becker  <becker@super.org>
+ *		inspired by Russ Nelson's parallel port packet driver.
+ *	Further development by Tommy Thorn <thorn@daimi.aau.dk>
+ *	Some changes by Tanabe Hiroyasu <hiro@sanpo.t.u-tokyo.ac.jp>
+ *	Upgraded for PL12 by Donald Becker
+ *	Minor hacks by Alan Cox <gw4pts@gw4pts.ampr.org> to get it working
+ *		more reliably (Ha!)
+ *     Changes even more Peter Bauer (100136.3530@compuserve.com)
+ *     Protocol changed back to original plip as in crynwr's packet-drivers.
+ *     Tested this against ncsa-telnet 2.3 and pcip_pkt using plip.com (which
+ *     contains "version	equ	0" and ";History:562,1" in the firts 2
+ *    source-lines						28-Mar-94
+ *	
+ *	
+ *
+ *  This is parallel port packet pusher.  It's actually more general
+ *  than the "IP" in its name suggests -- but 'plip' is just such a
+ *  great name!
+ * 
+ *    
+ *   Bugs: Please read this: The PLIP driver is a nasty hack and like all nasty hacks 
+ *	   has some 'features'.
+ *
+ *	Can lock machines solid if one end goes down or crashes, or due to cable faults.
+ *	Can lock both machines solid on a broadcast collision.
+ *	Some laptops don't have all the wires we use.
+ *	Doesn't match the original Russ Nelson protocol so won't talk to Amiga or PC drivers.
+ *	Waits far too long with interrupts off [X is unbearable, forget action games, xntp is a joke]
+ *	Doesn't work on some fast 486DX machines
+ *
+ *	If it works be thankful, if not fix it!
+ *
+ *  Info:
+ *     	I <Alan> got 15K/second NFS throughput (about 20-25K second IP). I also got some ethernet cards
+ *	so don't ask me for help. This code needs a real major rewrite. Any volunteers ?
+ */
 
 static char *version =
-    "Net2Debugged PLIP 1.01 (from plip.c:v0.15 for 0.99pl12+, 8/11/93)\n";
+    "NET3 PLIP.010 (from plip.c:v0.15 for 0.99pl12+, 8/11/93)\n";
 
 #include <linux/config.h>
 
@@ -87,14 +91,9 @@
 #include <netinet/in.h>
 #include <errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #ifdef PRINTK
 #undef PRINTK
@@ -138,8 +137,8 @@
 static int plip_close(struct device *dev);
 static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
 static int plip_header (unsigned char *buff, struct device *dev,
-		 unsigned short type, unsigned long h_dest,
-		 unsigned long h_source, unsigned len);
+		 unsigned short type, void *dest,
+		 void *source, unsigned len, struct sk_buff *skb);
 
 /* variables used internally. */
 #define INITIALTIMEOUTFACTOR 4
@@ -151,10 +150,6 @@
 static void plip_receiver_error(struct device *dev);
 static void plip_set_physicaladdr(struct device *dev, unsigned long ipaddr);
 static int plip_addrcmp(struct ethhdr *eth);
-static int plip_send_enethdr(struct device *dev, struct ethhdr *eth);
-static int plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth,
-				unsigned char h_dest, unsigned char h_source,
-				unsigned short type);
 static void cold_sleep(int tics);
 static void plip_interrupt(int reg_ptr); /* Dispatch from interrupts. */
 static int plip_receive_packet(struct device *dev);
@@ -189,10 +184,9 @@
     memset(dev->priv, 0, sizeof(struct netstats));
 
     for (i = 0; i < DEV_NUMBUFFS; i++)
-	dev->buffs[i] = NULL;
+	skb_queue_head_init(&dev->buffs[i]);
+
     dev->hard_header = &plip_header;
-    dev->add_arp = eth_add_arp;
-    dev->queue_xmit = dev_queue_xmit;
     dev->rebuild_header = eth_rebuild_header;
     dev->type_trans = eth_type_trans;
 
@@ -283,15 +277,6 @@
 	return 0;
     }
 
-    /* Pretend we are an ethernet and fill in the header.  This could use
-       a simplified routine someday. */
-    if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-	skb->dev = dev;
-	arp_queue (skb);
-	return 0;
-    }
-    skb->arp=1;
-
     dev->trans_start = jiffies;
     ret_val = plip_send_packet(dev, skb->data, skb->len);
     if (skb->free)
@@ -303,14 +288,14 @@
 
 static int
 plip_header (unsigned char *buff, struct device *dev,
-	     unsigned short type, unsigned long h_dest,
-	     unsigned long h_source, unsigned len)
+	     unsigned short type, void *daddr	     	     ,
+	     void *saddr, unsigned len, struct sk_buff *skb)
 {
     if (dev->dev_addr[0] == 0) {
 	/* set physical address */
-	plip_set_physicaladdr(dev, h_source);
+	plip_set_physicaladdr(dev, dev->pa_addr);
     }
-    return eth_header(buff, dev, type, h_dest, h_source, len);
+    return eth_header(buff, dev, type, daddr, saddr, len, skb);
 }
 
 static void
@@ -351,7 +336,7 @@
     } while ( (val & 0x80) );
     val = inb(dev->base_addr + PAR_STATUS);
     low_nibble = (val >> 3) & 0x0f;
-    outb(0x11, dev->base_addr + PAR_DATA);
+    outb(0x10, dev->base_addr + PAR_DATA);
     timeout = jiffies + timeoutfactor * 2;
     do {
 	oldval = val;
@@ -365,7 +350,7 @@
     val = inb(dev->base_addr + PAR_STATUS);
     PRINTK2(("%02x %s ", low_nibble | ((val << 1) & 0xf0),
 	       error ? "t":""));
-    outb(0x01, dev->base_addr + PAR_DATA);
+    outb(0x00, dev->base_addr + PAR_DATA);
     if (error) {
 	/* timeout error */
 	double_timeoutfactor();
@@ -418,7 +403,6 @@
 static int
 plip_receive_packet(struct device *dev)
 {
-    int plip_type;
     unsigned length;
     int checksum = 0;
     struct sk_buff *skb;
@@ -431,34 +415,16 @@
 
     {
 	/* get header octet and length of packet */
-	plip_type = get_byte(dev);
-	if (plip_type < 0) return 1; /* probably wrong interrupt */
-	length = get_byte(dev) << 8;
-	length |= get_byte(dev);
-	switch ( plip_type ) {
-	  case PLIP_HEADER_TYPE1:
-	    {
-		int i;
-		unsigned char *eth_p = (unsigned char*)&eth;
-		for ( i = 0; i < sizeof(eth); i++, eth_p++) {
-		    *eth_p = get_byte(dev);
-		}
-	    }
-	    break;
-	  case PLIP_HEADER_TYPE2:
-	    {
-		unsigned char h_dest, h_source;
-		unsigned short type;
-		h_dest = get_byte(dev);
-		h_source = get_byte(dev);
-		type = get_byte(dev) << 8;
-		type |= get_byte(dev);
-		plip_rebuild_enethdr(dev, &eth, h_dest, h_source, type);
-	    }
-	    break;
-	  default:
-	    PRINTK(("%s: wrong header octet\n", dev->name));
-	}
+        
+	length = get_byte(dev);
+	length |= get_byte(dev) << 8;
+        {
+	 int i;
+	 unsigned char *eth_p = (unsigned char*)&eth;
+	 for ( i = 0; i < sizeof(eth); i++, eth_p++) {
+	     *eth_p = get_byte(dev);
+	 }
+        }
 	PRINTK2(("length = %d\n", length));
 	if (length > dev->mtu || length < 8) {
 	    PRINTK2(("%s: bogus packet size %d.\n", dev->name, length));
@@ -469,17 +435,13 @@
 	/* get skb area from kernel and 
 	 * set appropriate values to skb
 	 */
-	int sksize;
-	sksize = sizeof(struct sk_buff) + length;
-	skb = alloc_skb(sksize, GFP_ATOMIC);
+	skb = alloc_skb(length, GFP_ATOMIC);
 	if (skb == NULL) {
 	    PRINTK(("%s: Couldn't allocate a sk_buff of size %d.\n",
-		    dev->name, sksize));
+		    dev->name,length));
 	    return 1;
 	}
 	skb->lock = 0;
-	skb->mem_len = sksize;
-	skb->mem_addr = skb;
     }
     {
 	/* phase of receiving the data */
@@ -531,13 +493,10 @@
 {
     int timeout;
     int error = 0;
-    if (!(inb(dev->base_addr+PAR_STATUS) & 0x08)) {
-	PRINTK(("remote end become unready while sending\n"));
-	return -1;
-    }
     PRINTK2((" S%02x", val));
-    outb(val, dev->base_addr); /* this makes data bits more stable */
-    outb(0x10 | val, dev->base_addr);
+    outb((val & 0xf), dev->base_addr); /* this makes data bits more stable */
+                                       /* (especially the &0xf :-> PB )    */
+    outb(0x10 | (val & 0xf), dev->base_addr);
     timeout = jiffies + timeoutfactor;
     while( inb(dev->base_addr+PAR_STATUS) & 0x80 )
 	if ( timeout < jiffies ) {
@@ -626,7 +585,6 @@
 plip_send_packet(struct device *dev, unsigned char *buf, int length)
 {
     int error = 0;
-    int plip_type;
     struct netstats *localstats;
 
     PRINTK2(("%s: plip_send_packet(%d) %02x %02x %02x %02x %02x...",
@@ -663,44 +621,22 @@
     if (plip_send_start(dev, (struct ethhdr *)buf) < 0)
 	return 1;
 
-    /* select plip type */
-    {
-	/* Use stripped ethernet header if each first 5 octet of eth
-	 * address is same.
-	 */
-	int i;
-	struct ethhdr *eth = (struct ethhdr *)buf;
-
-	plip_type = PLIP_HEADER_TYPE2;	
-	for ( i = 0; i < ETH_ALEN - 1; i++)
-	    if (eth->h_dest[i] != eth->h_source[i])
-		plip_type = PLIP_HEADER_TYPE1;
-    }
-
-    send_byte(dev, plip_type); /* send header octet */
-
     {
-	/* send packet's length */
-	/*
-	 * in original plip (before v0.1), it was sent with little endian.
-	 * but in internet, network byteorder is big endian,
-	 * so changed to use big endian.
-	 * maybe using 'ntos()' is better.
+	/* send packet's length 
+	   the byte order has changed now and then. Today it's sent as in 
+           the original crynwr-plip ...
+           Gruss PB
 	 */
-	send_byte(dev, length >> 8); send_byte(dev, length);
+	send_byte(dev, length); 
+        send_byte(dev, length >> 8);
     }
     {
 	/* phase of sending data */
 	int i;
 	int checksum = 0;
 
-	if (plip_type == PLIP_HEADER_TYPE2) {
-	    plip_send_enethdr(dev, (struct ethhdr*)buf);
-	}
 	for ( i = 0; i < sizeof(struct ethhdr); i++ ) {
-	    if (plip_type == PLIP_HEADER_TYPE1) {
-		send_byte(dev, *buf);
-	    }
+            send_byte(dev, *buf);
 	    checksum += *buf++;
 	}
 
@@ -772,29 +708,6 @@
     PRINTK2(("h_dest = %08x%04x h_source = %08x%04x\n",
 	    *(long*)&eth->h_dest[2],*(short*)&eth->h_dest[0],
 	    *(long*)&eth->h_source[2],*(short*)&eth->h_source[0]));
-    return 0;
-}
-
-static int
-plip_send_enethdr(struct device *dev, struct ethhdr *eth)
-{
-    send_byte(dev, eth->h_dest[ETH_ALEN-1]);
-    send_byte(dev, eth->h_source[ETH_ALEN-1]);
-    send_byte(dev, eth->h_proto >> 8);
-    send_byte(dev, eth->h_proto);
-    return 0;
-}
-
-static int
-plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth,
-		     unsigned char dest, unsigned char source,
-		     unsigned short type)
-{
-    eth->h_proto = type;
-    memcpy(eth->h_dest, dev->dev_addr, ETH_ALEN-1);
-    eth->h_dest[ETH_ALEN-1] = dest;
-    memcpy(eth->h_source, dev->dev_addr, ETH_ALEN-1);
-    eth->h_source[ETH_ALEN-1] = source;
     return 0;
 }
 
diff -u --recursive --new-file v1.1.3/linux/drivers/net/skeleton.c linux/drivers/net/skeleton.c
--- v1.1.3/linux/drivers/net/skeleton.c	Wed Feb  2 17:30:05 1994
+++ linux/drivers/net/skeleton.c	Wed Apr 13 12:52:19 1994
@@ -56,10 +56,9 @@
 #include <asm/dma.h>
 #include <errno.h>
 
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
 #ifndef HAVE_AUTOIRQ
 /* From auto_irq.c, in ioport.h for later versions. */
@@ -70,11 +69,6 @@
 extern struct device *irq2dev_map[16];
 #endif
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
 #ifndef HAVE_PORTRESERVE
 #define check_region(ioaddr, size) 		0
 #define	snarf_region(ioaddr, size);		do ; while (0)
@@ -111,9 +105,7 @@
 static void net_rx(struct device *dev);
 static int net_close(struct device *dev);
 static struct enet_statistics *net_get_stats(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 /* Example routines you must write ;->. */
 #define tx_done(dev) 1
@@ -213,36 +205,11 @@
 	dev->stop		= net_close;
 	dev->hard_start_xmit = net_send_packet;
 	dev->get_stats	= net_get_stats;
-#ifdef HAVE_MULTICAST
 	dev->set_multicast_list = &set_multicast_list;
-#endif
 
-	/* Fill in the fields of the device structure with ethernet-generic values.
-	   This should be in a common file instead of per-driver.  */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
-
-	dev->hard_header	= eth_header;
-	dev->add_arp		= eth_add_arp;
-	dev->queue_xmit		= dev_queue_xmit;
-	dev->rebuild_header	= eth_rebuild_header;
-	dev->type_trans		= eth_type_trans;
-
-	dev->type			= ARPHRD_ETHER;
-	dev->hard_header_len = ETH_HLEN;
-	dev->mtu			= 1500; /* eth_mtu */
-	dev->addr_len		= ETH_ALEN;
-	for (i = 0; i < ETH_ALEN; i++) {
-		dev->broadcast[i]=0xff;
-	}
-
-	/* New-style flags. */
-	dev->flags			= IFF_BROADCAST;
-	dev->family			= AF_INET;
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr		= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= sizeof(unsigned long);
+	/* Fill in the fields of the device structure with ethernet-generic values. */
+	
+	ether_setup(dev);
 
 	return 0;
 }
@@ -314,15 +281,6 @@
 		return 0;
 	}
 
-	/* For ethernet, fill in the header.  This should really be done by a
-	   higher level, rather than duplicated for each ethernet adaptor. */
-	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
-		skb->dev = dev;
-		arp_queue (skb);
-		return 0;
-	}
-	skb->arp=1;
-
 	/* Block a timer-based transmit from overlapping.  This could better be
 	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
 	if (set_bit(0, (void*)&dev->tbusy) != 0)
@@ -406,17 +364,14 @@
 			if (status & 0x04) lp->stats.rx_fifo_errors++;
 		} else {
 			/* Malloc up new buffer. */
-			int sksize = sizeof(struct sk_buff) + pkt_len;
 			struct sk_buff *skb;
 
-			skb = alloc_skb(sksize, GFP_ATOMIC);
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
-			skb->mem_len = sksize;
-			skb->mem_addr = skb;
 			skb->len = pkt_len;
 			skb->dev = dev;
 
@@ -426,16 +381,7 @@
 			/* or */
 			insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
 
-#ifdef HAVE_NETIF_RX
 			netif_rx(skb);
-#else
-			skb->lock = 0;
-			if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
-				kfree_s(skb, sksize);
-				lp->stats.rx_dropped++;
-				break;
-			}
-#endif
 			lp->stats.rx_packets++;
 		}
 	} while (--boguscount);
@@ -492,7 +438,6 @@
 	return &lp->stats;
 }
 
-#ifdef HAVE_MULTICAST
 /* Set or clear the multicast filter for this adaptor.
    num_addrs == -1	Promiscuous mode, receive all packets
    num_addrs == 0	Normal mode, clear multicast list
@@ -508,7 +453,6 @@
 	} else
 		outw(99, ioaddr);		/* Disable promiscuous mode, use normal mode */
 }
-#endif
 
 /*
  * Local variables:
diff -u --recursive --new-file v1.1.3/linux/drivers/net/slhc.c linux/drivers/net/slhc.c
--- v1.1.3/linux/drivers/net/slhc.c	Fri Dec 10 16:29:01 1993
+++ linux/drivers/net/slhc.c	Wed Apr 13 12:52:19 1994
@@ -36,8 +36,15 @@
  *			allow zero or one slots
  *			separate routines
  *			status display
+ *
+ *
+ *	This module is a difficult issue. Its clearly inet code but its also clearly
+ *	driver code belonging close to PPP and SLIP
  */
 
+#include <linux/config.h>
+#ifdef CONFIG_INET
+/* Entire module is for IP only */
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -47,15 +54,14 @@
 #include <linux/termios.h>
 #include <linux/in.h>
 #include <linux/fcntl.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "icmp.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
-#include "arp.h"
 #include <linux/errno.h>
 #include <linux/timer.h>
 #include <asm/system.h>
@@ -723,3 +729,4 @@
 	}
 }
 
+#endif /* CONFIG_INET */
diff -u --recursive --new-file v1.1.3/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.1.3/linux/drivers/net/slip.c	Mon Mar 21 16:16:10 1994
+++ linux/drivers/net/slip.c	Wed Apr 13 12:52:19 1994
@@ -21,6 +21,10 @@
  *		Michael Riepe	:	Automatic CSLIP recognition added
  *		Charles Hedrick :	CSLIP header length problem fix.
  *		Alan Cox	:	Corrected non-IP cases of the above.
+ *
+ *
+ *	FIXME:	This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
+ *	CONFIG_INET defined.
  */
  
 #include <asm/segment.h>
@@ -39,21 +43,24 @@
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #ifdef CONFIG_AX25
 #include "ax25.h"
 #endif
-#include "eth.h"
+#include <linux/etherdevice.h>
+#ifdef CONFIG_INET
 #include "ip.h"
 #include "route.h"
 #include "protocol.h"
 #include "tcp.h"
-#include "skbuff.h"
+#endif
+#include <linux/skbuff.h>
 #include "sock.h"
-#include "arp.h"
 #include "slip.h"
+#ifdef CONFIG_INET
 #include "slhc.h"
+#endif
 
 #define	SLIP_VERSION	"0.7.5"
 
@@ -363,6 +370,7 @@
   int count;
 
   count = sl->rcount;
+#ifdef CONFIG_INET  
   if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
     if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
 #if 1
@@ -408,7 +416,7 @@
 
   DPRINTF((DBG_SLIP, "<< \"%s\" recv:\r\n", sl->dev->name));
   ip_dump(sl->rbuff, sl->rcount);
-
+#endif
   /* Bump the datagram to the upper layers... */
   do {
 	DPRINTF((DBG_SLIP, "SLIP: packet is %d at 0x%X\n",
@@ -454,49 +462,14 @@
   }
 
   p = icp;
+#ifdef CONFIG_INET  
   if(sl->mode & SL_MODE_CSLIP)
 	  len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
-
-#ifdef OLD  
-  /*
-   * Send an initial END character to flush out any
-   * data that may have accumulated in the receiver
-   * due to line noise.
-   */
-  bp = sl->xbuff;
-  *bp++ = END;
-  count = 1;
-
-  /*
-   * For each byte in the packet, send the appropriate
-   * character sequence, according to the SLIP protocol.
-   */
-  while(len-- > 0) {
-	c = *p++;
-	switch(c) {
-		case END:
-			*bp++ = ESC;
-			*bp++ = ESC_END;
-			count += 2;
-                       	break;
-		case ESC:
-			*bp++ = ESC;
-			*bp++ = ESC_ESC;
-			count += 2;
-                       	break;
-		default:
-			*bp++ = c;
-			count++;
-	}
-  }
-  *bp++ = END;  
-  count++;
-#else
+#endif
   if(sl->mode & SL_MODE_SLIP6)
   	count=slip_esc6(p, (unsigned char *)sl->xbuff,len);
   else
   	count=slip_esc(p, (unsigned char *)sl->xbuff,len);
-#endif  	  
   sl->spacket++;
   bp = sl->xbuff;
 
@@ -552,36 +525,45 @@
 
   /* We were not, so we are now... :-) */
   if (skb != NULL) {
-#ifdef CONFIG_AX25  
-	if(sl->mode & SL_MODE_AX25)
-	{
-		if(!skb->arp && dev->rebuild_header(skb->data,dev))
-		{
-			skb->dev=dev;
-			arp_queue(skb);
-			return 0;
-		}
-		skb->arp=1;
-	}
+#if 0  
+#ifdef CONFIG_AX25 
+  	if(sl->mode & SL_MODE_AX25)
+  	{
+  		if(!skb->arp && dev->rebuild_header(skb->data,dev))
+  		{
+  			skb->dev=dev;
+  			arp_queue(skb);
+  			return 0;
+  		}
+  		skb->arp=1;
+  	}
 #endif  	
+#endif
 	sl_lock(sl);
-	size = skb->len;
-	if (!(sl->mode & SL_MODE_AX25)) {
-		if (size < sizeof(struct iphdr)) {
+	
+	size=skb->len;
+	
+	if(!(sl->mode&SL_MODE_AX25))
+	{
+		if(size<sizeof(struct iphdr))
+		{
 			printk("Runt IP frame fed to slip!\n");
-		} else {
-			size = ((struct iphdr *)(skb->data))->tot_len;
-			size = ntohs(size);
+		}
+		else
+		{
+			size=((struct iphdr *)(skb->data))->tot_len;
+			size=ntohs(size);
+		/*	sl_hex_dump(skb->data,skb->len);*/
 		}
 	}
-	/*	sl_hex_dump(skb->data,skb->len);*/
 	sl_encaps(sl, skb->data, size);
-	if (skb->free)
+	if (skb->free) 
 		kfree_skb(skb, FREE_WRITE);
   }
   return(0);
 }
 
+
 /* Return the frame type ID.  This is normally IP but maybe be AX.25. */
 static unsigned short
 sl_type_trans (struct sk_buff *skb, struct device *dev)
@@ -589,49 +571,37 @@
 #ifdef CONFIG_AX25
 	struct slip *sl=&sl_ctrl[dev->base_addr];
 	if(sl->mode&SL_MODE_AX25)
-		return(NET16(ETH_P_AX25));
+		return htons(ETH_P_AX25);
 #endif
-  return(NET16(ETH_P_IP));
+  return htons(ETH_P_IP);
 }
 
 
 /* Fill in the MAC-level header. Not used by SLIP. */
 static int
 sl_header(unsigned char *buff, struct device *dev, unsigned short type,
-	  unsigned long daddr, unsigned long saddr, unsigned len)
+	  void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
 {
 #ifdef CONFIG_AX25
   struct slip *sl=&sl_ctrl[dev->base_addr];
-  if((sl->mode&SL_MODE_AX25) && type!=NET16(ETH_P_AX25))
-  	return ax25_encapsulate_ip(buff,dev,type,daddr,saddr,len);
+  if((sl->mode&SL_MODE_AX25) && type!=htons(ETH_P_AX25))
+  	return ax25_encapsulate(buff,dev,type,daddr,saddr,len,skb);
 #endif  
 
   return(0);
 }
 
 
-/* Add an ARP-entry for this device's broadcast address. Not used. */
-static void
-sl_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
-{
-#ifdef CONFIG_AX25
-	struct slip *sl=&sl_ctrl[dev->base_addr];
-	
-	if(sl->mode&SL_MODE_AX25)
-		arp_add(addr,((char *) skb->data)+8,dev);
-#endif		
-}
-
-
 /* Rebuild the MAC-level header.  Not used by SLIP. */
 static int
-sl_rebuild_header(void *buff, struct device *dev)
+sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+		struct sk_buff *skb)
 {
 #ifdef CONFIG_AX25
   struct slip *sl=&sl_ctrl[dev->base_addr];
   
   if(sl->mode&SL_MODE_AX25)
-  	return ax25_rebuild_header(buff,dev);
+  	return ax25_rebuild_header(buff,dev,raddr, skb);
 #endif  
   return(0);
 }
@@ -779,33 +749,10 @@
 		break;
 	}
 	p = buff;
-#ifdef OLD	
-	while (count--) {
-		c = *p++;
-		if (sl->escape) {
-			if (c == ESC_ESC)
-				sl_enqueue(sl, ESC);
-			else if (c == ESC_END)
-				sl_enqueue(sl, END);
-			else
-				printk ("SLIP: received wrong character\n");
-			sl->escape = 0;
-		} else {
-			if (c == ESC)
-				sl->escape = 1;
-			else if (c == END) {
-				if (sl->rcount > 2) sl_bump(sl);
-				sl_dequeue(sl, sl->rcount);
-				sl->rcount = 0;
-			} else	sl_enqueue(sl, c);
-		}
-	}
-#else
 	if(sl->mode & SL_MODE_SLIP6)
 		slip_unesc6(sl,buff,count,error);
 	else
 		slip_unesc(sl,buff,count,error);
-#endif		
   } while(1);
   
 }
@@ -1102,9 +1049,9 @@
   DPRINTF((DBG_SLIP, "SLIP: ioctl(%d, 0x%X, 0x%X)\n", tty->line, cmd, arg));
   switch(cmd) {
 	case SIOCGIFNAME:
-		err=verify_area(VERIFY_WRITE, arg, strlen(sl->dev->name) + 1);
+		err=verify_area(VERIFY_WRITE, arg, 16);
 		if(err)
-			return err;
+			return -err;
 		memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
 		return(0);
 	case SIOCGIFENCAP:
@@ -1156,7 +1103,9 @@
   if (already++ == 0) {
 	printk("SLIP: version %s (%d channels)\n",
 				SLIP_VERSION, SL_NRUNIT);
+#ifdef CONFIG_INET				
 	printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+#endif	
 #ifdef CONFIG_AX25
 	printk("AX25: KISS encapsulation enabled\n");
 #endif	
@@ -1191,7 +1140,6 @@
   dev->open		= sl_open;
   dev->stop		= sl_close;
   dev->hard_header	= sl_header;
-  dev->add_arp		= sl_add_arp;
   dev->type_trans	= sl_type_trans;
   dev->get_stats	= sl_get_stats;
 #ifdef HAVE_SET_MAC_ADDR
@@ -1206,10 +1154,9 @@
   memcpy(dev->broadcast,ax25_bcast,7);		/* Only activated in AX.25 mode */
   memcpy(dev->dev_addr,ax25_test,7);		/*    ""      ""       ""    "" */
 #endif  
-  dev->queue_xmit	= dev_queue_xmit;
   dev->rebuild_header	= sl_rebuild_header;
   for (i = 0; i < DEV_NUMBUFFS; i++)
-		dev->buffs[i] = NULL;
+	skb_queue_head_init(&dev->buffs[i]);
 
   /* New-style flags. */
   dev->flags		= 0;
diff -u --recursive --new-file v1.1.3/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v1.1.3/linux/drivers/net/smc-ultra.c	Tue Mar  1 19:29:05 1994
+++ linux/drivers/net/smc-ultra.c	Wed Apr 13 12:52:19 1994
@@ -24,7 +24,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
-#include "dev.h"
+#include <linux/netdevice.h>
 #include "8390.h"
 
 /* Compatibility definitions for earlier kernel versions. */
diff -u --recursive --new-file v1.1.3/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v1.1.3/linux/drivers/net/wd.c	Fri Dec 17 11:38:47 1993
+++ linux/drivers/net/wd.c	Wed Apr 13 12:52:19 1994
@@ -25,7 +25,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
-#include "dev.h"
+#include <linux/netdevice.h>
 #include "8390.h"
 
 /* Compatibility definitions for earlier kernel versions. */
diff -u --recursive --new-file v1.1.3/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.1.3/linux/drivers/scsi/sd.c	Wed Apr 13 11:33:52 1994
+++ linux/drivers/scsi/sd.c	Wed Apr 13 10:47:04 1994
@@ -632,12 +632,12 @@
 	      printk("Use sg, count %d %x %d\n", SCpnt->use_sg, count, dma_free_sectors);
 	      printk("maxsg = %x, counted = %d this_count = %d\n", max_sg, counted, this_count);
 	      while(bh){
-		printk("[%8.8x %x] ", bh->b_data, bh->b_size);
+		printk("[%p %lx] ", bh->b_data, bh->b_size);
 		bh = bh->b_reqnext;
 	      };
 	      if(SCpnt->use_sg < 16)
 		for(count=0; count<SCpnt->use_sg; count++)
-		  printk("{%d:%8.8x %8.8x %d}  ", count,
+		  printk("{%d:%p %p %d}  ", count,
 			 sgpnt[count].address,
 			 sgpnt[count].alt_address,
 			 sgpnt[count].length);
diff -u --recursive --new-file v1.1.3/linux/fs/buffer.c linux/fs/buffer.c
--- v1.1.3/linux/fs/buffer.c	Wed Apr 13 18:37:57 1994
+++ linux/fs/buffer.c	Wed Apr 13 10:44:23 1994
@@ -193,10 +193,9 @@
 			 /* If an unlocked buffer is not uptodate, there has
 			     been an IO error. Skip it. */
 			 if (wait && bh->b_req && !bh->b_lock &&
-			     !bh->b_dirt && !bh->b_uptodate)
-			  {
+			     !bh->b_dirt && !bh->b_uptodate) {
 				  err = 1;
-				  printk("Weird - unlocked, clean and not uptodate buffer on %d list %d\n", nlist);
+				  printk("Weird - unlocked, clean and not uptodate buffer on list %d\n", nlist);
 				  continue;
 			  }
 			 /* Don't write clean buffers.  Don't write ANY buffers
@@ -208,7 +207,7 @@
 			 ll_rw_block(WRITE, 1, &bh);
 
 			 if(nlist != BUF_DIRTY) { 
-				 printk("[%d %x %d] ", nlist, bh->b_dev, bh->b_blocknr);
+				 printk("[%d %x %ld] ", nlist, bh->b_dev, bh->b_blocknr);
 				 ncount++;
 			 };
 			 bh->b_count--;
@@ -596,7 +595,8 @@
 	 to request some blocks in a filesystem that we know that we will
 	 be needing ahead of time. */
 
-	if(nr_free[isize] > 100) return 0;
+	if (nr_free[isize] > 100)
+		return;
 
 	/* If there are too many dirty buffers, we wake up the update process
 	   now so as to ensure that there are still clean buffers available
diff -u --recursive --new-file v1.1.3/linux/fs/proc/net.c linux/fs/proc/net.c
--- v1.1.3/linux/fs/proc/net.c	Tue Mar 22 08:54:43 1994
+++ linux/fs/proc/net.c	Wed Apr 13 12:52:20 1994
@@ -15,6 +15,8 @@
  *            quite a bit, modularized the code.
  * fvk 4/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
  *	      Renamed "route_get_info()" to "rt_get_info()" for consistency.
+ * Alan Cox (gw4pts@gw4pts.ampr.org) 4/94
+ *	      Dusted off the code and added IPX. Fixed the 4K limit.
  *
  *  proc net directory handling functions
  */
@@ -36,17 +38,20 @@
 
 /* the get_*_info() functions are in the net code, and are configured
    in via the standard mechanism... */
-extern int unix_get_info(char *);
+extern int unix_get_info(char *, char **, off_t, int);
 #ifdef CONFIG_INET
-extern int tcp_get_info(char *);
-extern int udp_get_info(char *);
-extern int raw_get_info(char *);
-extern int arp_get_info(char *);
-extern int dev_get_info(char *);
-extern int rt_get_info(char *);
+extern int tcp_get_info(char *, char **, off_t, int);
+extern int udp_get_info(char *, char **, off_t, int);
+extern int raw_get_info(char *, char **, off_t, int);
+extern int arp_get_info(char *, char **, off_t, int);
+extern int dev_get_info(char *, char **, off_t, int);
+extern int rt_get_info(char *, char **, off_t, int);
 #endif /* CONFIG_INET */
+#ifdef CONFIG_IPX
+extern int ipx_get_info(char *, char **, off_t, int);
+extern int ipx_rt_get_info(char *, char **, off_t, int);
+#endif /* CONFIG_IPX */
 
-
 static struct file_operations proc_net_operations = {
 	NULL,			/* lseek - default */
 	proc_readnet,		/* read - bad */
@@ -93,6 +98,10 @@
 	{ 133,3,"tcp" },
 	{ 134,3,"udp" }
 #endif	/* CONFIG_INET */
+#ifdef CONFIG_IPX
+	,{ 135,9,"ipx_route" },
+	{ 136,3,"ipx" }
+#endif /* CONFIG_IPX */
 };
 
 #define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])))
@@ -154,57 +163,86 @@
 }
 
 
+#define PROC_BLOCK_SIZE	(3*1024)		/* 4K page size but our output routines use some slack for overruns */
+
 static int proc_readnet(struct inode * inode, struct file * file,
 			char * buf, int count)
 {
 	char * page;
 	int length;
-	int end;
 	unsigned int ino;
+	int bytes=count;
+	int thistime;
+	int copied=0;
+	char *start;
 
 	if (count < 0)
 		return -EINVAL;
 	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	ino = inode->i_ino;
-	switch (ino) {
-		case 128:
-			length = unix_get_info(page);
-			break;
+
+	while(bytes>0)
+	{
+		thistime=bytes;
+		if(bytes>PROC_BLOCK_SIZE)
+			thistime=PROC_BLOCK_SIZE;
+
+		switch (ino) 
+		{
+			case 128:
+				length = unix_get_info(page,&start,file->f_pos,thistime);
+				break;
 #ifdef CONFIG_INET
-		case 129:
-			length = arp_get_info(page);
-			break;
-		case 130:
-			length = rt_get_info(page);
-			break;
-		case 131:
-			length = dev_get_info(page);
-			break;
-		case 132:
-			length = raw_get_info(page);
-			break;
-		case 133:
-			length = tcp_get_info(page);
-			break;
-		case 134:
-			length = udp_get_info(page);
-			break;
+			case 129:
+				length = arp_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 130:
+				length = rt_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 131:
+				length = dev_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 132:
+				length = raw_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 133:
+				length = tcp_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 134:
+				length = udp_get_info(page,&start,file->f_pos,thistime);
+				break;
 #endif /* CONFIG_INET */
-		default:
-			free_page((unsigned long) page);
-			return -EBADF;
-	}
-	if (file->f_pos >= length) {
-		free_page((unsigned long) page);
-		return 0;
+#ifdef CONFIG_IPX
+			case 135:
+				length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
+				break;
+			case 136:
+				length = ipx_get_info(page,&start,file->f_pos,thistime);
+				break;
+#endif /* CONFIG_IPX */
+			default:
+				free_page((unsigned long) page);
+				return -EBADF;
+		}
+		
+		/*
+ 		 *	We have been given a non page aligned block of
+		 *	the data we asked for + a bit. We have been given
+ 		 *	the start pointer and we know the length.. 
+		 */
+
+		/*
+ 		 *	Copy the bytes
+		 */
+		memcpy_tofs(buf+copied, start, length);
+		file->f_pos+=length;	/* Move down the file */
+		bytes-=length;
+		copied+=length;
+		if(length<thistime)
+			break;	/* End of file */
 	}
-	if (count + file->f_pos > length)
-		count = length - file->f_pos;
-	end = count + file->f_pos;
-	memcpy_tofs(buf, page + file->f_pos, count);
 	free_page((unsigned long) page);
-	file->f_pos = end;
-	return count;
+	return copied;
 
 }
diff -u --recursive --new-file v1.1.3/linux/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- v1.1.3/linux/include/linux/etherdevice.h
+++ linux/include/linux/etherdevice.h	Wed Apr 13 12:52:20 1994
@@ -0,0 +1,41 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  NET  is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Definitions for the Ethernet handlers.
+ *
+ * Version:	@(#)eth.h	1.0.4	05/13/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ *		Relocated to include/linux where it belongs by Alan Cox 
+ *							<gw4pts@gw4pts.ampr.org>
+ *
+ *		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.
+ *
+ *	WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_ETHERDEVICE_H
+#define _LINUX_ETHERDEVICE_H
+
+
+#include <linux/if_ether.h>
+
+#ifdef __KERNEL__
+extern int		eth_header(unsigned char *buff, struct device *dev,
+				   unsigned short type, void *daddr,
+				   void *saddr, unsigned len,
+				   struct sk_buff *skb);
+extern int		eth_rebuild_header(void *buff, struct device *dev,
+			unsigned long raddr, struct sk_buff *skb);
+extern unsigned short	eth_type_trans(struct sk_buff *skb, struct device *dev);
+
+#endif
+
+#endif	/* _LINUX_ETHERDEVICE_H */
diff -u --recursive --new-file v1.1.3/linux/include/linux/if.h linux/include/linux/if.h
--- v1.1.3/linux/include/linux/if.h	Tue Apr  5 08:14:29 1994
+++ linux/include/linux/if.h	Wed Apr 13 12:52:20 1994
@@ -22,44 +22,6 @@
 #include <linux/types.h>		/* for "caddr_t" et al		*/
 #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
 
-
-/* Structure defining a queue for a network interface. */
-#ifdef not_yet_in_linux
-struct ifnet {
-  char		*if_name;		/* name, e.g. ``en'' or ``lo''	*/
-  short		if_unit;		/* sub-unit for device driver	*/
-  short		if_mtu;			/* maximum transmission unit	*/
-  short		if_flags;		/* up/down, broadcast, etc.	*/
-  short		if_timer;		/* time 'til if_watchdog called	*/
-  int		if_metric;		/* routing metric (not used)	*/
-  struct	ifaddr *if_addrlist;	/* linked list of addrs per if	*/
-  struct	ifqueue {
-	struct mbuf	*ifq_head;
-	struct mbuf	*ifq_tail;
-	int		ifq_len;
-	int		ifq_maxlen;
-	int		ifq_drops;
-  } if_snd;				/* output queue			*/
-
-  /* Procedure handles. */
-  int		(*if_init)();		/* init routine			*/
-  int		(*if_output)();		/* output routine		*/
-  int		(*if_ioctl)();		/* ioctl routine		*/
-  int		(*if_reset)();		/* bus reset routine		*/
-  int		(*if_watchdog)();	/* timer routine		*/
-
-  /* Generic interface statistics. */
-  int		if_ipackets;		/* packets recv'd on interface	*/
-  int		if_ierrors;		/* input errors on interface	*/
-  int		if_opackets;		/* packets sent on interface	*/
-  int		if_oerrors;		/* output errors on interface	*/
-  int		if_collisions;		/* collisions on CSMA i'faces	*/
-
-  /* Linked list: pointer to next interface. */
-  struct ifnet	*if_next;
-};
-#endif
-
 /* Standard interface flags. */
 #define	IFF_UP		0x1		/* interface is up		*/
 #define	IFF_BROADCAST	0x2		/* broadcast address valid	*/
@@ -106,7 +68,7 @@
 	union
 	{
 		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
-		char	ifrn_hwaddr[IFHWADDRLEN];
+		char	ifrn_hwaddr[IFHWADDRLEN];	/* Obsolete */
 	} ifr_ifrn;
 	
 	union {
@@ -114,6 +76,7 @@
 		struct	sockaddr ifru_dstaddr;
 		struct	sockaddr ifru_broadaddr;
 		struct	sockaddr ifru_netmask;
+		struct  sockaddr ifru_hwaddr;
 		short	ifru_flags;
 		int	ifru_metric;
 		int	ifru_mtu;
@@ -122,7 +85,8 @@
 };
 
 #define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
-#define ifr_hwaddr	ifr_ifrn.ifrn_hwaddr	/* interface hardware   */
+#define old_ifr_hwaddr	ifr_ifrn.ifrn_hwaddr	/* interface hardware   */
+#define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
 #define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
 #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
 #define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
diff -u --recursive --new-file v1.1.3/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.1.3/linux/include/linux/if_arp.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/if_arp.h	Wed Apr 13 12:52:20 1994
@@ -38,33 +38,6 @@
 #define	ARPOP_RREPLY	4		/* RARP reply			*/
 
 
-/*
- * Address Resolution Protocol.
- *
- * See RFC 826 for protocol description.  ARP packets are variable
- * in size; the arphdr structure defines the fixed-length portion.
- * Protocol type values are the same as those for 10 Mb/s Ethernet.
- * It is followed by the variable-sized fields ar_sha, arp_spa,
- * arp_tha and arp_tpa in that order, according to the lengths
- * specified.  Field names used correspond to RFC 826.
- */
-struct arphdr {
-  unsigned short	ar_hrd;		/* format of hardware address	*/
-  unsigned short	ar_pro;		/* format of protocol address	*/
-  unsigned char		ar_hln;		/* length of hardware address	*/
-  unsigned char		ar_pln;		/* length of protocol address	*/
-  unsigned short	ar_op;		/* ARP opcode (command)		*/
-
-  /* The rest is variable in size, according to the sizes above. */
-#if 0
-  unsigned char		ar_sha[];	/* sender hardware address	*/
-  unsigned char		ar_spa[];	/* sender protocol address	*/
-  unsigned char		ar_tha[];	/* target hardware address	*/
-  unsigned char		ar_tpa[];	/* target protocol address	*/
-#endif	/* not actually included! */
-};
-
-
 /* ARP ioctl request. */
 struct arpreq {
   struct sockaddr	arp_pa;		/* protocol address		*/
@@ -73,7 +46,6 @@
 };
 
 /* ARP Flag values. */
-#define	ATF_INUSE	0x01		/* entry in use			*/
 #define ATF_COM		0x02		/* completed entry (ha valid)	*/
 #define	ATF_PERM	0x04		/* permanent entry		*/
 #define	ATF_PUBL	0x08		/* publish entry		*/
diff -u --recursive --new-file v1.1.3/linux/include/linux/inet.h linux/include/linux/inet.h
--- v1.1.3/linux/include/linux/inet.h
+++ linux/include/linux/inet.h	Wed Apr 13 12:52:20 1994
@@ -0,0 +1,90 @@
+/*
+ *		Swansea University Computer Society NET3
+ *
+ *	This work is derived from NET2Debugged, which is in turn derived
+ *	from NET2D which was written by:
+ * 		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ *		This work was derived friom Ross Biro's inspirational work
+ *		for the LINUX operating system.  His version numbers were:
+ *
+ *		$Id: Space.c,v     0.8.4.5  1992/12/12 19:25:04 bir7 Exp $
+ *		$Id: arp.c,v       0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
+ *		$Id: arp.h,v       0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
+ *		$Id: dev.c,v       0.8.4.13 1993/01/23 18:00:11 bir7 Exp $
+ *		$Id: dev.h,v       0.8.4.7  1993/01/23 18:00:11 bir7 Exp $
+ *		$Id: eth.c,v       0.8.4.4  1993/01/22 23:21:38 bir7 Exp $
+ *		$Id: eth.h,v       0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
+ *		$Id: icmp.c,v      0.8.4.9  1993/01/23 18:00:11 bir7 Exp $
+ *		$Id: icmp.h,v      0.8.4.2  1992/11/15 14:55:30 bir7 Exp $
+ * 		$Id: ip.c,v        0.8.4.8  1992/12/12 19:25:04 bir7 Exp $
+ * 		$Id: ip.h,v        0.8.4.2  1993/01/23 18:00:11 bir7 Exp $
+ * 		$Id: loopback.c,v  0.8.4.8  1993/01/23 18:00:11 bir7 Exp $
+ * 		$Id: packet.c,v    0.8.4.7  1993/01/26 22:04:00 bir7 Exp $
+ *		$Id: protocols.c,v 0.8.4.3  1992/11/15 14:55:30 bir7 Exp $
+ *		$Id: raw.c,v       0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ *		$Id: sock.c,v      0.8.4.6  1993/01/28 22:30:00 bir7 Exp $
+ *		$Id: sock.h,v      0.8.4.7  1993/01/26 22:04:00 bir7 Exp $
+ *		$Id: tcp.c,v       0.8.4.16 1993/01/26 22:04:00 bir7 Exp $
+ *		$Id: tcp.h,v       0.8.4.7  1993/01/22 22:58:08 bir7 Exp $
+ *		$Id: timer.c,v     0.8.4.8  1993/01/23 18:00:11 bir7 Exp $
+ *		$Id: timer.h,v     0.8.4.2  1993/01/23 18:00:11 bir7 Exp $
+ *		$Id: udp.c,v       0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ *		$Id: udp.h,v       0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
+ *		$Id: we.c,v        0.8.4.10 1993/01/23 18:00:11 bir7 Exp $
+ *		$Id: wereg.h,v     0.8.4.1  1992/11/10 00:17:18 bir7 Exp $
+ *
+ *		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.
+ */
+#ifndef _LINUX_INET_H
+#define _LINUX_INET_H
+
+
+#include <linux/ddi.h>
+
+
+#undef	INET_DEBUG
+#ifdef	INET_DEBUG
+#  define	DPRINTF(x)	dprintf x 
+#else
+#   define	DPRINTF(x)	do ; while (0)
+#endif
+
+/* Debug levels. One per module. */
+#define DBG_OFF		0			/* no debugging		*/
+#define DBG_INET	1			/* sock.c		*/
+#define DBG_RT		2			/* route.c		*/
+#define DBG_DEV		3			/* dev.c		*/
+#define DBG_ETH		4			/* eth.c		*/
+#define DBG_PROTO	5			/* protocol.c		*/
+#define DBG_TMR		6			/* timer.c		*/
+#define DBG_PKT		7			/* packet.c		*/
+#define DBG_RAW		8			/* raw.c		*/
+
+#define DBG_LOOPB	10			/* loopback.c		*/
+#define DBG_SLIP	11			/* slip.c		*/
+
+#define DBG_ARP		20			/* arp.c		*/
+#define DBG_IP		21			/* ip.c			*/
+#define DBG_ICMP	22			/* icmp.c		*/
+#define DBG_TCP		23			/* tcp.c		*/
+#define DBG_UDP		24			/* udp.c		*/
+
+#ifdef __KERNEL__
+
+extern int		inet_debug;
+
+
+extern void		inet_proto_init(struct ddi_proto *pro);
+extern char		*in_ntoa(unsigned long in);
+extern unsigned long	in_aton(char *str);
+
+extern void		dprintf(int level, char *fmt, ...);
+
+extern int		dbg_ioctl(void *arg, int level);
+
+#endif
+#endif	/* _LINUX_INET_H */
diff -u --recursive --new-file v1.1.3/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v1.1.3/linux/include/linux/ipx.h
+++ linux/include/linux/ipx.h	Wed Apr 13 12:52:20 1994
@@ -0,0 +1,23 @@
+struct sockaddr_ipx
+{
+	short sipx_family;
+	unsigned long  sipx_network;
+	unsigned char sipx_node[6];
+	short sipx_port;
+};
+
+struct ipx_route_def
+{
+	unsigned long ipx_network;
+	unsigned long ipx_router_network;
+#define IPX_ROUTE_NO_ROUTER	0
+	unsigned char ipx_router_node[6];
+	unsigned char ipx_device[16];
+	unsigned short ipx_flags;
+#define IPX_RT_BLUEBOOK		2
+#define IPX_RT_ROUTED		1
+};
+
+#define IPX_MTU		576
+
+
diff -u --recursive --new-file v1.1.3/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.1.3/linux/include/linux/netdevice.h
+++ linux/include/linux/netdevice.h	Wed Apr 13 12:52:20 1994
@@ -0,0 +1,195 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Definitions for the Interfaces handler.
+ *
+ * Version:	@(#)dev.h	1.0.10	08/12/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Corey Minyard <wf-rch!minyard@relay.EU.net>
+ *		Donald J. Becker, <becker@super.org>
+ *
+ *		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.
+ *
+ *		Moved to /usr/include/linux for NET3
+ */
+#ifndef _LINUX_NETDEVICE_H
+#define _LINUX_NETDEVICE_H
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+/* for future expansion when we will have different priorities. */
+#define DEV_NUMBUFFS	3
+#define MAX_ADDR_LEN	7
+#define MAX_HEADER	18
+
+#define IS_MYADDR	1		/* address is (one of) our own	*/
+#define IS_LOOPBACK	2		/* address is for LOOPBACK	*/
+#define IS_BROADCAST	3		/* address is a valid broadcast	*/
+#define IS_INVBCAST	4		/* Wrong netmask bcast not for us */
+
+/*
+ * The DEVICE structure.
+ * Actually, this whole structure is a big mistake.  It mixes I/O
+ * data with strictly "high-level" data, and it has to know about
+ * almost every data structure used in the INET module.  
+ */
+struct device 
+{
+
+  /*
+   * This is the first field of the "visible" part of this structure
+   * (i.e. as seen by users in the "Space.c" file).  It is the name
+   * the interface.
+   */
+  char			  *name;
+
+  /* I/O specific fields.  These will be moved to DDI soon. */
+  unsigned long		  rmem_end;		/* shmem "recv" end	*/
+  unsigned long		  rmem_start;		/* shmem "recv" start	*/
+  unsigned long		  mem_end;		/* sahared mem end	*/
+  unsigned long		  mem_start;		/* shared mem start	*/
+  unsigned short	  base_addr;		/* device I/O address	*/
+  unsigned char		  irq;			/* device IRQ number	*/
+
+  /* Low-level status flags. */
+  volatile unsigned char  start,		/* start an operation	*/
+                          tbusy,		/* transmitter busy	*/
+                          interrupt;		/* interrupt arrived	*/
+
+  /*
+   * Another mistake.
+   * This points to the next device in the "dev" chain. It will
+   * be moved to the "invisible" part of the structure as soon as
+   * it has been cleaned up. -FvK
+   */
+  struct device		  *next;
+
+  /* The device initialization function. Called only once. */
+  int			  (*init)(struct device *dev);
+
+  /* Some hardware also needs these fields, but they are not part of the
+     usual set specified in Space.c. */
+  unsigned char		  if_port;		/* Selectable AUI, TP,..*/
+  unsigned char		  dma;			/* DMA channel		*/
+
+  struct enet_statistics* (*get_stats)(struct device *dev);
+
+  /*
+   * This marks the end of the "visible" part of the structure. All
+   * fields hereafter are internal to the system, and may change at
+   * will (read: may be cleaned up at will).
+   */
+
+  /* These may be needed for future network-power-down code. */
+  unsigned long		  trans_start;	/* Time (in jiffies) of last Tx	*/
+  unsigned long		  last_rx;	/* Time of last Rx		*/
+
+  unsigned short	  flags;	/* interface flags (a la BSD)	*/
+  unsigned short	  family;	/* address family ID (AF_INET)	*/
+  unsigned short	  metric;	/* routing metric (not used)	*/
+  unsigned short	  mtu;		/* interface MTU value		*/
+  unsigned short	  type;		/* interface hardware type	*/
+  unsigned short	  hard_header_len;	/* hardware hdr length	*/
+  void			  *priv;	/* pointer to private data	*/
+
+  /* Interface address info. */
+  unsigned char		  broadcast[MAX_ADDR_LEN];	/* hw bcast add	*/
+  unsigned char		  dev_addr[MAX_ADDR_LEN];	/* hw address	*/
+  unsigned char		  addr_len;	/* harfware address length	*/
+  unsigned long		  pa_addr;	/* protocol address		*/
+  unsigned long		  pa_brdaddr;	/* protocol broadcast addr	*/
+  unsigned long		  pa_dstaddr;	/* protocol P-P other side addr	*/
+  unsigned long		  pa_mask;	/* protocol netmask		*/
+  unsigned short	  pa_alen;	/* protocol address length	*/
+
+  /* Pointer to the interface buffers. */
+  struct sk_buff_head	  buffs[DEV_NUMBUFFS];
+
+  /* Pointers to interface service routines. */
+  int			  (*open)(struct device *dev);
+  int			  (*stop)(struct device *dev);
+  int			  (*hard_start_xmit) (struct sk_buff *skb,
+					      struct device *dev);
+  int			  (*hard_header) (unsigned char *buff,
+					  struct device *dev,
+					  unsigned short type,
+					  void *daddr,
+					  void *saddr,
+					  unsigned len,
+					  struct sk_buff *skb);
+  int			  (*rebuild_header)(void *eth, struct device *dev,
+				unsigned long raddr, struct sk_buff *skb);
+  unsigned short	  (*type_trans) (struct sk_buff *skb,
+					 struct device *dev);
+#define HAVE_MULTICAST			 
+  void			  (*set_multicast_list)(struct device *dev,
+  					 int num_addrs, void *addrs);
+#define HAVE_SET_MAC_ADDR  		 
+  int			  (*set_mac_address)(struct device *dev, void *addr);
+};
+
+
+struct packet_type {
+  unsigned short	type;	/* This is really htons(ether_type). */
+  unsigned short	copy:1;
+  int			(*func) (struct sk_buff *, struct device *,
+				 struct packet_type *);
+  void			*data;
+  struct packet_type	*next;
+};
+
+
+#ifdef __KERNEL__
+
+/* Used by dev_rint */
+#define IN_SKBUFF	1
+
+extern volatile char in_bh;
+
+extern struct device	*dev_base;
+extern struct packet_type *ptype_base;
+
+
+extern int		ip_addr_match(unsigned long addr1, unsigned long addr2);
+extern int		ip_chk_addr(unsigned long addr);
+extern struct device	*ip_dev_check(unsigned long daddr);
+extern unsigned long	ip_my_addr(void);
+extern unsigned long	ip_get_mask(unsigned long addr);
+
+extern void		dev_add_pack(struct packet_type *pt);
+extern void		dev_remove_pack(struct packet_type *pt);
+extern struct device	*dev_get(char *name);
+extern int		dev_open(struct device *dev);
+extern int		dev_close(struct device *dev);
+extern void		dev_queue_xmit(struct sk_buff *skb, struct device *dev,
+				       int pri);
+#define HAVE_NETIF_RX 1
+extern void		netif_rx(struct sk_buff *skb);
+/* The old interface to netif_rx(). */
+extern int		dev_rint(unsigned char *buff, long len, int flags,
+				 struct device * dev);
+extern void		dev_transmit(void);
+extern int		in_inet_bh(void);
+extern void		inet_bh(void *tmp);
+extern void		dev_tint(struct device *dev);
+extern int		dev_get_info(char *buffer, char **start, off_t offset, int length);
+extern int		dev_ioctl(unsigned int cmd, void *);
+
+extern void		dev_init(void);
+
+/* This function lives elsewhere (drivers/net/net_init.c but is related) */
+
+extern void		ether_setup(struct device *dev);
+
+#endif /* __KERNEL__ */
+
+#endif	/* _LINUX_DEV_H */
diff -u --recursive --new-file v1.1.3/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.1.3/linux/include/linux/sched.h	Wed Mar 30 08:42:03 1994
+++ linux/include/linux/sched.h	Wed Apr 13 17:08:41 1994
@@ -23,6 +23,12 @@
 
 extern unsigned long intr_count;
 
+#define start_bh_atomic() \
+__asm__ __volatile__("incl _intr_count")
+
+#define end_bh_atomic() \
+__asm__ __volatile__("decl _intr_count")
+
 /*
  * Bus types (default is ISA, but people can check others with these..)
  * MCA_bus hardcoded to 0 for now.
diff -u --recursive --new-file v1.1.3/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.1.3/linux/include/linux/skbuff.h
+++ linux/include/linux/skbuff.h	Wed Apr 13 13:09:49 1994
@@ -0,0 +1,137 @@
+/*
+ *	Definitions for the 'struct sk_buff' memory handlers.
+ *
+ *	Authors:
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Florian La Roche, <rzsfl@rz.uni-sb.de>
+ *
+ *	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.
+ */
+ 
+#ifndef _LINUX_SKBUFF_H
+#define _LINUX_SKBUFF_H
+#include <linux/malloc.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+
+#define CONFIG_SKB_CHECK	1
+
+#define HAVE_ALLOC_SKB		/* For the drivers to know */
+
+
+#define FREE_READ	1
+#define FREE_WRITE	0
+
+
+struct sk_buff_head {
+  struct sk_buff		* volatile next;
+  struct sk_buff		* volatile prev;
+#if CONFIG_SKB_CHECK
+  int				magic_debug_cookie;
+#endif
+};
+
+
+struct sk_buff {
+  struct sk_buff		* volatile next;
+  struct sk_buff		* volatile prev;
+#if CONFIG_SKB_CHECK
+  int				magic_debug_cookie;
+#endif
+  struct sk_buff		* volatile link3;
+  struct sock			*sk;
+  volatile unsigned long	when;	/* used to compute rtt's	*/
+  struct timeval		stamp;
+  struct device			*dev;
+  void				*mem_addr;
+  union {
+	struct tcphdr	*th;
+	struct ethhdr	*eth;
+	struct iphdr	*iph;
+	struct udphdr	*uh;
+	unsigned char	*raw;
+	unsigned long	seq;
+  } h;
+  struct iphdr		*ip_hdr;		/* For IPPROTO_RAW */
+  unsigned long			mem_len;
+  unsigned long 		len;
+  unsigned long			fraglen;
+  struct sk_buff		*fraglist;	/* Fragment list */
+  unsigned long			truesize;
+  unsigned long 		saddr;
+  unsigned long 		daddr;
+  unsigned long			raddr;		/* next hop addr */
+  volatile char 		acked,
+				used,
+				free,
+				arp;
+  unsigned char			tries,lock;
+  unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) */
+  unsigned long			padding[0];
+  unsigned char			data[0];
+};
+
+#define SK_WMEM_MAX	32767
+#define SK_RMEM_MAX	32767
+
+#ifdef CONFIG_SKB_CHECK
+#define SK_FREED_SKB	0x0DE2C0DE
+#define SK_GOOD_SKB	0xDEC0DED1
+#define SK_HEAD_SKB	0x12231298
+#endif
+
+#ifdef __KERNEL__
+/*
+ *	Handling routines are only of interest to the kernel
+ */
+ 
+#if 0
+extern void			print_skb(struct sk_buff *);
+#endif
+extern void			kfree_skb(struct sk_buff *skb, int rw);
+extern void			skb_queue_head_init(struct sk_buff_head *list);
+extern void			skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
+extern void			skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
+extern struct sk_buff *		skb_dequeue(struct sk_buff_head *list);
+extern void 			skb_insert(struct sk_buff *old,struct sk_buff *newsk);
+extern void			skb_append(struct sk_buff *old,struct sk_buff *newsk);
+extern void			skb_unlink(struct sk_buff *buf);
+extern struct sk_buff *		skb_peek_copy(struct sk_buff_head *list);
+extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
+extern void			kfree_skbmem(void *mem, unsigned size);
+extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
+extern void			skb_kept_by_device(struct sk_buff *skb);
+extern void			skb_device_release(struct sk_buff *skb,
+					int mode);
+extern int			skb_device_locked(struct sk_buff *skb);
+/*
+ *	Peek an sk_buff. Unlike most other operations you _MUST_
+ *	be careful with this one. A peek leaves the buffer on the
+ *	list and someone else may run off with it. For an interrupt
+ *	type system cli() peek the buffer copy the data and sti();
+ */
+static __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
+{
+	struct sk_buff *list = (struct sk_buff *)list_;
+	return (list->next != list)? list->next : NULL;
+}
+
+#if CONFIG_SKB_CHECK
+extern int 			skb_check(struct sk_buff *skb,int,int, char *);
+#define IS_SKB(skb)		skb_check((skb), 0, __LINE__,__FILE__)
+#define IS_SKB_HEAD(skb)	skb_check((skb), 1, __LINE__,__FILE__)
+#else
+#define IS_SKB(skb)		0
+#define IS_SKB_HEAD(skb)	0
+#endif
+
+extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
+extern int			datagram_select(struct sock *sk, int sel_type, select_table *wait);
+extern void			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
+extern void			skb_free_datagram(struct sk_buff *skb);
+
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_SKBUFF_H */
diff -u --recursive --new-file v1.1.3/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.1.3/linux/include/linux/sockios.h	Sat Apr  2 20:05:43 1994
+++ linux/include/linux/sockios.h	Wed Apr 13 12:52:20 1994
@@ -19,25 +19,14 @@
 #define _LINUX_SOCKIOS_H
 
 /* This section will go away soon! */
-#if 1	/* FIXME: */
-#define MAX_IP_NAME	20
-#define IP_SET_DEV	0x2401
 
-struct ip_config {
-  char		name[MAX_IP_NAME];
-  unsigned long	paddr;
-  unsigned long	router;
-  unsigned long	net;
-  unsigned up:1,destroy:1;
-};
-#endif	/* FIXME: */
-
 /* Socket-level I/O control calls. */
 #define FIOSETOWN 	0x8901
 #define SIOCSPGRP	0x8902
 #define FIOGETOWN	0x8903
 #define SIOCGPGRP	0x8904
 #define SIOCATMARK	0x8905
+#define SIOCGSTAMP	0x8096		/* Get stamp */
 
 /* Routing table calls. */
 #define SIOCADDRT	0x890B		/* add routing table entry	*/
@@ -63,10 +52,11 @@
 #define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
 #define SIOCGIFMTU	0x8921		/* get MTU size			*/
 #define SIOCSIFMTU	0x8922		/* set MTU size			*/
-#define	SIOCGIFHWADDR	0x8923		/* get hardware address		*/
+#define	OLD_SIOCGIFHWADDR	0x8923		/* get hardware address		*/
 #define	SIOCSIFHWADDR	0x8924		/* set hardware address (NI)	*/
 #define SIOCGIFENCAP	0x8925		/* get/set slip encapsulation   */
 #define SIOCSIFENCAP	0x8926		
+#define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
 
 /* Routing table calls (oldrtent - don't use) */
 #define SIOCADDRTOLD	0x8940		/* add routing table entry	*/
diff -u --recursive --new-file v1.1.3/linux/net/Space.c linux/net/Space.c
--- v1.1.3/linux/net/Space.c	Tue Dec 14 12:12:06 1993
+++ linux/net/Space.c	Wed Apr 13 12:52:20 1994
@@ -7,7 +7,11 @@
  * Version:	@(#)Space.c	1.0.2	04/22/93
  *
  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ *	Please see the comments in ddi.c - Alan
+ * 
  */
+ 
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -27,7 +31,7 @@
 #  include "unix/unix.h"
 #endif
 #ifdef	CONFIG_INET
-#  include "inet/inet.h"
+#  include <linux/inet.h>
 #endif
 #ifdef CONFIG_IPX
 #include "inet/ipxcall.h"
@@ -53,43 +57,3 @@
 };
 
 
-/*
- * Section B:	Device Driver Modules.
- *		This section defines which network device drivers
- *		get linked into the Linux kernel.  It is currently
- *		only used by the INET protocol.  Any takers for the
- *		other protocols like XNS or Novell?
- *
- * WARNING:	THIS SECTION IS NOT YET USED BY THE DRIVERS !!!!!
- */
-/*#include "drv/we8003/we8003.h"	Western Digital WD-80[01]3	*/
-/*#include "drv/dp8390/dp8390.h"	Donald Becker's DP8390 kit	*/
-/*#inclde "drv/slip/slip.h"		Laurence Culhane's SLIP kit	*/
-
-
-struct ddi_device devices[] = {
-#if CONF_WE8003
-  { "WD80x3[EBT]",
-	"",	0,	1,	we8003_init,	NULL,
-	19,	0,	DDI_FCHRDEV,
-    { 0x280,	0,	15,	0,	32768,	0xD0000		}	},
-#endif
-#if CONF_DP8390
-  { "DP8390/WD80x3",
-	"",	0,	1,	dpwd8003_init,	NULL,
-	20,	0,	DDI_FCHRDEV,
-    {	0,	0,	0,	0,	0,	0,		}	},
-  { "DP8390/NE-x000",
-	"",	0,	1,	dpne2000_init,	NULL,
-	20,	8,	DDI_FCHRDEV,
-    {	0,	0,	0,	0,	0,	0,		}	},
-  { "DP8390/3C50x",
-	"",	0,	1,	dpec503_init,	NULL,
-	20,	16,	DDI_FCHRDEV,
-    {	0,	0,	0,	0,	0,	0,		}	},
-#endif
-  { NULL,
-	"",	0,	0,	NULL,		NULL,
-	0,	0,	0,
-    {	0,	0,	0,	0,	0,	0		}	}
-};
diff -u --recursive --new-file v1.1.3/linux/net/ddi.c linux/net/ddi.c
--- v1.1.3/linux/net/ddi.c	Wed Dec  1 14:44:15 1993
+++ linux/net/ddi.c	Wed Apr 13 12:52:20 1994
@@ -7,6 +7,14 @@
  * Version:	@(#)ddi.c	1.0.5	04/22/93
  *
  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ *
+ *	For the moment I'm classifying DDI as 'dead'. However if/when Fred
+ *	produces a masterpiece of design DDI may get resurrected. With the
+ *	current kernel as modules work by Peter MacDonald they might be
+ *	redundant anyway. Thus I've removed all but the protocol initialise.
+ *
+ *	We will end up with protocol initialisers and socket family initialisers.
  */
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -28,39 +36,10 @@
 #endif
 
 
-extern struct ddi_device	devices[];	/* device driver map	*/
 extern struct ddi_proto		protocols[];	/* network protocols	*/
 
 
 /*
- * This function gets called with an ASCII string representing the
- * ID of some DDI driver.  We loop through the DDI Devices table
- * and return the address of the control block that has a matching
- * "name" field.  It is used by upper-level layers that want to
- * dynamically bind some UNIX-domain "/dev/XXXX" file name to a
- * DDI device driver.  The "iflink(8)" program is an example of
- * this behaviour.
- */
-struct ddi_device *
-ddi_map(const char *id)
-{
-  register struct ddi_device *dev;
-
-  PRINTK (("DDI: MAP: looking for \"%s\": ", id));
-  dev = devices;
-  while (dev->title != NULL) {
-	if (strncmp(dev->name, id, DDI_MAXNAME) == 0) {
-		PRINTK (("OK at 0x%X\n", dev));
-		return(dev);
-	}
-	dev++;
-  }
-  PRINTK (("NOT FOUND\n"));
-  return(NULL);
-}
-
-
-/*
  * This is the function that is called by a kernel routine during
  * system startup.  Its purpose is to walk trough the "devices"
  * table (defined above), and to call all moduled defined in it.
@@ -68,24 +47,16 @@
 void
 ddi_init(void)
 {
-  struct ddi_proto *pro;
-  struct ddi_device *dev;
+	struct ddi_proto *pro;
 
-  PRINTK (("DDI: Starting up!\n"));
+	PRINTK (("DDI: Starting up!\n"));
 
-  /* First off, kick all configured protocols. */
-  pro = protocols;
-  while (pro->name != NULL) {
-	(*pro->init)(pro);
-	pro++;
-  }
-  
-  /* Done.  Now kick all configured device drivers. */
-  dev = devices;
-  while (dev->title != NULL) {
-	(*dev->init)(dev);
-	dev++;
-  }
-
-  /* We're all done... */
+	/* Kick all configured protocols. */
+	pro = protocols;
+	while (pro->name != NULL) 
+	{
+		(*pro->init)(pro);
+		pro++;
+	}
+	/* We're all done... */
 }
diff -u --recursive --new-file v1.1.3/linux/net/inet/Makefile linux/net/inet/Makefile
--- v1.1.3/linux/net/inet/Makefile	Tue Dec 14 12:14:18 1993
+++ linux/net/inet/Makefile	Wed Apr 13 12:52:21 1994
@@ -15,10 +15,21 @@
 	$(CC) $(CFLAGS) -S -o $*.s $<
 
 
-OBJS	= sock.o utils.o route.o proc.o timer.o protocol.o loopback.o \
+OBJS	= sock.o utils.o route.o proc.o timer.o protocol.o \
 	  eth.o packet.o arp.o dev.o ip.o raw.o icmp.o tcp.o udp.o \
-	  datagram.o skbuff.o
-#	  ipx.o ax25.o ax25_in.o ax25_out.o ax25_subr.o ax25_timer.o
+	  datagram.o skbuff.o devinet.o
+
+ifdef CONFIG_AX25
+
+OBJS	:= $(OBJS) ax25.o ax25_in.o ax25_out.o ax25_subr.o ax25_timer.o
+
+endif
+
+ifdef CONFIG_IPX
+
+OBJS	:= $(OBJS) ipx.o
+
+endif
 
 ifdef CONFIG_INET
 
diff -u --recursive --new-file v1.1.3/linux/net/inet/README linux/net/inet/README
--- v1.1.3/linux/net/inet/README	Wed Dec  1 14:44:15 1993
+++ linux/net/inet/README	Wed Apr 13 12:52:21 1994
@@ -1,44 +1,92 @@
 
-NET2Debugged 1.24 README
-------------------------
+This is snapshot 010
 
-Major Changes
-
-o	PLIP driver sort of works
-o	UDP and RAW have been partially rewritten for speed
-o	Internals heavily cleaned up, and memory monitoring of network
-	memory is now done. (On shift-scroll-lock)
-o	ARP should now not generate garbage
-o	Using MSG_PEEK can't cause race conditions and crashes
-o	Support for bootp clients.
-o	Supports RFC931 TAP authd
-o	NFS problems with certain types of network configuration are
-	fixed.
-o	Doesn't forward packets for other subnet (can cause packet storms)
-o	TCP won't ack rst frames causing packet storms (especially with
-	Lan workplace for DOS).
-o	Numerous fixes for solidity
-o	Verify_area used properly.
-o	MSG_PEEK is faster again
-o	Minor TCP fixes. Hopefully no more TCP lockups (ha!)
-o	Donald's promiscuous mode. Go forth and write protocol analysers...
--------------------------------------------------------------------------
-NOTE:
-	Drivers for this stack set must be using alloc_skb() not just
-kmalloc. If you get millions of 'non sk_buff...' errors please check the
-driver you are using. All Donald's drivers know about this. If you have
-a problem driver replace all cases of
-	
-	.. =(struct sk_buff *)kmalloc(sizeof(struct sk_buff)+...
-
-With
-	..=alloc_skb(sizeof(struct sk_buff)+...
-
-And if it uses kfree_s on the packet change that to use kfree_skbmem().
-
--------------------------------------------------------------------------
-Bug fixes and improvements for this section of the code should be mailed to
-iiitac@pyr.swan.ac.uk.
-
-
-Alan
+Notes:
+ARP
+	As of snapshot 006, ARP should compile and work correctly
+	for any protocol that has the right build_header support.
+
+AX25	
+	This is an ALPHA release. It will not be a standard part
+	of the real release module. Please read the copyrights on
+	the AX.25 code carefully. When AX.25 is finished it will
+	be part of a seperatly available amateur radio add on. Also
+	please rememeber this is ALPHA code. It works well for a lot
+	of people but I know for a fact it is currently buggy.
+
+IPX
+	The IPX module in here is fairly complete, and certainly
+	usable for things. The IPX user code isn't yet very useful
+	(nobody has written a RIP/SAP daemon!).
+
+NetROM
+	I'm slowly doing bits of this code, but its not even fit
+	to include here.
+
+
+Status:
+
+Done:
+	Replaced ARP with Florian la Roche's ARP.
+	Replaced/improved sk_buff handlers (again from Florian)
+	Removed surplus DDI code.
+	Reformatted most modules.
+	Fixed ICMP handling bugs (ICMP error to ICMP error).
+	Fixed fragmentation bugs (both memory and mtu).
+	Moved some includes.
+	Drivers now build correctly with no IP layer.
+	Merged Linus 1.0.1 diffs and my patches 1-3.
+	Further fixups on clean driver build.
+	Loopback driver now lives where it belongs.
+	UDP verified against specification (passes).
+	IP verified against specification (two errors: Incorrect forwarding and
+		no mandatory option handling).
+	ARP verified against specification (passes: recommendation that ARP
+		rejects MAC broadcast/multicast addresses - this needs
+		driver changes doing).
+	All surplus skb->sk assignment and skb->mem_len skb->mem_addr removed.
+	eth.h became linux/etherdevice.h.
+	alloc_skb nows adds the sizeof(struct sk_buff) itself.
+	Now relative to Linux 1.0.4.
+	All IP wakeups are now callbacks.
+	IPX and AX.25 callbacks now use wake_up_interruptible correctly.
+	ICMP,IP and UDP collect snmp statistics.
+	Removed the 4K limit from the /proc/net/* files.
+	Routing bugs.
+	Cleaned up skb duplication.
+	IPX /proc from Mark Evans.
+	Driver packet ordering now enforced.
+	AX.25 unused SSID bits now set.
+
+In Progress:
+	Module by module validation against specifications.
+	TCP delayed ACK	 [RFC1122 requires this].
+	Byte-order fixes.
+	Core code restructure to enable a working non IP build
+	Trying to fix /proc to do >4K correctly, as well as dynamic addition
+ 		of /proc/ and /proc/net/ objects (for module protocol layers).
+	Adding the extra NET2E driver ioctl() support.
+	SNMP MIB statistic capture - finish TCP and add device layer when 
+		Donald is ready.
+	Donald Beckers latest driver mods.
+	Adding the ICMP_TIMESTAMP support patch.
+	Routing bugs.
+	Packet level time stamping.
+	Merging in support for the I^2IT 'TICK' time synchronisation chip.
+	Crynwyr compliant PLIP driver.
+
+To Do:
+	Merge in sk_buff data handling module.
+	Socket family/protocol seperation.
+	Additional BSD options (SO_LOWAT etc).
+	IP option handling, especially on ip forwards.
+	AX.25 /proc support.
+	SNMP /proc support.
+	TCP MSS/Window and route metrics in the routing table.
+	TCP mtu discovery support.
+	NetROM.
+	TCP closing side state machine bug fixes.
+	NetBEUI (Lan Manager) [ie IEE802.3/IEE802.2/NetBIOS].
+	Make drivers record type and addressing category(Multicast/Broadcast..)
+	Speed it up.
+	Unix domain cleanup/rewrite.
diff -u --recursive --new-file v1.1.3/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.3/linux/net/inet/arp.c	Wed Mar  2 10:03:40 1994
+++ linux/net/inet/arp.c	Wed Apr 13 12:52:21 1994
@@ -1,59 +1,32 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		This file implements the Address Resolution Protocol (ARP),
- *		which is used by TCP/IP to map the IP addresses from a host
- *		to a low-level hardware address (like an Ethernet address)
- *		which it can use to talk to that host.
+/* linux/net/inet/arp.c
  *
- * NOTE:	This module will be rewritten completely in the near future,
- *		because I want it to become a multi-address-family address
- *		resolver, like it should be.  It will be put in a separate
- *		directory under 'net', being a protocol of its own. -FvK
+ * Copyright (C) 1994 by Florian  La Roche
  *
- * Version:	@(#)arp.c	1.0.15	05/25/93
+ * This module implements the Address Resolution Protocol ARP (RFC 826),
+ * which is used to convert IP addresses (or in the future maybe other
+ * high-level addresses into a low-level hardware address (like an Ethernet
+ * address).
  *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *		Arnt Gulbrandsen, <agulbra@pvv.unit.no>
+ * FIXME:
+ *	Experiment with better retransmit timers
+ *	Clean up the timer deletions
+ *	If you create a proxy entry set your interface address to the address
+ *	and then delete it, proxies may get out of sync with reality - check this
  *
- * Fixes:
- *		Stephen A. Wood	:	arp problems
- *		'Mr Linux'	:	arp problems.
- *		Alan Cox	:	arp_ioctl now checks memory areas with verify_area.
- *		Alan Cox	:	Non IP arp message now only appears with debugging on.
- *		Alan Cox	: 	arp queue is volatile (may be altered by arp messages while doing sends) 
- *					Generic queue code is urgently needed!
- *		Alan Cox	:	Deleting your own ip addr now gives EINVAL not a printk message.
- *		Alan Cox	:	Fix to arp linked list error
- *		Alan Cox	:	Ignore broadcast arp (Linus' idea 8-))
- *		Alan Cox	:	arp_send memory leak removed
- *		Alan Cox	:	generic skbuff code fixes.
- *		Alan Cox	:	'Bad Packet' only reported on debugging
- *		Alan Cox	:	Proxy arp.
- *		Alan Cox	:	skb->link3 maintained by letting the other xmit queue kill the packet.
- *		Alan Cox	:	Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet
- *					one.
- *		Dominik Kubla	:	Better checking
- *		Tegge		:	Assorted corrections on cross port stuff
- *		Alan Cox	:	ATF_PERM was backwards! - might be useful now (sigh)
- *		Alan Cox	:	Arp timer added.
+ * 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.
  *
- * To Fix:
- *				:	arp response allocates an skbuff to send. However there is a perfectly
- *					good spare skbuff the right size about to be freed (the query). Use the
- *					query for the reply. This avoids an out of memory case _and_ speeds arp
- *					up.
- *				:	FREE_READ v FREE_WRITE errors. Not critical as loopback arps don't occur
  *
+ * Fixes:
+ *		Alan Cox	:	Removed the ethernet assumptions in Florians code
+ *		Alan Cox	:	Fixed some small errors in the ARP logic
+ *		Alan Cox	:	Allow >4K in /proc
+ *		Alan Cox	:	Make ARP add its own protocol entry
  *
- *		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/types.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
@@ -61,917 +34,999 @@
 #include <linux/config.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
-#include <linux/timer.h>
 #include <linux/errno.h>
 #include <linux/if_arp.h>
 #include <linux/in.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <stdarg.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include "ip.h"
 #include "route.h"
 #include "protocol.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "arp.h"
-
+#ifdef CONFIG_AX25
+#include "ax25.h"
+#endif
 
-#define ARP_MAX_TRIES	3
-
+/*
+ *	This structure defines the ARP mapping cache. As long as we make changes
+ *	in this structure, we keep interrupts of. But normally we can copy the
+ *	hardware address and the device pointer in a local variable and then make
+ *	any "long calls" to send a packet out. 
+ */
+ 
+struct arp_table 
+{
+	struct arp_table		*next;			/* Linked entry list 		*/
+	unsigned long			last_used;		/* For expiry 			*/
+	unsigned int			flags;			/* Control status 		*/
+	unsigned long			ip;			/* ip address of entry 		*/
+	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
+	unsigned char			hlen;			/* Length of hardware address 	*/
+	unsigned char			htype;			/* Type of hardware in use	*/
+	struct device			*dev;			/* Device the entry is tied to 	*/
 
-static char *unk_print(unsigned char *, int);
-static char *eth_aprint(unsigned char *, int);
+	/*
+	 *	The following entries are only used for unresolved hw addresses. 
+	 */
+	 
+	struct timer_list		timer;			/* expire timer 		*/	
+	int				retries;		/* remaining retries	 	*/
+	struct sk_buff_head		skb;			/* list of queued packets 	*/
+};
 
+/* 
+ *	This structure defines an ethernet arp header. 
+ */
+ 
+struct arphdr 
+{
+	unsigned short	ar_hrd;		/* format of hardware address	*/
+	unsigned short	ar_pro;		/* format of protocol address	*/
+	unsigned char	ar_hln;		/* length of hardware address	*/
+	unsigned char	ar_pln;		/* length of protocol address	*/
+	unsigned short	ar_op;		/* ARP opcode (command)		*/
+
+#if 0
+	 /*
+	  *	 Ethernet looks like this : This bit is variable sized however...
+	  */
+	unsigned char		ar_sha[ETH_ALEN];	/* sender hardware address	*/
+	unsigned char		ar_sip[4];		/* sender IP address		*/
+	unsigned char		ar_tha[ETH_ALEN];	/* target hardware address	*/
+	unsigned char		ar_tip[4];		/* target IP address		*/
+#endif
 
-static char *arp_cmds[] = {
-  "0x%04X",
-  "REQUEST",
-  "REPLY",
-  "REVERSE REQUEST",
-  "REVERSE REPLY",
-  NULL
 };
-#define	ARP_MAX_CMDS	(sizeof(arp_cmds) / sizeof(arp_cmds[0]))
 
-static struct {
-  char	*name;
-  char	*(*print)(unsigned char *ptr, int len);
-} arp_types[] = {
-  { "0x%04X",			unk_print	},
-  { "10 Mbps Ethernet", 	eth_aprint	},
-  { "3 Mbps Ethernet",		eth_aprint	},
-  { "AX.25",			unk_print	},
-  { "Pronet",			unk_print	},
-  { "Chaos",			unk_print	},
-  { "IEEE 802.2 Ethernet (?)",	eth_aprint	},
-  { "Arcnet",			unk_print	},
-  { "AppleTalk",		unk_print	},
-  { NULL,			NULL		}
-};
-#define	ARP_MAX_TYPE	(sizeof(arp_types) / sizeof(arp_types[0]))
+/*
+ *	Configurable Parameters (don't touch unless you know what you are doing
+ */
+ 
+/*
+ *	If an arp request is send, ARP_RES_TIME is the timeout value until the
+ *	next request is send. 
+ */
+ 
+#define ARP_RES_TIME		(250*(HZ/10))
 
+/*
+ *	The number of times an arp request is send, until the host is
+ *	considered unreachable. 
+ */
+ 
+#define ARP_MAX_TRIES		3
 
-struct arp_table *arp_tables[ARP_TABLE_SIZE] = {
-  NULL,
-};
+/*
+ *	After that time, an unused entry is deleted from the arp table. 
+ */
+ 
+#define ARP_TIMEOUT		(600*HZ)
 
-static int arp_proxies=0;	/* So we can avoid the proxy arp 
-				   overhead with the usual case of
-				   no proxy arps */
+/*
+ *	How often is the function 'arp_check_retries' called.
+ *	An entry is invalidated in the time between ARP_TIMEOUT and
+ *	(ARP_TIMEOUT+ARP_CHECK_INTERVAL). 
+ */
 
-struct sk_buff * volatile arp_q = NULL;
+#define ARP_CHECK_INTERVAL	(60 * HZ)
 
-static struct arp_table *arp_lookup(unsigned long addr);
-static struct arp_table *arp_lookup_proxy(unsigned long addr);
 
-/* Dump the ADDRESS bytes of an unknown hardware type. */
-static char *
-unk_print(unsigned char *ptr, int len)
-{
-  static char buff[32];
-  char *bufp = buff;
-  int i;
+static void arp_check_expire (unsigned long);  /* Forward declaration. */
 
-  for (i = 0; i < len; i++)
-	bufp += sprintf(bufp, "%02X ", (*ptr++ & 0377));
-  return(buff);
-}
 
+static struct timer_list arp_timer =
+	{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
 
-/* Dump the ADDRESS bytes of an Ethernet hardware type. */
-static char *
-eth_aprint(unsigned char *ptr, int len)
-{
-  if (len != ETH_ALEN) return("");
-  return(eth_print(ptr));
-}
+/*
+ * 	The size of the hash table. Must be a power of two.
+ * 	Maybe we should remove hashing in the future for arp and concentrate
+ * 	on Patrick Schaaf's Host-Cache-Lookup... 
+ */
 
+#define ARP_TABLE_SIZE  16
 
-/* Dump an ARP packet. Not complete yet for non-Ethernet packets. */
-static void
-arp_print(struct arphdr *arp)
+struct arp_table *arp_tables[ARP_TABLE_SIZE] = 
 {
-  int len, idx;
-  unsigned char *ptr;
-
-  if (inet_debug != DBG_ARP) return;
-
-  printk("ARP: ");
-  if (arp == NULL) {
-	printk("(null)\n");
-	return;
-  }
+	NULL,
+};
 
-  /* Print the opcode name. */
-  len = htons(arp->ar_op);
-  if (len < ARP_MAX_CMDS) idx = len;
-    else idx = 0;
-  printk("op ");
-  printk(arp_cmds[idx], len);
-
-  /* Print the ARP header. */
-  len = htons(arp->ar_hrd);
-  if (len < ARP_MAX_TYPE) idx = len;
-    else idx = 0;
-  printk("   hrd = "); printk(arp_types[idx].name, len);
-  printk("   pro = 0x%04X\n", htons(arp->ar_pro));
-  printk("   hlen = %d plen = %d\n", arp->ar_hln, arp->ar_pln);
-
-  /*
-   * Print the variable data.
-   * When ARP gets redone (after the formal introduction of NET-2),
-   * this part will be redone.  ARP will then be a multi-family address
-   * resolver, and the code below will be made more general. -FvK
-   */
-  ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
-  printk("   sender HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
-  ptr += arp->ar_hln;
-  printk("  PA = %s\n", in_ntoa(*(unsigned long *) ptr));
-  ptr += arp->ar_pln;
-  printk("   target HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
-  ptr += arp->ar_hln;
-  printk("  PA = %s\n", in_ntoa(*(unsigned long *) ptr));
-}
+/*
+ *	The last bits in the IP address are used for the cache lookup. 
+ */
+ 
+#define HASH(paddr) 		(htonl(paddr) & (ARP_TABLE_SIZE - 1))
 
+/*
+ *	Number of proxy arp entries. This is normally zero and we use it to do
+ *	some optimizing for normal uses
+ */
+ 
+static int proxies = 0;
 
-/* This will try to retransmit everything on the queue. */
-static void
-arp_send_q(void)
-{
-  struct sk_buff *skb;
-  struct sk_buff *volatile work_q;
-  cli();
-  work_q = arp_q;
-  skb_new_list_head(&work_q);
-  arp_q = NULL;
-  sti();
-  while((skb=skb_dequeue(&work_q))!=NULL)
-  {
-  	IS_SKB(skb);
-	skb->magic = 0;
-	skb->next = NULL;
-	skb->prev = NULL;
 
-	/* Decrement the 'tries' counter. */
+/*
+ *	Check if there are too old entries and remove them. If the ATF_PERM
+ *	flag is set, they are always left in the arp cache (permanent entry).
+ *	Note: Only fully resolved entries, which don't have any packets in
+ *	the queue, can be deleted, since ARP_TIMEOUT is much greater than
+ *	ARP_MAX_TRIES*ARP_RES_TIME. 
+ */
+ 
+static void arp_check_expire(unsigned long dummy)
+{
+	int i;
+	unsigned long now = jiffies;
+	unsigned long flags;
+	save_flags(flags);
 	cli();
-	skb->tries--;
-	if (skb->tries == 0) {
-		/*
-		 * Grmpf.
-		 * We have tried ARP_MAX_TRIES to resolve the IP address
-		 * from this datagram.  This means that the machine does
-		 * not listen to our ARP requests.  Perhaps someone tur-
-		 * ned off the thing?
-		 * In any case, trying further is useless.  So, we kill
-		 * this packet from the queue.  (grinnik) -FvK
-		 */
-		skb->sk = NULL;
-		if(skb->free)
-			kfree_skb(skb, FREE_WRITE);
-			/* If free was 0, magic is now 0, next is 0 and 
-			   the write queue will notice and kill */
-		sti();
-		continue;
-	}
-
-	/* Can we now complete this packet? */
-	sti();
-	if (skb->arp || !skb->dev->rebuild_header(skb->data, skb->dev)) {
-		skb->arp  = 1;
-		skb->dev->queue_xmit(skb, skb->dev, 0);
-	} else {
-		/* Alas.  Re-queue it... */
-		skb->magic = ARP_QUEUE_MAGIC;      
-		skb_queue_head(&arp_q,skb);
+	
+	for (i = 0; i < ARP_TABLE_SIZE; i++) 
+	{
+		struct arp_table *entry;
+		struct arp_table **pentry = &arp_tables[i];
+
+		while ((entry = *pentry) != NULL) 
+		{
+			if ((now - entry->last_used) > ARP_TIMEOUT
+				&& !(entry->flags & ATF_PERM)) 
+			{
+				*pentry = entry->next;	/* remove from list */
+				if (entry->flags & ATF_PUBL)
+					proxies--;
+				del_timer(&entry->timer);	/* Paranoia */ 			
+				kfree_s(entry, sizeof(struct arp_table));
+			} 
+			else 
+				pentry = &entry->next;	/* go to next entry */
+		}
 	}
-  }
-}
-
-
-static struct timer_list arp_timer;
+	restore_flags(flags);
 
-static void arp_queue_ticker(unsigned long data);
+	/*
+	 *	Set the timer again. 
+	 */
 
-static void arp_queue_kick(void)
-{
-	arp_timer.expires = 500;	/* 5 seconds */
-	arp_timer.data = 0;
-	arp_timer.function = arp_queue_ticker;
 	del_timer(&arp_timer);
+	arp_timer.expires = ARP_CHECK_INTERVAL;	
 	add_timer(&arp_timer);
 }
 
-static void arp_queue_ticker(unsigned long data/*UNUSED*/)
+
+/*
+ *	Release all linked skb's and the memory for this entry. 
+ */
+ 
+static void arp_release_entry(struct arp_table *entry)
 {
-	arp_send_q();
-	if (skb_peek(&arp_q))
-		arp_queue_kick();
-}
-
-
-
-/* Create and send our response to an ARP request. */
-static int
-arp_response(struct arphdr *arp1, struct device *dev,  int addrtype)
-{
-  struct arphdr *arp2;
-  struct sk_buff *skb;
-  unsigned long src, dst;
-  unsigned char *ptr1, *ptr2;
-  int hlen;
-  struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */
-
-  /* Decode the source (REQUEST) message. */
-  ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short);
-  src = *((unsigned long *) (ptr1 + arp1->ar_hln));
-  dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln));
-  
-  if(addrtype!=IS_MYADDR)
-  {
-  	apt=arp_lookup_proxy(dst);
-  	if(apt==NULL)
-  		return(1);
-  }
-
-  /* Get some mem and initialize it for the return trip. */
-  skb = alloc_skb(sizeof(struct sk_buff) +
-  		sizeof(struct arphdr) +
-		(2 * arp1->ar_hln) + (2 * arp1->ar_pln) +
-		dev->hard_header_len, GFP_ATOMIC);
-  if (skb == NULL) {
-	printk("ARP: no memory available for ARP REPLY!\n");
-	return(1);
-  }
-
-  skb->mem_addr = skb;
-  skb->len      = sizeof(struct arphdr) + (2 * arp1->ar_hln) + 
-		  (2 * arp1->ar_pln) + dev->hard_header_len;
-  skb->mem_len  = sizeof(struct sk_buff) + skb->len;
-  hlen = dev->hard_header(skb->data, dev, ETH_P_ARP, src, dst, skb->len);
-  if (hlen < 0) {
-	printk("ARP: cannot create HW frame header for REPLY !\n");
-	kfree_skb(skb, FREE_WRITE);
-	return(1);
-  }
-
-  /*
-   * Fill in the ARP REPLY packet.
-   * This looks ugly, but we have to deal with the variable-length
-   * ARP packets and such.  It is not as bad as it looks- FvK
-   */
-  arp2 = (struct arphdr *) (skb->data + hlen);
-  ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short);
-  arp2->ar_hrd = arp1->ar_hrd;
-  arp2->ar_pro = arp1->ar_pro;
-  arp2->ar_hln = arp1->ar_hln;
-  arp2->ar_pln = arp1->ar_pln;
-  arp2->ar_op = htons(ARPOP_REPLY);
-  if(addrtype==IS_MYADDR)
-	  memcpy(ptr2, dev->dev_addr, arp2->ar_hln);
-  else		/* Proxy arp, so pull from the table */
-  	  memcpy(ptr2, apt->ha, arp2->ar_hln);
-  ptr2 += arp2->ar_hln;
-  memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln);
-  ptr2 += arp2->ar_pln;
-  memcpy(ptr2, ptr1, arp2->ar_hln);
-  ptr2 += arp2->ar_hln;
-  memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln);
-
-  skb->free = 1;
-  skb->arp = 1;
-  skb->sk = NULL;
-  skb->next = NULL;
-
-  DPRINTF((DBG_ARP, ">>"));
-  arp_print(arp2);
-
-  /* Queue the packet for transmission. */
-  dev->queue_xmit(skb, dev, 0);
-  return(0);
-}
-
-
-/* This will find an entry in the ARP table by looking at the IP address. */
-static struct arp_table *
-arp_lookup(unsigned long paddr)
-{
-  struct arp_table *apt;
-  unsigned long hash;
-
-  DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr)));
-
-  /* We don't want to ARP ourselves. */
-  if (chk_addr(paddr) == IS_MYADDR) {
-	printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr));
-	return(NULL);
-  }
-
-  /* Loop through the table for the desired address. */
-  hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
-  cli();
-  apt = arp_tables[hash];
-  while(apt != NULL) {
-	if (apt->ip == paddr) {
-		sti();
-		return(apt);
+	struct sk_buff *skb;
+
+	if (entry->flags & ATF_PUBL)
+		proxies--;
+	/* Release the list of `skb' pointers. */
+	while ((skb = skb_dequeue(&entry->skb)) != NULL) 
+	{
+		if (skb->free)
+			kfree_skb(skb, FREE_WRITE);
 	}
-	apt = apt->next;
-  }
-  sti();
-  return(NULL);
+	del_timer(&entry->timer);
+	kfree_s(entry, sizeof(struct arp_table));
+	return;
 }
 
 
-/* This will find a proxy in the ARP table by looking at the IP address. */
-static struct arp_table *arp_lookup_proxy(unsigned long paddr)
-{
-  struct arp_table *apt;
-  unsigned long hash;
-
-  DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr)));
+/*
+ *	Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
+ *	message. 
+ */
+ 
+static void arp_send(int type, unsigned long dest_ip, struct device *dev,
+	unsigned long src_ip, unsigned char *dest_hw, unsigned char *src_hw)
+{
+	struct sk_buff *skb;
+	struct arphdr *arp;
+	unsigned char *arp_ptr;
 
-  /* Loop through the table for the desired address. */
-  hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
-  cli();
-  apt = arp_tables[hash];
-  while(apt != NULL) {
-	if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) {
-		sti();
-		return(apt);
+	/*
+	 *	No arp on this interface.
+	 */
+	 	
+	if(dev->flags&IFF_NOARP)
+		return;
+		
+	/*
+	 *	Allocate a buffer
+	 */
+	 
+	skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+				+ dev->hard_header_len, GFP_ATOMIC);
+	if (skb == NULL) 
+	{
+		printk("ARP: no memory to send an arp packet\n");
+		return;
 	}
-	apt = apt->next;
-  }
-  sti();
-  return(NULL);
-}
+	skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4);
+	skb->arp = 1;
+	skb->dev = dev;
+	skb->free = 1;
 
+	/*
+	 *	Fill the device header for the ARP frame 
+	 */
 
-/* Delete an ARP mapping entry in the cache. */
-void
-arp_destructor(unsigned long paddr, int force)
-{
-  struct arp_table *apt;
-  struct arp_table **lapt;
-  unsigned long hash;
+	dev->hard_header(skb->data,dev,ETH_P_ARP,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb);
 
-  DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr)));
+	/* Fill out the arp protocol part. */
+	arp = (struct arphdr *) (skb->data + dev->hard_header_len);
+	arp->ar_hrd = htons(dev->type);
+#ifdef CONFIG_AX25	
+	arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP);
+#else
+	arp->ar_pro = htons(ETH_P_IP);
+#endif	
+	arp->ar_hln = dev->addr_len;
+	arp->ar_pln = 4;
+	arp->ar_op = htons(type);
+
+	arp_ptr=(unsigned char *)(arp+1);
+	
+	memcpy(arp_ptr, src_hw, dev->addr_len);
+	arp_ptr+=dev->addr_len;
+	memcpy(arp_ptr, &src_ip,4);
+	arp_ptr+=4;
+	if (dest_hw != NULL)
+		memcpy(arp_ptr, dest_hw, dev->addr_len);
+	else
+		memset(arp_ptr, 0, dev->addr_len);
+	arp_ptr+=dev->addr_len;
+	memcpy(arp_ptr, &dest_ip, 4);
+	
+	dev_queue_xmit(skb, dev, 0);
+}
 
-  /* We cannot destroy our own ARP entry. */
-  if (chk_addr(paddr) == IS_MYADDR) {
-	DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n",
-							in_ntoa(paddr)));
-	return;
-  }
-  hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
 
-  cli();
-  lapt = &arp_tables[hash];
-  while ((apt = *lapt) != NULL) {
-	if (apt->ip == paddr) {
-		if((apt->flags&ATF_PERM) && !force)
-			return;
-		*lapt = apt->next;
-		if(apt->flags&ATF_PUBL)
-			arp_proxies--;			
-		kfree_s(apt, sizeof(struct arp_table));
-		sti();
+/*
+ *	This function is called, if an entry is not resolved in ARP_RES_TIME.
+ *	Either resend a request, or give it up and free the entry. 
+ */
+ 
+static void arp_expire_request (unsigned long arg)
+{
+	struct arp_table *entry = (struct arp_table *) arg;
+	struct arp_table **pentry;
+	unsigned long hash;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	
+	/*
+	 *	Since all timeouts are handled with interrupts enabled, there is a
+	 *	small chance, that this entry has just been resolved by an incoming
+	 *	packet. This is the only race condition, but it is handled... 
+	 */
+	 
+	if (entry->flags & ATF_COM) 
+	{
+		restore_flags(flags);
+		return;
+	}
+	
+	if (--entry->retries > 0) 
+	{
+		unsigned long ip = entry->ip;
+		struct device *dev = entry->dev;
+
+		/* Set new timer. */
+		del_timer(&entry->timer);
+		entry->timer.expires = ARP_RES_TIME;
+		add_timer(&entry->timer);
+		restore_flags(flags);
+		arp_send(ARPOP_REQUEST, ip, dev, dev->pa_addr, NULL,
+				dev->dev_addr);
 		return;
 	}
-	lapt = &apt->next;
-  }
-  sti();
+	
+	/*
+	 *	Arp request timed out. Delete entry and all waiting packets.
+	 *	If we give each entry a pointer to itself, we don't have to
+	 *	loop through everything again. Maybe hash is good enough, but
+	 *	I will look at it later. 
+	 */
+	 
+	hash = HASH(entry->ip);
+	pentry = &arp_tables[hash];
+	while (*pentry != NULL) 
+	{
+		if (*pentry == entry) 
+		{
+			*pentry = entry->next;	/* delete from linked list */
+			del_timer(&entry->timer);
+			restore_flags(flags);
+			arp_release_entry(entry);
+			return;
+		}
+		pentry = &(*pentry)->next;
+	}
+	restore_flags(flags);
+	printk("Possible ARP queue corruption.\n");
+	/*
+	 *	We should never arrive here. 
+	 */
 }
 
-/*
- *	Kill an entry - eg for ioctl()
- */
-
-void arp_destroy(unsigned long paddr)
-{	
-	arp_destructor(paddr,1);
-}
 
 /*
- *	Delete a possibly invalid entry (see timer.c)
+ *	This will try to retransmit everything on the queue. 
  */
-
-void arp_destroy_maybe(unsigned long paddr)
+ 
+static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest)
 {
-	arp_destructor(paddr,0);
+	struct sk_buff *skb;
+	
+	
+	/*
+	 *	Empty the entire queue, building its data up ready to send
+	 */
+	 
+	if(!(entry->flags&ATF_COM))
+	{
+		printk("arp_send_q: incomplete entry for %s\n",
+				in_ntoa(entry->ip));
+		return;
+	}
+	
+	while((skb = skb_dequeue(&entry->skb)) != NULL) 
+	{
+		IS_SKB(skb);
+		if(!skb->dev->rebuild_header(skb->data,skb->dev,skb->raddr,skb))
+		{
+			skb->arp  = 1;		
+			if(skb->sk==NULL)
+				dev_queue_xmit(skb, skb->dev, 0);
+			else
+				dev_queue_xmit(skb,skb->dev,skb->sk->priority);
+		}
+		else
+		{
+			/* This routine is only ever called when 'entry' is
+			   complete. Thus this can't fail (but does) */
+			printk("arp_send_q: The impossible occurred. Please notify Alan.\n");
+			printk("arp_send_q: active entity %s\n",in_ntoa(entry->ip));
+			printk("arp_send_q: failed to find %s\n",in_ntoa(skb->raddr));
+		}
+	}
 }
 
-/* Create an ARP entry.  The caller should check for duplicates! */
-static struct arp_table *
-arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype)
-{
-  struct arp_table *apt;
-  unsigned long hash;
-
-  DPRINTF((DBG_ARP, "ARP: create(%s, ", in_ntoa(paddr)));
-  DPRINTF((DBG_ARP, "%s, ", eth_print(addr)));
-  DPRINTF((DBG_ARP, "%d, %d)\n", hlen, htype));
 
-  apt = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC);
-  if (apt == NULL) {
-	printk("ARP: no memory available for new ARP entry!\n");
-	return(NULL);
-  }
+/*
+ *	Delete an ARP mapping entry in the cache. 
+ */
+ 
+void arp_destroy(unsigned long ip_addr, int force)
+{
+	struct arp_table *entry;
+	struct arp_table **pentry;
+	unsigned long hash = HASH(ip_addr);
 
-  /* Fill in the allocated ARP cache entry. */
-  hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
-  apt->ip = paddr;
-  apt->hlen = hlen;
-  apt->htype = htype;
-  apt->flags = (ATF_INUSE | ATF_COM);		/* USED and COMPLETED entry */
-  memcpy(apt->ha, addr, hlen);
-  apt->last_used = jiffies;
-  cli();
-  apt->next = arp_tables[hash];
-  arp_tables[hash] = apt;
-  sti();
-  return(apt);
+	cli();
+	pentry = &arp_tables[hash];
+	while ((entry = *pentry) != NULL) 
+	{
+		if (entry->ip == ip_addr) 
+		{
+			if ((entry->flags & ATF_PERM) && !force)
+				return;
+			*pentry = entry->next;
+			sti();
+			del_timer(&entry->timer);
+			arp_release_entry(entry);
+			return;
+		}
+		pentry = &entry->next;
+	}
+	sti();
 }
 
 
-/*
- * An ARP REQUEST packet has arrived.
- * We try to be smart here, and fetch the data of the sender of the
- * packet- we might need it later, so fetching it now can save us a
- * broadcast later.
- * Then, if the packet was meant for us (i.e. the TARGET address was
- * one of our own IP addresses), we set up and send out an ARP REPLY
- * packet to the sender.
+/* 
+ *	Receive an arp request by the device layer. Maybe I rewrite it, to
+ *	use the incoming packet for the reply. The time for the current
+ *	"overhead" isn't that high... 
  */
-int
-arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+ 
+int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
-  struct arphdr *arp;
-  struct arp_table *tbl;
-  unsigned long src, dst;
-  unsigned char *ptr;
-  int ret;
-  int addr_hint;
+	/*
+	 *	We shouldn't use this type conversion. Check later. 
+	 */
+	 
+	struct arphdr *arp = (struct arphdr *)skb->h.raw;
+	unsigned char *arp_ptr= (unsigned char *)(arp+1);
+	struct arp_table *entry;
+	struct arp_table *proxy_entry;
+	int addr_hint;
+	unsigned long hash;
+	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
+	long sip,tip;
+	unsigned char *sha,*tha;
 
-  DPRINTF((DBG_ARP, "<<\n"));
-  arp = skb->h.arp;
-  arp_print(arp);
+	/*
+	 *	If this test doesn't pass, its not IP, or we should ignore it anyway
+	 */
+	 
+	if (arp->ar_hln != dev->addr_len || dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP) 
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
 
-  /* If this test doesn't pass, its not IP. Might be DECNET or friends */
-  if (arp->ar_hln != dev->addr_len || dev->type != NET16(arp->ar_hrd)) 
-  {
-	DPRINTF((DBG_ARP,"ARP: Bad packet received on device \"%s\" !\n", dev->name));
-	kfree_skb(skb, FREE_READ);
-	return(0);
-  }
+	/*
+	 *	For now we will only deal with IP addresses. 
+	 */
+	if (
+#ifdef CONFIG_AX25	
+		(arp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif		
+		(arp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
+		|| arp->ar_pln != 4) 
+	{
+		/* This packet is not for us. Remove it. */
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
 
-  /* For now we will only deal with IP addresses. */
-  if (((arp->ar_pro != NET16(0x00CC) && dev->type==3) || (arp->ar_pro != NET16(ETH_P_IP) && dev->type!=3) ) || arp->ar_pln != 4) 
-  {
-	if (arp->ar_op != NET16(ARPOP_REQUEST))
-		DPRINTF((DBG_ARP,"ARP: Non-IP request on device \"%s\" !\n", dev->name));
-	kfree_skb(skb, FREE_READ);
-	return(0);
-  }
+	/*
+	 *	Extract variable width fields
+	 */
 
-  /*
-   * As said before, we try to be smart by using the
-   * info already present in the packet: the sender's
-   * IP and hardware address.
-   */
-  ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
-  memcpy(&src, ptr + arp->ar_hln, arp->ar_pln);
-  tbl = arp_lookup(src);
-  if (tbl != NULL) {
-	DPRINTF((DBG_ARP, "ARP: udating entry for %s\n", in_ntoa(src)));
-	memcpy(tbl->ha, ptr, arp->ar_hln);
-	tbl->hlen = arp->ar_hln;
-	tbl->flags |= ATF_COM;
-	tbl->last_used = jiffies;
-  } else {
-	memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
-	if (chk_addr(dst) != IS_MYADDR && arp_proxies == 0) {
-		kfree_skb(skb, FREE_READ);
-		return(0);
-	} else {
-		tbl = arp_create(src, ptr, arp->ar_hln, arp->ar_hrd);
-		if (tbl == NULL) {
+	sha=arp_ptr;
+	arp_ptr+=dev->addr_len;
+	memcpy(&sip,arp_ptr,4);
+	arp_ptr+=4;
+	tha=arp_ptr;
+	arp_ptr+=dev->addr_len;
+	memcpy(&tip,arp_ptr,4);
+	
+	/*
+	 *	Process entry
+	 */
+	
+	addr_hint = ip_chk_addr(tip);
+
+	hash = HASH(sip);
+	proxy_entry = NULL;
+	if (proxies != 0 && addr_hint != IS_MYADDR) 
+	{
+		unsigned long dest_hash = HASH(tip);
+		cli();
+		proxy_entry = arp_tables[dest_hash];
+		while (proxy_entry != NULL) 
+		{
+			if (proxy_entry->ip == tip && proxy_entry->htype==arp->ar_hrd)
+				break;
+			proxy_entry = proxy_entry->next;
+		}
+		if (proxy_entry && (proxy_entry->flags & ATF_PUBL))
+			memcpy(ha, proxy_entry->ha, dev->addr_len);
+		else
+			proxy_entry = NULL;
+	} 
+	else 
+		cli();
+
+	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+		if (entry->ip == sip) 
+			break;
+			
+	if (entry != NULL) 
+	{
+		int old_flags = entry->flags;
+		memcpy(entry->ha, sha, arp->ar_hln);
+		entry->hlen = arp->ar_hln;
+		/* This seems sensible but not everyone gets it right ! */
+		entry->htype = ntohs(arp->ar_hrd);
+		if(entry->htype==0)
+			entry->htype = dev->type;	/* Not good but we have no choice */
+		entry->last_used = jiffies;
+		if (!(entry->flags & ATF_COM)) 
+		{
+			del_timer(&entry->timer);
+			entry->flags |= ATF_COM;
+		}
+		sti();
+		if (!(old_flags & ATF_COM)) 
+		{
+			/* Send out waiting packets. We might have problems,
+			   if someone is manually removing entries right now.
+			   I will fix this one. */
+			arp_send_q(entry, sha);
+		}
+		if (addr_hint != IS_MYADDR && proxy_entry == NULL) 
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+	} 
+	else 
+	{
+		if (addr_hint != IS_MYADDR && proxy_entry == NULL) 
+		{
+			/* We don't do "smart arp" and cache all possible
+			   entries. That just makes us more work. */
+			sti();
 			kfree_skb(skb, FREE_READ);
-			return(0);
+			return 0;
 		}
+		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),
+					GFP_ATOMIC);
+		if (entry == NULL) 
+		{
+			sti();
+			kfree_skb(skb, FREE_READ);
+			printk("ARP: no memory for new arp entry\n");
+			return 0;
+		}
+		entry->ip = sip;
+		entry->hlen = arp->ar_hln;
+		entry->htype = arp->ar_hrd;
+		entry->flags = ATF_COM;
+		memcpy(entry->ha, sha, arp->ar_hln);
+		entry->last_used = jiffies;
+		entry->next = arp_tables[hash];
+		arp_tables[hash] = entry;
+		entry->dev = skb->dev;
+		skb_queue_head_init(&entry->skb);
+		sti();
 	}
-  }
 
-  /*
-   * Since we updated the ARP cache, we might have enough
-   * information to send out some previously queued IP
-   * datagrams....
-   */
-  arp_send_q();
-
-  /*
-   * OK, we used that part of the info.  Now check if the
-   * request was an ARP REQUEST for one of our own addresses..
-   */
-  if (arp->ar_op != NET16(ARPOP_REQUEST)) {
-	kfree_skb(skb, FREE_READ);
-	return(0);
-  }
+	/* From here on, interrupts are enabled. Never touch entry->..
+	   any more. */
 
-/*
- * A broadcast arp, ignore it
- */
+	if (arp->ar_op != htons(ARPOP_REQUEST)
+		|| tip == INADDR_LOOPBACK) 
+	{
+		/* This wasn't a request, or some bad request for 127.0.0.1
+		   has made its way to the net, so delete it. */
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	/* Either we respond with our own hw address, or we do proxy arp for
+	   another machine. */
+	arp_send(ARPOP_REPLY, sip, dev, tip, sha,
+		(addr_hint == IS_MYADDR)? dev->dev_addr : ha);
 
-  if(chk_addr(dst)==IS_BROADCAST)
-  {
 	kfree_skb(skb, FREE_READ);
 	return 0;
-  }
-  
-  memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
-  if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) {
-	DPRINTF((DBG_ARP, "ARP: request was not for me!\n"));
-	kfree_skb(skb, FREE_READ);
-	return(0);
-  }
-
-  /*
-   * Yes, it is for us.
-   * Allocate, fill in and send an ARP REPLY packet.
-   */
-  ret = arp_response(arp, dev, addr_hint);
-  kfree_skb(skb, FREE_READ);
-  return(ret);
 }
 
 
-/* Create and send an ARP REQUEST packet. */
-void
-arp_send(unsigned long paddr, struct device *dev, unsigned long saddr)
-{
-  struct sk_buff *skb;
-  struct arphdr *arp;
-  unsigned char *ptr;
-  int tmp;
-
-  DPRINTF((DBG_ARP, "ARP: send(paddr=%s, ", in_ntoa(paddr)));
-  DPRINTF((DBG_ARP, "dev=%s, ", dev->name));
-  DPRINTF((DBG_ARP, "saddr=%s)\n", in_ntoa(saddr)));
-
-  skb = alloc_skb(sizeof(struct sk_buff) +
-  		sizeof(struct arphdr) + (2 * dev->addr_len) +
-		dev->hard_header_len +
-		(2 * 4 /* arp->plen */), GFP_ATOMIC);
-  if (skb == NULL) {
-	printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr));
-	return;
-  }
-  
-  /* Fill in the request. */
-  skb->sk = NULL;
-  skb->mem_addr = skb;
-  skb->len = sizeof(struct arphdr) +
-	     dev->hard_header_len + (2 * dev->addr_len) + 8;
-  skb->mem_len = sizeof(struct sk_buff) + skb->len;
-  skb->arp = 1;
-  skb->dev = dev;
-  skb->next = NULL;
-  skb->free = 1;
-  tmp = dev->hard_header(skb->data, dev, ETH_P_ARP, 0, saddr, skb->len);
-  if (tmp < 0) {
-	kfree_skb(skb,FREE_WRITE);
-	return;
-  }
-  arp = (struct arphdr *) (skb->data + tmp);
-  arp->ar_hrd = htons(dev->type);
-  if(dev->type!=3)	/* AX.25 */
-  	arp->ar_pro = htons(ETH_P_IP);
-  else
-  	arp->ar_pro = htons(0xCC);
-  arp->ar_hln = dev->addr_len;
-  arp->ar_pln = 4;
-  arp->ar_op = htons(ARPOP_REQUEST);
-
-  ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
-  memcpy(ptr, dev->dev_addr, arp->ar_hln);
-  ptr += arp->ar_hln;
-  memcpy(ptr, &saddr, arp->ar_pln);
-  ptr += arp->ar_pln;
-  /*memcpy(ptr, dev->broadcast, arp->ar_hln);*/
-  memset(ptr,0,arp->ar_hln);
-  ptr += arp->ar_hln;
-  memcpy(ptr, &paddr, arp->ar_pln);
-
-  DPRINTF((DBG_ARP, ">>\n"));
-  arp_print(arp);
-  dev->queue_xmit(skb, dev, 0);
-}
-
+/*
+ *	Find an arp mapping in the cache. If not found, post a request. 
+ */
+ 
+int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
+	   unsigned long saddr, struct sk_buff *skb)
+{
+	struct arp_table *entry;
+	unsigned long hash;
+
+	switch (ip_chk_addr(paddr)) 
+	{
+		case IS_MYADDR:
+			printk("ARP: arp called for own IP address\n");
+			memcpy(haddr, dev->dev_addr, dev->addr_len);
+			skb->arp = 1;
+			return 0;
+		case IS_BROADCAST:
+			memcpy(haddr, dev->broadcast, dev->addr_len);
+			skb->arp = 1;
+			return 0;
+	}
 
-/* Find an ARP mapping in the cache. If not found, post a REQUEST. */
-int
-arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
-	   unsigned long saddr)
-{
-  struct arp_table *apt;
-
-  DPRINTF((DBG_ARP, "ARP: find(haddr=%s, ", eth_print(haddr)));
-  DPRINTF((DBG_ARP, "paddr=%s, ", in_ntoa(paddr)));
-  DPRINTF((DBG_ARP, "dev=%s, saddr=%s)\n", dev->name, in_ntoa(saddr)));
-
-  switch(chk_addr(paddr)) {
-	case IS_MYADDR:
-		memcpy(haddr, dev->dev_addr, dev->addr_len);
-		return(0);
-	case IS_BROADCAST:
-		memcpy(haddr, dev->broadcast, dev->addr_len);
-		return(0);
-  }
-		
-  apt = arp_lookup(paddr);
-  if (apt != NULL) {
+	hash = HASH(paddr);
+	cli();
+	
 	/*
-	 * Make sure it's not too old. If it is too old, we will
-	 * just pretend we did not find it, and then arp_send will
-	 * verify the address for us.
+	 *	Find an entry 
 	 */
-        if ((apt->flags & ATF_PERM) ||
-	    (apt->last_used < jiffies+ARP_TIMEOUT && apt->hlen != 0)) {
-		apt->last_used = jiffies;
-		memcpy(haddr, apt->ha, dev->addr_len);
-		return(0);
-	} else {
-		DPRINTF((DBG_ARP, "ARP: find: found expired entry for %s\n",
-							in_ntoa(apt->ip)));
+	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+		if (entry->ip == paddr) 
+			break;
+			
+	
+	if (entry != NULL) 	/* It exists */
+	{
+	        if (!(entry->flags & ATF_COM)) 
+	        {
+			/* 
+			 *	A request was already send, but no reply yet. Thus
+			 *	queue the packet with the previous attempt
+			 */
+			 
+			if (skb != NULL)
+				skb_queue_tail(&entry->skb, skb);
+			sti();
+			return 1;
+		}
+		
+		/*
+		 *	Update the record
+		 */
+		 
+		entry->last_used = jiffies;
+		memcpy(haddr, entry->ha, dev->addr_len);
+		if (skb)
+			skb->arp = 1;
+		sti();
+		return 0;
 	}
-  }
 
-  /*
-   * This assume haddr are at least 4 bytes.
-   * If this isn't true we can use a lookup table, one for every dev.
-   * NOTE: this bit of code still looks fishy to me- FvK
-   */
-  *(unsigned long *)haddr = paddr;
+	/*
+	 *	Create a new unresolved entry. 
+	 */
+	 
+	entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+					GFP_ATOMIC);
+	if (entry != NULL) 
+	{
+		entry->ip = paddr;
+		entry->hlen = dev->addr_len;
+		entry->htype = dev->type;
+		entry->flags = 0;
+		memset(entry->ha, 0, dev->addr_len);
+		entry->last_used = jiffies;
+		entry->next = arp_tables[hash];
+		entry->dev = dev;
+		arp_tables[hash] = entry;
+		entry->timer.function = arp_expire_request;
+		entry->timer.data = (unsigned long)entry;
+		entry->timer.expires = ARP_RES_TIME;
+		add_timer(&entry->timer);
+		entry->retries = ARP_MAX_TRIES;
+		skb_queue_head_init(&entry->skb);
+		if (skb != NULL)
+			skb_queue_tail(&entry->skb, skb);
+	} 
+	else 
+	{
+		if (skb != NULL && skb->free)
+			kfree_skb(skb, FREE_WRITE);
+  	}
+	sti();
 
-  /* If we didn't find an entry, we will try to send an ARP packet. */
-  arp_send(paddr, dev, saddr);
+	/*
+	 *	If we didn't find an entry, we will try to send an ARP packet. 
+	 */
+	 
+	arp_send(ARPOP_REQUEST, paddr, dev, saddr, NULL, dev->dev_addr);
 
-  return(1);
+	return 1;
 }
 
 
-/* Add an entry to the ARP cache.  Check for dupes! */
-void
-arp_add(unsigned long addr, unsigned char *haddr, struct device *dev)
+/* 
+ *	Write the contents of the ARP cache to a PROCfs file. 
+ *
+ *	Will change soon to ASCII format
+ */
+ 
+int arp_get_info(char *buffer, char **start, off_t offset, int length)
 {
-  struct arp_table *apt;
+	struct arp_table *entry;
+	struct arpreq *req = (struct arpreq *) buffer;
+	int i;
+	off_t pos=0;
+	off_t begin=0;
+	int len=0;
 
-  DPRINTF((DBG_ARP, "ARP: add(%s, ", in_ntoa(addr)));
-  DPRINTF((DBG_ARP, "%s, ", eth_print(haddr)));
-  DPRINTF((DBG_ARP, "%d, %d)\n", dev->hard_header_len, dev->type));
+	cli();
+	/* Loop over the ARP table and copy structures to the buffer. */
+	for (i = 0; i < ARP_TABLE_SIZE; i++) 
+	{
+		for (entry = arp_tables[i]; entry; entry = entry->next) 
+		{
+			memset(req, 0, sizeof(struct arpreq));
+			req->arp_pa.sa_family = AF_INET;
+			memcpy(req->arp_pa.sa_data, &entry->ip, 4);
+			req->arp_ha.sa_family = entry->htype;
+			memcpy(req->arp_ha.sa_data, &entry->ha, MAX_ADDR_LEN);
+			req->arp_flags = entry->flags;
+			req++;
+			len+=sizeof(struct arpreq);
+			pos+=sizeof(struct arpreq);
+			if(pos<offset)
+			{
+				len=0;
+				begin=pos;
+				req=(struct arpreq *) buffer;
+			}
+			if(pos>offset+length)
+				break;
+		}
+		if(pos>offset+length)
+			break;
+	}
+	sti();
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	return len;
+}
 
-  /* This is probably a good check... */
-  if (addr == 0) {
-	printk("ARP: add: will not add entry for 0.0.0.0 !\n");
-	return;
-  }
 
-  /* First see if the address is already in the table. */
-  apt = arp_lookup(addr);
-  if (apt != NULL) {
-	DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr)));
-	apt->last_used = jiffies;
-	memcpy(apt->ha, haddr , dev->addr_len);
-	return;
-  }
-  arp_create(addr, haddr, dev->addr_len, dev->type);
+/* 
+ *	This will find an entry in the ARP table by looking at the IP address.
+ *	Be careful, interrupts are turned off on exit!!!  
+ */
+ 
+static struct arp_table *arp_lookup(unsigned long paddr)
+{
+	struct arp_table *entry;
+	unsigned long hash = HASH(paddr);
+
+	cli();
+	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+		if (entry->ip == paddr) break;
+	return entry;
 }
 
 
-/* Create an ARP entry for a device's broadcast address. */
-void
-arp_add_broad(unsigned long addr, struct device *dev)
+/*
+ *	Set (create) an ARP cache entry. 
+ */
+ 
+static int arp_req_set(struct arpreq *req)
 {
-  struct arp_table *apt;
+	struct arpreq r;
+	struct arp_table *entry;
+	struct sockaddr_in *si;
+	int htype, hlen;
+	unsigned long ip, hash;
+	struct rtable *rt;
+
+	memcpy_fromfs(&r, req, sizeof(r));
+
+	/* We only understand about IP addresses... */
+	if (r.arp_pa.sa_family != AF_INET)
+		return -EPFNOSUPPORT;
+
+	/*
+	 * Find out about the hardware type.
+	 * We have to be compatible with BSD UNIX, so we have to
+	 * assume that a "not set" value (i.e. 0) means Ethernet.
+	 */
+	 
+	switch (r.arp_ha.sa_family) {
+		case 0:
+			/* Moan about this. ARP family 0 is NetROM and _will_ be needed */
+			printk("Application using old BSD convention for arp set. Please recompile it.\n");
+		case ARPHRD_ETHER:
+			htype = ARPHRD_ETHER;
+			hlen = ETH_ALEN;
+			break;
+#ifdef CONFIG_AX25			
+		case ARPHRD_AX25:
+			htype = ARPHRD_AX25;
+			hlen = 7;
+			break;
+#endif			
+		default:
+			return -EPFNOSUPPORT;
+	}
+
+	si = (struct sockaddr_in *) &r.arp_pa;
+	ip = si->sin_addr.s_addr;
+	if (ip == 0) 
+	{
+		printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
+		return -EINVAL;
+	}
+	
+	/*
+	 *	Is it reachable directly ?
+	 */
+
+	rt = ip_rt_route(ip, NULL, NULL);
+	if (rt == NULL)
+		return -ENETUNREACH;
+
+	/*
+	 *	Is there an existing entry for this address? 
+	 */
+	 
+	hash = HASH(ip);
+	cli();
+	
+	/*
+	 *	Find the entry
+	 */
+	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
+		if (entry->ip == ip) 
+			break;
+			
+	/*
+	 *	Do we need to create a new entry
+	 */
+	 
+	if (entry == NULL) 
+	{
+		entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+					GFP_ATOMIC);
+		if (entry == NULL) 
+		{
+			sti();
+			return -ENOMEM;
+		}
+		entry->ip = ip;
+		entry->hlen = hlen;
+		entry->htype = htype;
+		entry->next = arp_tables[hash];
+		arp_tables[hash] = entry;
+		skb_queue_head_init(&entry->skb);
+	} 
+	else 
+		if (entry->flags & ATF_PUBL)
+			proxies--;
+	/*
+	 *	We now have a pointer to an ARP entry.  Update it! 
+	 */
+	 
+	memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
+	entry->last_used = jiffies;
+	entry->flags = r.arp_flags | ATF_COM;
+	if (entry->flags & ATF_PUBL)
+		proxies++;
+	entry->dev = rt->rt_dev;
+	sti();
 
-  arp_add(addr, dev->broadcast, dev);
-  apt = arp_lookup(addr);
-  if (apt != NULL) {
-	apt->flags |= ATF_PERM;
-  }
+	return 0;
 }
 
 
-/* Queue an IP packet, while waiting for the ARP reply packet. */
-void
-arp_queue(struct sk_buff *skb)
+/*
+ *	Get an ARP cache entry. 
+ */
+ 
+static int arp_req_get(struct arpreq *req)
 {
-  cli();
-  skb->tries = ARP_MAX_TRIES;
+	struct arpreq r;
+	struct arp_table *entry;
+	struct sockaddr_in *si;
+
+	/*
+	 *	We only understand about IP addresses... 
+	 */
+	 
+	memcpy_fromfs(&r, req, sizeof(r));
+	
+	if (r.arp_pa.sa_family != AF_INET) 
+		return -EPFNOSUPPORT;
+
+	/*
+	 *	Is there an existing entry for this address? 
+	 */
+	 
+	si = (struct sockaddr_in *) &r.arp_pa;
+	entry = arp_lookup(si->sin_addr.s_addr);
+
+	if (entry == NULL) 
+	{
+		sti();
+		return -ENXIO;
+	}
 
-  if (skb->next != NULL) {
+	/*
+	 *	We found it; copy into structure. 
+	 */
+	 
+	memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
+	r.arp_ha.sa_family = entry->htype;
+	r.arp_flags = entry->flags;
 	sti();
-	printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic);
-	return;
-  }
-  if(arp_q==NULL)
-  	arp_queue_kick();
-  skb_queue_tail(&arp_q,skb);
-  skb->magic = ARP_QUEUE_MAGIC;
-  sti();
+
+	/*
+	 *	Copy the information back 
+	 */
+	 
+	memcpy_tofs(req, &r, sizeof(r));
+	return 0;
 }
 
 
 /*
- * Write the contents of the ARP cache to a PROCfs file.
- * This is not by long perfect, as the internal ARP table doesn't
- * have all the info we would like to have.  Oh well, it works for
- * now, eh? - FvK
- * Also note, that due to space limits, we cannot generate more than
- * 4Kbyte worth of data.  This usually is enough, but I have seen
- * machines die from under me because of a *very* large ARP cache.
- * This can be simply tested by doing:
- *
- *	# ping 255.255.255.255
- *	# arp -a
- *
- * Perhaps we should redo PROCfs to handle larger buffers?  Michael?
+ *	Handle an ARP layer I/O control request. 
  */
-int
-arp_get_info(char *buffer)
+ 
+int arp_ioctl(unsigned int cmd, void *arg)
 {
-  struct arpreq *req;
-  struct arp_table *apt;
-  int i;
-  char *pos;
-
-  /* Loop over the ARP table and copy structures to the buffer. */
-  pos = buffer;
-  i = 0;
-  for (i = 0; i < ARP_TABLE_SIZE; i++) {
-	cli();
-	apt = arp_tables[i];
-	sti();
-	while (apt != NULL) {
-		if (pos < (buffer + 4000)) {
-			req = (struct arpreq *) pos;
-			memset((char *) req, 0, sizeof(struct arpreq));
-			req->arp_pa.sa_family = AF_INET;
-			memcpy((char *) req->arp_pa.sa_data, (char *) &apt->ip, 4);
-				req->arp_ha.sa_family = apt->htype;
-			memcpy((char *) req->arp_ha.sa_data,
-		       		(char *) &apt->ha, apt->hlen);
-			req->arp_flags = apt->flags;
-		}
-		pos += sizeof(struct arpreq);
-		cli();
-		apt = apt->next;
-		sti();
+	struct arpreq r;
+	struct sockaddr_in *si;
+	int err;
+
+	switch(cmd) 
+	{
+		case DDIOCSDBG:
+			return dbg_ioctl(arg, DBG_ARP);
+		case SIOCDARP:
+			if (!suser()) 
+				return -EPERM;
+			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+			if(err) 
+				return err;
+			memcpy_fromfs(&r, arg, sizeof(r));
+			if (r.arp_pa.sa_family != AF_INET)
+				return -EPFNOSUPPORT;
+			si = (struct sockaddr_in *) &r.arp_pa;
+			arp_destroy(si->sin_addr.s_addr, 1);
+			return 0;
+		case SIOCGARP:
+			err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
+			if(err)	
+				return err;
+			return arp_req_get((struct arpreq *)arg);
+		case SIOCSARP:
+			if (!suser()) 
+				return -EPERM;
+			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+			if(err) 
+				return err;
+			return arp_req_set((struct arpreq *)arg);
+		default:
+			return -EINVAL;
 	}
-  }
-  return(pos - buffer);
+	/*NOTREACHED*/
+	return 0;
 }
 
 
-/* Set (create) an ARP cache entry. */
-static int
-arp_req_set(struct arpreq *req)
-{
-  struct arpreq r;
-  struct arp_table *apt;
-  struct sockaddr_in *si;
-  int htype, hlen;
-
-  /* We only understand about IP addresses... */
-  memcpy_fromfs(&r, req, sizeof(r));
-  if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
-
-  /*
-   * Find out about the hardware type.
-   * We have to be compatible with BSD UNIX, so we have to
-   * assume that a "not set" value (i.e. 0) means Ethernet.
-   */
-  si = (struct sockaddr_in *) &r.arp_pa;
-  switch(r.arp_ha.sa_family) {
-	case 0:
-	case ARPHRD_ETHER:
-		htype = ARPHRD_ETHER;
-		hlen = ETH_ALEN;
-		break;
-		case ARPHRD_AX25:
-			htype = ARPHRD_AX25;
-			hlen = 7;
-			break;
-		
-	default:
-		return(-EPFNOSUPPORT);
-  }
-
-  /* Is there an existing entry for this address? */
-  if (si->sin_addr.s_addr == 0) {
-	printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
-	return(-EINVAL);
-  }
-  apt = arp_lookup(si->sin_addr.s_addr);
-  if (apt == NULL) {
-	apt = arp_create(si->sin_addr.s_addr,
-		(unsigned char *) r.arp_ha.sa_data, hlen, htype);
-	if (apt == NULL) return(-ENOMEM);
-  }
-
-  /* We now have a pointer to an ARP entry.  Update it! */
-  memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen);
-  apt->last_used = jiffies;
-  apt->flags = r.arp_flags;
-  if(apt->flags&ATF_PUBL)
-  	arp_proxies++;		/* Count proxy arps so we know if to use it */
-
-  return(0);
-}
-
-
-/* Get an ARP cache entry. */
-static int
-arp_req_get(struct arpreq *req)
-{
-  struct arpreq r;
-  struct arp_table *apt;
-  struct sockaddr_in *si;
-
-  /* We only understand about IP addresses... */
-  memcpy_fromfs(&r, req, sizeof(r));
-  if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
-
-  /* Is there an existing entry for this address? */
-  si = (struct sockaddr_in *) &r.arp_pa;
-  apt = arp_lookup(si->sin_addr.s_addr);
-  if (apt == NULL) return(-ENXIO);
-
-  /* We found it; copy into structure. */
-  memcpy((char *) r.arp_ha.sa_data, (char *) &apt->ha, apt->hlen);
-  r.arp_ha.sa_family = apt->htype;
-
-  /* Copy the information back */
-  memcpy_tofs(req, &r, sizeof(r));
-  return(0);
-}
-
-
-/* Delete an ARP cache entry. */
-static int
-arp_req_del(struct arpreq *req)
-{
-  struct arpreq r;
-  struct sockaddr_in *si;
-
-  /* We only understand about IP addresses... */
-  memcpy_fromfs(&r, req, sizeof(r));
-  if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
-
-  si = (struct sockaddr_in *) &r.arp_pa;
-  
-  /* The system cope with this but splats up a nasty kernel message 
-     We trap it beforehand and tell the user off */
-  if(chk_addr(si->sin_addr.s_addr)==IS_MYADDR)
-  	return -EINVAL;
-  	
-  arp_destroy(si->sin_addr.s_addr);
-
-  return(0);
-}
-
-
-/* Handle an ARP layer I/O control request. */
-int
-arp_ioctl(unsigned int cmd, void *arg)
-{
-  int err;
-  switch(cmd) {
-	case DDIOCSDBG:
-		return(dbg_ioctl(arg, DBG_ARP));
-	case SIOCDARP:
-		if (!suser()) return(-EPERM);
-		err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq));
-		if(err)
-			return err;
-		return(arp_req_del((struct arpreq *)arg));
-	case SIOCGARP:
-		err=verify_area(VERIFY_WRITE,arg,sizeof(struct arpreq));
-		if(err)
-			return err;
-		return(arp_req_get((struct arpreq *)arg));
-	case SIOCSARP:
-		if (!suser()) return(-EPERM);
-		err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq));
-		if(err)
-			return err;
-		return(arp_req_set((struct arpreq *)arg));
-	default:
-		return(-EINVAL);
-  }
-  /*NOTREACHED*/
-  return(0);
+/*
+ *	Called once on startup. 
+ */
+
+static struct packet_type arp_packet_type = 
+{
+	0,	/* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */
+	0,		/* copy */
+	arp_rcv,
+	NULL,
+	NULL
+};
+ 
+void arp_init (void)
+{
+	/* Register the packet type */
+	arp_packet_type.type=htons(ETH_P_ARP);
+	dev_add_pack(&arp_packet_type);
+	/* Start with the regular checks for expired arp entries. */
+	add_timer(&arp_timer);
 }
+
diff -u --recursive --new-file v1.1.3/linux/net/inet/arp.h linux/net/inet/arp.h
--- v1.1.3/linux/net/inet/arp.h	Thu Jan 13 20:51:25 1994
+++ linux/net/inet/arp.h	Wed Apr 13 12:52:21 1994
@@ -1,64 +1,14 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Definitions for the ARP protocol module.
- *
- * Version:	@(#)arp.h	1.0.6	05/21/93
- *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *
- *		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.
- */
+/* linux/net/inet/arp.h */
 #ifndef _ARP_H
 #define _ARP_H
 
-#define ARP_TABLE_SIZE	16		/* size of ARP table		*/
-#define ARP_TIMEOUT	30000		/* five minutes			*/
-#define ARP_RES_TIME	250		/* 2.5 seconds			*/
-
-#define ARP_MAX_TRIES	3		/* max # of tries to send ARP	*/
-#define ARP_QUEUE_MAGIC	0x0432447A	/* magic # for queues		*/
-
-
-/* This structure defines the ARP mapping cache. */
-struct arp_table {
-  struct arp_table		*next;
-  volatile unsigned long	last_used;
-  unsigned int			flags;
-#if 1
-  unsigned long			ip;
-#else
-  unsigned char			pa[MAX_ADDR_LEN];
-  unsigned char			plen;
-  unsigned char			ptype;
-#endif
-  unsigned char			ha[MAX_ADDR_LEN];
-  unsigned char			hlen;
-  unsigned char			htype;
-};
-
-
-/* This is also used in "sock.c" and "tcp.c" - YUCK! - FvK */
-extern struct sk_buff *arp_q;
-
-
-extern void	arp_destroy(unsigned long paddr);
+extern void	arp_init(void);
+extern void	arp_destroy(unsigned long paddr, int force);
 extern int	arp_rcv(struct sk_buff *skb, struct device *dev,
 			struct packet_type *pt);
 extern int	arp_find(unsigned char *haddr, unsigned long paddr,
-			 struct device *dev, unsigned long saddr);
-extern void	arp_add(unsigned long addr, unsigned char *haddr,
-			struct device *dev);
-extern void	arp_add_broad(unsigned long addr, struct device *dev);
-extern void	arp_queue(struct sk_buff *skb);
-extern int	arp_get_info(char *buffer);
+		struct device *dev, unsigned long saddr, struct sk_buff *skb);
+extern int	arp_get_info(char *buffer, char **start, off_t origin, int length);
 extern int	arp_ioctl(unsigned int cmd, void *arg);
-extern void	arp_destroy_maybe(unsigned long paddr);
 
 #endif	/* _ARP_H */
diff -u --recursive --new-file v1.1.3/linux/net/inet/datagram.c linux/net/inet/datagram.c
--- v1.1.3/linux/net/inet/datagram.c	Fri Feb 11 09:14:40 1994
+++ linux/net/inet/datagram.c	Wed Apr 13 12:52:21 1994
@@ -1,5 +1,5 @@
 /*
- *	SUCS	NET2 Debugged.
+ *	SUCS NET3:
  *
  *	Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
  *	of these would make sense. Not tonight however 8-).
@@ -14,6 +14,11 @@
  *		Alan Cox	:	Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
  *					AX.25 now works right, and SPX is feasible.
  *		Alan Cox	:	Fixed write select of non IP protocol crash.
+ *		Florian  La Roche:	Changed for my new skbuff handling.
+ *
+ *	Note:
+ *		A lot of this will change when the protocol/socket seperation
+ *	occurs. Using this will make things reasonably clean.
  */
 
 #include <linux/config.h>
@@ -26,15 +31,14 @@
 #include <linux/in.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
-#include "arp.h"
 #include "route.h"
 #include "tcp.h"
 #include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 
 
@@ -53,7 +57,7 @@
 	/* Socket is inuse - so the timer doesn't attack it */
 restart:
 	sk->inuse = 1;
-	while(sk->rqueue == NULL)	/* No data */
+	while(skb_peek(&sk->receive_queue) == NULL)	/* No data */
 	{
 		/* If we are shutdown then no more data is going to appear. We are done */
 		if (sk->shutdown & RCV_SHUTDOWN)
@@ -91,7 +95,7 @@
 		/* Interrupts off so that no packet arrives before we begin sleeping.
 		   Otherwise we might miss our wake up */
 		cli();
-		if (sk->rqueue == NULL)
+		if (skb_peek(&sk->receive_queue) == NULL)
 		{
 			interruptible_sleep_on(sk->sleep);
 			/* Signals may need a restart of the syscall */
@@ -115,10 +119,10 @@
 		sti();
 	  }
 	  /* Again only user level code calls this function, so nothing interrupt level
-	     will suddenely eat the rqueue */
+	     will suddenely eat the receive_queue */
 	  if (!(flags & MSG_PEEK))
 	  {
-		skb=skb_dequeue(&sk->rqueue);
+		skb=skb_dequeue(&sk->receive_queue);
 		if(skb!=NULL)
 			skb->users++;
 		else
@@ -127,7 +131,7 @@
 	  else
 	  {
 		cli();
-		skb=skb_peek(&sk->rqueue);
+		skb=skb_peek(&sk->receive_queue);
 		if(skb!=NULL)
 			skb->users++;
 		sti();
@@ -150,7 +154,7 @@
 		return;
 	}
 	/* See if it needs destroying */
-	if(skb->list == NULL)	/* Been dequeued by someone - ie its read */
+	if(!skb->next && !skb->prev)	/* Been dequeued by someone - ie its read */
 		kfree_skb(skb,FREE_READ);
 	restore_flags(flags);
 }
@@ -178,7 +182,7 @@
 				/* Connection closed: Wake up */
 				return(1);
 			}
-			if (sk->rqueue != NULL || sk->err != 0)
+			if (skb_peek(&sk->receive_queue) != NULL || sk->err != 0)
 			{	/* This appears to be consistent
 				   with other stacks */
 				return(1);
diff -u --recursive --new-file v1.1.3/linux/net/inet/dev.c linux/net/inet/dev.c
--- v1.1.3/linux/net/inet/dev.c	Mon Mar 21 16:13:14 1994
+++ linux/net/inet/dev.c	Wed Apr 13 17:09:46 1994
@@ -1,41 +1,28 @@
 /*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
+ * 	NET3	Protocol independant device support routines.
  *
- *		Interface (streams) handling functions.
- *
- * Version:	@(#)dev.c	1.0.19	05/31/93
- *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *		Mark Evans, <evansmp@uhura.aston.ac.uk>
- * 
- * Fixes:	
- *		Alan Cox:	check_addr returns a value for a wrong subnet
- *				ie not us but don't forward this!
- *		Alan Cox:	block timer if the inet_bh handler is running
- *		Alan Cox:	generic queue code added. A lot neater now
- *		C.E.Hawkins:	SIOCGIFCONF only reports 'upped' interfaces
- *		C.E.Hawkins:	IFF_PROMISC support
- *		Alan Cox:	Supports Donald Beckers new hardware 
- *				multicast layer, but not yet multicast lists.
- *		Alan Cox:	ip_addr_match problems with class A/B nets.
- *		C.E.Hawkins	IP 0.0.0.0 and also same net route fix. [FIXME: Ought to cause ICMP_REDIRECT]
- *		Alan Cox:	Removed bogus subnet check now the subnet code
- *				a) actually works for all A/B nets
- *				b) doesn't forward off the same interface.
- *		Alan Cox:	Multiple extra protocols
- *		Alan Cox:	Fixed ifconfig up of dud device setting the up flag
- *		Alan Cox:	Fixed verify_area errors
- *		Alan Cox:	Removed IP_SET_DEV as per Fred's comment. I hope this doesn't give
- *				anything away 8)
- *
  *		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.
+ *
+ *	Derived from the non IP parts of dev.c 1.0.19
+ * 		Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *				Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ *	Additional Authors:
+ *		Florian la Roche <rzsfl@rz.uni-sb.de>
+ *		Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ *	Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have
+ *	the rest as well commented in the end.
+ */
+
+/*
+ *	A lot of these includes will be going walkies very soon 
  */
+ 
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -51,544 +38,547 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/if_ether.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include "ip.h"
 #include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "arp.h"
-#ifdef CONFIG_AX25
-#include "ax25.h"
-#endif
-
-
-#ifdef CONFIG_IPX
-
-static struct packet_type ipx_8023_type = {
-  NET16(ETH_P_802_3),
-  0,
-  ipx_rcv,
-  NULL,
-  NULL
-};
-
-static struct packet_type ipx_packet_type = {
-  NET16(ETH_P_IPX),
-  0,
-  ipx_rcv,
-  NULL,
-  &ipx_8023_type
-};
 
-#endif
 
-#ifdef CONFIG_AX25
+/*
+ *	The list of packet types we will receive (as opposed to discard)
+ *	and the routines to invoke.
+ */
 
-static struct packet_type ax25_packet_type = {
-  NET16(ETH_P_AX25),
-  0,
-  ax25_rcv,
-  NULL,
-#ifdef CONFIG_IPX
-  &ipx_packet_type
-#else
-  NULL
-#endif
-};
-#endif
+struct packet_type *ptype_base = NULL;
 
+/*
+ *	Device drivers call our routines to queue packets here. We empty the
+ *	queue in the bottom half handler.
+ */
 
-static struct packet_type arp_packet_type = {
-  NET16(ETH_P_ARP),
-  0,		/* copy */
-  arp_rcv,
-  NULL,
-#ifdef CONFIG_IPX
-#ifndef CONFIG_AX25
-  &ipx_packet_type
-#else
-  &ax25_packet_type
-#endif
-#else
-#ifdef CONFIG_AX25
-  &ax25_packet_type
-#else
-  NULL		/* next */
-#endif
+static struct sk_buff_head backlog = 
+{
+	(struct sk_buff *)&backlog, (struct sk_buff *)&backlog
+#ifdef CONFIG_SKB_CHECK
+	,SK_HEAD_SKB
 #endif
 };
 
-
-static struct packet_type ip_packet_type = {
-  NET16(ETH_P_IP),
-  0,		/* copy */
-  ip_rcv,
-  NULL,
-  &arp_packet_type
-};
-   
-
-struct packet_type *ptype_base = &ip_packet_type;
-static struct sk_buff *volatile backlog = NULL;
+/* 
+ *	We don't overdo the queue or we will thrash memory badly.
+ */
+ 
 static int backlog_size = 0;
-static unsigned long ip_bcast = 0;
 
+/*
+ *	The number of sockets open for 'all' protocol use. We have to
+ *	know this to copy a buffer the correct number of times.
+ */
+ 
+static int dev_nit=0;
 
-/* Return the lesser of the two values. */
-static unsigned long
-min(unsigned long a, unsigned long b)
+/*
+ *	Return the lesser of the two values. 
+ */
+ 
+static __inline__ unsigned long min(unsigned long a, unsigned long b)
 {
-  if (a < b) return(a);
-  return(b);
+  return (a < b)? a : b;
 }
 
 
-/* Determine a default network mask, based on the IP address. */
-static unsigned long
-get_mask(unsigned long addr)
-{
-  unsigned long dst;
-
-  if (addr == 0L) 
-  	return(0L);	/* special case */
-
-  dst = ntohl(addr);
-  if (IN_CLASSA(dst)) 
-  	return(htonl(IN_CLASSA_NET));
-  if (IN_CLASSB(dst)) 
-  	return(htonl(IN_CLASSB_NET));
-  if (IN_CLASSC(dst)) 
-  	return(htonl(IN_CLASSC_NET));
-  
-  /* Something else, probably a subnet. */
-  return(0);
-}
+/******************************************************************************************
 
+		Protocol management and registration routines
 
-int
-ip_addr_match(unsigned long me, unsigned long him)
-{
-  int i;
-  unsigned long mask=0xFFFFFFFF;
-  DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
-  DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
-
-  if (me == him) 
-  	return(1);
-  for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8) {
-	if ((me & 0xFF) != (him & 0xFF)) {
-		/*
-		 * The only way this could be a match is for
-		 * the rest of addr1 to be 0 or 255.
-		 */
-		if (me != 0 && me != mask) return(0);
-		return(1);
-	}
-  }
-  return(1);
-}
+*******************************************************************************************/
 
 
-/* Check the address for our address, broadcasts, etc. */
-int chk_addr(unsigned long addr)
+/*
+ *	Add a protocol ID to the list.
+ */
+ 
+void dev_add_pack(struct packet_type *pt)
 {
-	struct device *dev;
-	unsigned long mask;
+	struct packet_type *p1;
+	pt->next = ptype_base;
 
-	/* Accept both `all ones' and `all zeros' as BROADCAST. */
-	if (addr == INADDR_ANY || addr == INADDR_BROADCAST)
-		return IS_BROADCAST;
-
-	mask = get_mask(addr);
-
-	/* Accept all of the `loopback' class A net. */
-	if ((addr & mask) == htonl(0x7F000000L))
-		return IS_MYADDR;
-
-	/* OK, now check the interface addresses. */
-	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (!(dev->flags & IFF_UP))
-			continue;
-		if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/)
-			return IS_MYADDR;
-		/* Is it the exact IP address? */
-		if (addr == dev->pa_addr)
-			return IS_MYADDR;
-		/* Is it our broadcast address? */
-		if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
-			return IS_BROADCAST;
-		/* Nope. Check for a subnetwork broadcast. */
-		if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0) {
-			if ((addr & ~dev->pa_mask) == 0)
-				return IS_BROADCAST;
-			if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
-				return IS_BROADCAST;
-		}
-		/* Nope. Check for Network broadcast. */
-		if (((addr ^ dev->pa_addr) & mask) == 0) {
-			if ((addr & ~mask) == 0)
-				return IS_BROADCAST;
-			if ((addr & ~mask) == ~mask)
-				return IS_BROADCAST;
-		}
+	/* 
+	 *	Don't use copy counts on ETH_P_ALL. Instead keep a global
+ 	 *	count of number of these and use it and pt->copy to decide
+	 *	copies 
+	 */
+	 
+	pt->copy=0;	/* Assume we will not be copying the buffer before 
+			 * this routine gets it
+			 */
+			 
+	if(pt->type == htons(ETH_P_ALL))
+  		dev_nit++;	/* I'd like a /dev/nit too one day 8) */
+	else
+	{
+  		/*
+  		 *	See if we need to copy it - that is another process also
+  		 *	wishes to receive this type of packet.
+  		 */
+		for (p1 = ptype_base; p1 != NULL; p1 = p1->next) 
+		{
+			if (p1->type == pt->type) 
+			{
+				pt->copy = 1;	/* We will need to copy */
+				break;
+			}
+	  	}
 	}
-	return 0;		/* no match at all */
-}
-
-
-/*
- * Retrieve our own address.
- * Because the loopback address (127.0.0.1) is already recognized
- * automatically, we can use the loopback interface's address as
- * our "primary" interface.  This is the addressed used by IP et
- * al when it doesn't know which address to use (i.e. it does not
- * yet know from or to which interface to go...).
- */
-unsigned long
-my_addr(void)
-{
-  struct device *dev;
-
-  for (dev = dev_base; dev != NULL; dev = dev->next) {
-	if (dev->flags & IFF_LOOPBACK) return(dev->pa_addr);
-  }
-  return(0);
-}
-
-
-static int dev_nit=0; /* Number of network taps running */
-
-/* Add a protocol ID to the list.  This will change soon. */
-void
-dev_add_pack(struct packet_type *pt)
-{
-  struct packet_type *p1;
-  pt->next = ptype_base;
-
-  /* Don't use copy counts on ETH_P_ALL. Instead keep a global
-     count of number of these and use it and pt->copy to decide
-     copies */
-  pt->copy=0;
-  if(pt->type==NET16(ETH_P_ALL))
-  	dev_nit++;	/* I'd like a /dev/nit too one day 8) */
-  else
-  {
-  	/* See if we need to copy it. */
-  	for (p1 = ptype_base; p1 != NULL; p1 = p1->next) {
-		if (p1->type == pt->type) {
-			pt->copy = 1;
-			break;
-		}
-	  }
-  }
   
   /*
    *	NIT taps must go at the end or inet_bh will leak!
    */
    
-  if(pt->type==NET16(ETH_P_ALL))
-  {
-  	pt->next=NULL;
-  	if(ptype_base==NULL)
-	  	ptype_base=pt;
-	else
+	if (pt->type == htons(ETH_P_ALL))
 	{
-		for(p1=ptype_base;p1->next!=NULL;p1=p1->next);
-		p1->next=pt;
-	}
-  }
-  else
-  	ptype_base = pt;
+  		pt->next=NULL;
+  		if(ptype_base==NULL)
+		  	ptype_base=pt;
+		else
+		{
+			/* 
+			 *	Move to the end of the list
+			 */
+			for(p1=ptype_base;p1->next!=NULL;p1=p1->next);
+			/*
+			 *	Hook on the end
+			 */
+			p1->next=pt;
+		}
+	 }
+	else
+/*
+ *	It goes on the start 
+ */
+		ptype_base = pt;
 }
 
 
-/* Remove a protocol ID from the list.  This will change soon. */
-void
-dev_remove_pack(struct packet_type *pt)
-{
-  struct packet_type *lpt, *pt1;
-
-  if (pt->type == NET16(ETH_P_ALL))
-  	dev_nit--;
-  if (pt == ptype_base) {
-	ptype_base = pt->next;
-	return;
-  }
+/*
+ *	Remove a protocol ID from the list.
+ */
+ 
+void dev_remove_pack(struct packet_type *pt)
+{
+	struct packet_type *lpt, *pt1;
 
-  lpt = NULL;
-  for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) {
-	if (pt1->next == pt ) {
-		cli();
-		if (!pt->copy && lpt) 
-			lpt->copy = 0;
-		pt1->next = pt->next;
-		sti();
+	/*
+	 *	Keep the count of nit (Network Interface Tap) sockets correct.
+	 */
+	 
+	if (pt->type == htons(ETH_P_ALL))
+	  	dev_nit--;
+	  	
+	/*
+	 *	If we are first, just unhook us.
+	 */
+	 
+	if (pt == ptype_base) 
+	{
+		ptype_base = pt->next;
 		return;
 	}
 
-	if (pt1->next -> type == pt ->type && pt->type != NET16(ETH_P_ALL)) {
-		lpt = pt1->next;
+	lpt = NULL;
+	
+	/*
+	 *	This is harder. What we do is to walk the list of sockets 
+	 *	for this type. We unhook the entry, and if there is a previous
+	 *	entry that is copying _and_ we are not copying, (ie we are the
+	 *	last entry for this type) then the previous one is set to
+	 *	non-copying as it is now the last.
+	 */
+	for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) 
+	{
+		if (pt1->next == pt ) 
+		{
+			cli();
+			if (!pt->copy && lpt) 
+				lpt->copy = 0;
+			pt1->next = pt->next;
+			sti();
+			return;
+		}
+		if (pt1->next->type == pt->type && pt->type != htons(ETH_P_ALL))
+			lpt = pt1->next;
 	}
-  }
 }
 
+/*****************************************************************************************
 
-/* Find an interface in the list. This will change soon. */
-struct device *
-dev_get(char *name)
-{
-  struct device *dev;
-
-  for (dev = dev_base; dev != NULL; dev = dev->next) {
-	if (strcmp(dev->name, name) == 0) 
-		return(dev);
-  }
-  return(NULL);
-}
+			    Device Inteface Subroutines
 
+******************************************************************************************/
 
-/* Find an interface that can handle addresses for a certain address. */
-struct device * dev_check(unsigned long addr)
+/* 
+ *	Find an interface by name.
+ */
+ 
+struct device *dev_get(char *name)
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev; dev = dev->next) {
-		if (!(dev->flags & IFF_UP))
-			continue;
-		if (!(dev->flags & IFF_POINTOPOINT))
-			continue;
-		if (addr != dev->pa_dstaddr)
-			continue;
-		return dev;
-	}
-	for (dev = dev_base; dev; dev = dev->next) {
-		if (!(dev->flags & IFF_UP))
-			continue;
-		if (dev->flags & IFF_POINTOPOINT)
-			continue;
-		if (dev->pa_mask & (addr ^ dev->pa_addr))
-			continue;
-		return dev;
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+		if (strcmp(dev->name, name) == 0)
+			return(dev);
 	}
-	return NULL;
+	return(NULL);
 }
 
 
-/* Prepare an interface for use. */
-int
-dev_open(struct device *dev)
-{
-  int ret = 0;
-
-  if (dev->open) 
-  	ret = dev->open(dev);
-  if (ret == 0) 
-  	dev->flags |= (IFF_UP | IFF_RUNNING);
+/*
+ *	Prepare an interface for use. 
+ */
+ 
+int dev_open(struct device *dev)
+{
+	int ret = 0;
+
+	/*
+	 *	Call device private open method
+	 */
+	if (dev->open) 
+  		ret = dev->open(dev);
 
-  return(ret);
+	/*
+	 *	If it went open OK then set the flags
+	 */
+	 
+	if (ret == 0) 
+		dev->flags |= (IFF_UP | IFF_RUNNING);
+	
+	return(ret);
 }
 
 
-/* Completely shutdown an interface. */
-int
-dev_close(struct device *dev)
-{
-  if (dev->flags != 0) {
-  	int ct=0;
-	dev->flags = 0;
-	if (dev->stop) 
-		dev->stop(dev);
-	rt_flush(dev);
-	dev->pa_addr = 0;
-	dev->pa_dstaddr = 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask = 0;
-	/* Purge any queued packets when we down the link */
-	while(ct<DEV_NUMBUFFS)
-	{
-		struct sk_buff *skb;
-		while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
-			if(skb->free)
-				kfree_skb(skb,FREE_WRITE);
-		ct++;
+/*
+ *	Completely shutdown an interface.
+ *
+ *	WARNING: Both because of the way the upper layers work (that can be fixed)
+ *	and because of races during a close (that can't be fixed any other way)
+ *	a device may be given things to transmit EVEN WHEN IT IS DOWN. The driver
+ *	MUST cope with this (eg by freeing and dumping the frame).
+ */
+ 
+int dev_close(struct device *dev)
+{
+	/*
+	 *	Only close a device if it is up.
+	 */
+	 
+	if (dev->flags != 0) 
+	{
+  		int ct=0;
+		dev->flags = 0;
+		/*
+		 *	Call the device specific close. This cannot fail.
+		 */
+		if (dev->stop) 
+			dev->stop(dev);
+		/*
+		 *	Delete the route to the device.
+		 */
+		ip_rt_flush(dev);
+		/*
+		 *	Blank the IP addresses
+		 */
+		dev->pa_addr = 0;
+		dev->pa_dstaddr = 0;
+		dev->pa_brdaddr = 0;
+		dev->pa_mask = 0;
+		/*
+		 *	Purge any queued packets when we down the link 
+		 */
+		while(ct<DEV_NUMBUFFS)
+		{
+			struct sk_buff *skb;
+			while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
+				if(skb->free)
+					kfree_skb(skb,FREE_WRITE);
+			ct++;
+		}
 	}
-  }
-
-  return(0);
+	return(0);
 }
 
 
-/* Send (or queue for sending) a packet. */
-void
-dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+/*
+ *	Send (or queue for sending) a packet. 
+ */
+
+void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
-  int where = 0;		/* used to say if the packet should go	*/
+	int where = 0;		/* used to say if the packet should go	*/
 				/* at the front or the back of the	*/
 				/* queue.				*/
 
-  DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
+	DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
 							skb, dev, pri));
 
-  if (dev == NULL) {
-	printk("dev.c: dev_queue_xmit: dev = NULL\n");
-	return;
-  }
+	if (dev == NULL) 
+	{
+		printk("dev.c: dev_queue_xmit: dev = NULL\n");
+		return;
+	}
  
-  IS_SKB(skb);
+	IS_SKB(skb);
     
-  skb->dev = dev;
-  if (skb->next != NULL) {
-	/* Make sure we haven't missed an interrupt. */
-	dev->hard_start_xmit(NULL, dev);
-	return;
-  }
+	skb->dev = dev;
+	start_bh_atomic();
 
-  if (pri < 0) {
-	pri = -pri-1;
-	where = 1;
-  }
-
-  if (pri >= DEV_NUMBUFFS) {
-	printk("bad priority in dev_queue_xmit.\n");
-	pri = 1;
-  }
+	/*
+	 *	This just eliminates some race conditions, but not all... 
+	 */
 
-  if (dev->hard_start_xmit(skb, dev) == 0) {
-	return;
-  }
+	if (skb->next != NULL) 
+	{
+		/*
+		 *	Make sure we haven't missed an interrupt. 
+		 */
+		printk("dev_queue_xmit: worked around a missed interrupt\n");
+		dev->hard_start_xmit(NULL, dev);
+		end_bh_atomic();
+		return;
+  	}
+
+	/*
+	 *	Negative priority is used to flag a frame that is being pulled from the
+	 *	queue front as a retransmit attempt. It therefore goes back on the queue
+	 *	start on a failure.
+	 */
+	 
+  	if (pri < 0) 
+  	{
+		pri = -pri-1;
+		where = 1;
+  	}
+
+	if (pri >= DEV_NUMBUFFS) 
+	{
+		printk("bad priority in dev_queue_xmit.\n");
+		pri = 1;
+	}
+
+	/*
+	 *	If the address has not been resolved called the device header rebuilder.
+	 *	This can cover all protocols and technically no just ARP either.
+	 */
+	 
+	if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
+		end_bh_atomic();
+		return;
+	}
+
+	/*
+	 *	This is vitally important. We _MUST_ keep packets in order. While tcp/ip
+	 *	suffers only a slow down some IPX apps, and all the AX.25 code will break
+	 *	if it occurs out of order. 
+	 *
+	 *	This is commented out while I fix a few 'side effects'
+	 */
+
+	if ((where==1 || skb_peek(&dev->buffs[pri])==NULL) && dev->hard_start_xmit(skb, dev) == 0) 
+	{
+		end_bh_atomic();
+		return;
+	}
+
+	/*
+	 *	Transmission failed, put skb back into a list. 
+	 */
 
-  /* Put skb into a bidirectional circular linked list. */
-  DPRINTF((DBG_DEV, "dev_queue_xmit dev->buffs[%d]=%X\n",
-					pri, dev->buffs[pri]));
-
-  /* Interrupts should already be cleared by hard_start_xmit. */
-  cli();
-  skb->magic = DEV_QUEUE_MAGIC;
-  if(where)
-  	skb_queue_head(&dev->buffs[pri],skb);
-  else
-  	skb_queue_tail(&dev->buffs[pri],skb);
-  skb->magic = DEV_QUEUE_MAGIC;
-  sti();
+	if(where)
+		skb_queue_head(&dev->buffs[pri],skb);
+	else
+		skb_queue_tail(&dev->buffs[pri],skb);
+	end_bh_atomic();
 }
 
 /*
- * Receive a packet from a device driver and queue it for the upper
- * (protocol) levels.  It always succeeds.
+ *	Receive a packet from a device driver and queue it for the upper
+ *	(protocol) levels.  It always succeeds. This is the recommended 
+ *	interface to use.
  */
-void
-netif_rx(struct sk_buff *skb)
+
+void netif_rx(struct sk_buff *skb)
 {
-  static int dropping = 0;
-  /* Set any necessary flags. */
-  skb->sk = NULL;
-  skb->free = 1;
-
-  /* check that we aren't oevrdoing things.. */
-  if (!backlog_size)
-  	dropping = 0;
-  else if (backlog_size > 100)
-  	dropping = 1;
-  if (dropping) {
-	kfree_skb(skb, FREE_READ);
-	return;
-  }
-  /* and add it to the "backlog" queue. */
-  IS_SKB(skb);
-  skb_queue_tail(&backlog,skb);
-  backlog_size++;
+	static int dropping = 0;
+	extern struct timeval xtime;
+
+	/*
+	 *	Any received buffers are un-owned and should be discarded
+	 *	when freed. These will be updated later as the frames get
+	 *	owners.
+	 */
+	skb->sk = NULL;
+	skb->free = 1;
+	if(skb->stamp.tv_sec==0)
+		skb->stamp = xtime;
+
+	/*
+	 *	Check that we aren't oevrdoing things.
+	 */
+
+	if (!backlog_size)
+  		dropping = 0;
+	else if (backlog_size > 100)
+		dropping = 1;
+
+	if (dropping) 
+	{
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+
+	/*
+	 *	Add it to the "backlog" queue. 
+	 */
+
+	IS_SKB(skb);
+	skb_queue_tail(&backlog,skb);
+	backlog_size++;
   
-  /* If any packet arrived, mark it for processing. */
-  if (backlog != NULL) mark_bh(INET_BH);
+	/*
+	 *	If any packet arrived, mark it for processing after the
+	 *	hardware interrupt returns.
+	 */
 
-  return;
+	mark_bh(INET_BH);
+	return;
 }
 
 
 /*
- * The old interface to fetch a packet from a device driver.
- * This function is the base level entry point for all drivers that
- * want to send a packet to the upper (protocol) levels.  It takes
- * care of de-multiplexing the packet to the various modules based
- * on their protocol ID.
+ *	The old interface to fetch a packet from a device driver.
+ *	This function is the base level entry point for all drivers that
+ *	want to send a packet to the upper (protocol) levels.  It takes
+ *	care of de-multiplexing the packet to the various modules based
+ *	on their protocol ID.
  *
- * Return values:	1 <- exit I can't do any more
+ *	Return values:	1 <- exit I can't do any more
  *			0 <- feed me more (i.e. "done", "OK"). 
+ *
+ *	This function is OBSOLETE and should not be used by any new
+ *	device.
  */
-int
-dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
+
+int dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
 {
-  static int dropping = 0;
-  struct sk_buff *skb = NULL;
-  unsigned char *to;
-  int amount, left;
-  int len2;
-
-  if (dev == NULL || buff == NULL || len <= 0) return(1);
-  if (flags & IN_SKBUFF) {
-	skb = (struct sk_buff *) buff;
-  } else {
-	if (dropping) {
-	  if (backlog != NULL)
-	      return(1);
-	  printk("INET: dev_rint: no longer dropping packets.\n");
-	  dropping = 0;
-	}
+	static int dropping = 0;
+	struct sk_buff *skb = NULL;
+	unsigned char *to;
+	int amount, left;
+	int len2;
 
-	skb = alloc_skb(sizeof(*skb) + len, GFP_ATOMIC);
-	if (skb == NULL) {
-		printk("dev_rint: packet dropped on %s (no memory) !\n",
-		       dev->name);
-		dropping = 1;
+	if (dev == NULL || buff == NULL || len <= 0) 
 		return(1);
+
+	if (flags & IN_SKBUFF) 
+	{
+		skb = (struct sk_buff *) buff;
 	}
-	skb->mem_len = sizeof(*skb) + len;
-	skb->mem_addr = (struct sk_buff *) skb;
+	else
+	{
+		if (dropping) 
+		{
+			if (skb_peek(&backlog) != NULL)
+				return(1);
+			printk("INET: dev_rint: no longer dropping packets.\n");
+			dropping = 0;
+		}
+
+		skb = alloc_skb(len, GFP_ATOMIC);
+		if (skb == NULL) 
+		{
+			printk("dev_rint: packet dropped on %s (no memory) !\n",
+			       dev->name);
+			dropping = 1;
+			return(1);
+		}
+
+		/* 
+		 *	First we copy the packet into a buffer, and save it for later. We
+		 *	in effect handle the incoming data as if it were from a circular buffer
+		 */
 
-	/* First we copy the packet into a buffer, and save it for later. */
-	to = skb->data;
-	left = len;
-	len2 = len;
-	while (len2 > 0) {
-		amount = min(len2, (unsigned long) dev->rmem_end -
+		to = skb->data;
+		left = len;
+
+		len2 = len;
+		while (len2 > 0) 
+		{
+			amount = min(len2, (unsigned long) dev->rmem_end -
 						(unsigned long) buff);
-		memcpy(to, buff, amount);
-		len2 -= amount;
-		left -= amount;
-		buff += amount;
-		to += amount;
-		if ((unsigned long) buff == dev->rmem_end)
-			buff = (unsigned char *) dev->rmem_start;
+			memcpy(to, buff, amount);
+			len2 -= amount;
+			left -= amount;
+			buff += amount;
+			to += amount;
+			if ((unsigned long) buff == dev->rmem_end)
+				buff = (unsigned char *) dev->rmem_start;
+		}
 	}
-  }
-  skb->len = len;
-  skb->dev = dev;
-  skb->free = 1;
-
-  netif_rx(skb);
-  /* OK, all done. */
-  return(0);
+
+	/*
+	 *	Tag the frame and kick it to the proper receive routine
+	 */
+	 
+	skb->len = len;
+	skb->dev = dev;
+	skb->free = 1;
+
+	netif_rx(skb);
+	/*
+	 *	OK, all done. 
+	 */
+	return(0);
 }
 
 
-/* This routine causes all interfaces to try to send some data. */
-void
-dev_transmit(void)
-{
-  struct device *dev;
-
-  for (dev = dev_base; dev != NULL; dev = dev->next) {
-	if (!dev->tbusy) {
-		dev_tint(dev);
+/*
+ *	This routine causes all interfaces to try to send some data. 
+ */
+ 
+void dev_transmit(void)
+{
+	struct device *dev;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+		if (!dev->tbusy) {
+			/*
+			 *	Kick the device
+			 */
+			dev_tint(dev);
+		}
 	}
-  }
 }
 
-static volatile char in_bh = 0;
+
+/**********************************************************************************
+
+			Receive Queue Processor
+			
+***********************************************************************************/
+
+/*
+ *	This is a single non-rentrant routine which takes the received packet
+ *	queue and throws it at the networking layers in the hope that something
+ *	useful will emerge.
+ */
+ 
+volatile char in_bh = 0;	/* Non-rentrant remember */
 
 int in_inet_bh()	/* Used by timer.c */
 {
@@ -596,116 +586,181 @@
 }
 
 /*
- * This function gets called periodically, to see if we can
- * process any data that came in from some interface.
- *
+ *	When we are called the queue is ready to grab, the interrupts are
+ *	on and hardware can interrupt and queue to the receive queue a we
+ *	run with no problems.
+ *	This is run as a bottom half after an interrupt handler that does
+ *	mark_bh(INET_BH);
  */
-void
-inet_bh(void *tmp)
+ 
+void inet_bh(void *tmp)
 {
-  struct sk_buff *skb;
-  struct packet_type *ptype;
-  unsigned short type;
-  unsigned char flag = 0;
-  int nitcount;
-
-  /* Atomically check and mark our BUSY state. */
-  if (set_bit(1, (void*)&in_bh))
-      return;
+	struct sk_buff *skb;
+	struct packet_type *ptype;
+	unsigned short type;
+	unsigned char flag = 0;
+	int nitcount;
+
+	/*
+	 *	Atomically check and mark our BUSY state. 
+	 */
+
+	if (set_bit(1, (void*)&in_bh))
+		return;
+
+	/*
+	 *	Can we send anything now? We want to clear the
+	 *	decks for any more sends that get done as we
+	 *	process the input.
+	 */
 
-  /* Can we send anything now? */
-  dev_transmit();
+	dev_transmit();
   
-  /* Any data left to process? */
-  while((skb=skb_dequeue(&backlog))!=NULL)
-  {
-  	backlog_size--;
-  	nitcount=dev_nit;
-	flag=0;
-	sti();
-       /*
-	* Bump the pointer to the next structure.
-	* This assumes that the basic 'skb' pointer points to
-	* the MAC header, if any (as indicated by its "length"
-	* field).  Take care now!
-	*/
-       skb->h.raw = skb->data + skb->dev->hard_header_len;
-       skb->len -= skb->dev->hard_header_len;
-
-       /*
-	* Fetch the packet protocol ID.  This is also quite ugly, as
-	* it depends on the protocol driver (the interface itself) to
-	* know what the type is, or where to get it from.  The Ethernet
-	* interfaces fetch the ID from the two bytes in the Ethernet MAC
-	* header (the h_proto field in struct ethhdr), but drivers like
-	* SLIP and PLIP have no alternative but to force the type to be
-	* IP or something like that.  Sigh- FvK
-	*/
-       type = skb->dev->type_trans(skb, skb->dev);
-
-	/*
-	 * We got a packet ID.  Now loop over the "known protocols"
-	 * table (which is actually a linked list, but this will
-	 * change soon if I get my way- FvK), and forward the packet
-	 * to anyone who wants it.
-	 */
-	for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) {
-		if (ptype->type == type || ptype->type == NET16(ETH_P_ALL)) {
-			struct sk_buff *skb2;
-
-			if (ptype->type==NET16(ETH_P_ALL))
-				nitcount--;
-			if (ptype->copy || nitcount) {	/* copy if we need to	*/
-				skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC);
-				if (skb2 == NULL) 
-					continue;
-				memcpy(skb2, (const void *) skb, skb->mem_len);
-				skb2->mem_addr = skb2;
-				skb2->h.raw = (unsigned char *)(
-				    (unsigned long) skb2 +
-				    (unsigned long) skb->h.raw -
-				    (unsigned long) skb
-				);
-				skb2->free = 1;
-			} else {
-				skb2 = skb;
-			}
+	/*
+	 *	Any data left to process. This may occur because a
+	 *	mark_bh() is done after we empty the queue including
+	 *	that from the device which does a mark_bh() just after
+	 */
 
-			/* This used to be in the 'else' part, but then
-			 * we don't have this flag set when we get a
-			 * protocol that *does* require copying... -FvK
-			 */
-			flag = 1;
+	cli();
+	
+	/*
+	 *	While the queue is not empty
+	 */
+	 
+	while((skb=skb_dequeue(&backlog))!=NULL)
+	{
+		/*
+		 *	We have a packet. Therefore the queue has shrunk
+		 */
+  		backlog_size--;
+
+	  	nitcount=dev_nit;
+		flag=0;
+		sti();
+		
+	       /*
+		*	Bump the pointer to the next structure.
+		*	This assumes that the basic 'skb' pointer points to
+		*	the MAC header, if any (as indicated by its "length"
+		*	field).  Take care now!
+		*/
+
+		skb->h.raw = skb->data + skb->dev->hard_header_len;
+		skb->len -= skb->dev->hard_header_len;
+
+	       /*
+		* 	Fetch the packet protocol ID.  This is also quite ugly, as
+		* 	it depends on the protocol driver (the interface itself) to
+		* 	know what the type is, or where to get it from.  The Ethernet
+		* 	interfaces fetch the ID from the two bytes in the Ethernet MAC
+		*	header (the h_proto field in struct ethhdr), but other drivers
+		*	may either use the ethernet ID's or extra ones that do not
+		*	clash (eg ETH_P_AX25). We could set this before we queue the
+		*	frame. In fact I may change this when I have time.
+		*/
+		
+		type = skb->dev->type_trans(skb, skb->dev);
 
-			/* Kick the protocol handler. */
-			ptype->func(skb2, skb->dev, ptype);
+		/*
+		 *	We got a packet ID.  Now loop over the "known protocols"
+		 *	table (which is actually a linked list, but this will
+		 *	change soon if I get my way- FvK), and forward the packet
+		 *	to anyone who wants it.
+		 *
+		 *	[FvK didn't get his way but he is right this ought to be
+		 *	hashed so we typically get a single hit. The speed cost
+		 *	here is minimal but no doubt adds up at the 4,000+ pkts/second
+		 *	rate we can hit flat out]
+		 */
+		 
+		for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) 
+		{
+			if (ptype->type == type || ptype->type == htons(ETH_P_ALL)) 
+			{
+				struct sk_buff *skb2;
+
+				if (ptype->type == htons(ETH_P_ALL))
+					nitcount--;
+				if (ptype->copy || nitcount) 
+				{	
+					/*
+					 *	copy if we need to
+					 */
+#ifdef OLD
+					skb2 = alloc_skb(skb->len, GFP_ATOMIC);
+					if (skb2 == NULL) 
+						continue;
+					memcpy(skb2, skb, skb2->mem_len);
+					skb2->mem_addr = skb2;
+					skb2->h.raw = (unsigned char *)(
+					    (unsigned long) skb2 +
+					    (unsigned long) skb->h.raw -
+					    (unsigned long) skb
+					);
+					skb2->free = 1;
+#else
+					skb2=skb_clone(skb, GFP_ATOMIC);
+					if(skb2==NULL)
+						continue;
+#endif				
+				} 
+				else 
+				{
+					skb2 = skb;
+				}
+
+				/*
+				 *	Protocol located. 
+				 */
+				 
+				flag = 1;
+
+				/*
+				 *	Kick the protocol handler. This should be fast
+				 *	and efficient code.
+				 */
+
+				ptype->func(skb2, skb->dev, ptype);
+			}
+		} /* End of protocol list loop */
+
+		/*
+		 * 	Has an unknown packet has been received ?
+		 */
+	 
+		if (!flag) 
+		{
+			DPRINTF((DBG_DEV,"INET: unknown packet type 0x%04X (ignored)\n", type));
+			kfree_skb(skb, FREE_WRITE);
 		}
-	}
+
+		/*
+		 *	Again, see if we can transmit anything now. 
+		 */
 
+		dev_transmit();
+		cli();
+  	}	/* End of queue loop */
+  	
+  	/*
+  	 *	We have emptied the queue
+  	 */
+  	 
+  	in_bh = 0;
+	sti();
+	
 	/*
-	 * That's odd.  We got an unknown packet.  Who's using
-	 * stuff like Novell or Amoeba on this network??
+	 *	One last output flush.
 	 */
-	if (!flag) {
-		DPRINTF((DBG_DEV,
-			"INET: unknown packet type 0x%04X (ignored)\n", type));
-		skb->sk = NULL;
-		kfree_skb(skb, FREE_WRITE);
-	}
-
-	/* Again, see if we can transmit anything now. */
+	 
 	dev_transmit();
-	cli();
-  }
-  in_bh = 0;
-  sti();
-  dev_transmit();
 }
 
 
 /*
- * This routine is called when an device driver (i.e. an
- * interface) is * ready to transmit a packet.
+ *	This routine is called when an device driver (i.e. an
+ *	interface) is ready to transmit a packet.
  */
  
 void dev_tint(struct device *dev)
@@ -713,13 +768,26 @@
 	int i;
 	struct sk_buff *skb;
 	
-	for(i = 0;i < DEV_NUMBUFFS; i++) {
+	/*
+	 *	Work the queues in priority order
+	 */
+	 
+	for(i = 0;i < DEV_NUMBUFFS; i++) 
+	{
+		/*
+		 *	Pull packets from the queue
+		 */
+		 
 		while((skb=skb_dequeue(&dev->buffs[i]))!=NULL)
 		{
-			skb->magic = 0;
-			skb->next = NULL;
-			skb->prev = NULL;
-			dev->queue_xmit(skb,dev,-i - 1);
+			/*
+			 *	Feed them to the output stage and if it fails
+			 *	indicate they re-queue at the front.
+			 */
+			dev_queue_xmit(skb,dev,-i - 1);
+			/*
+			 *	If we can take no more then stop here.
+			 */
 			if (dev->tbusy)
 				return;
 		}
@@ -727,59 +795,98 @@
 }
 
 
-/* Perform a SIOCGIFCONF call. */
-static int
-dev_ifconf(char *arg)
-{
-  struct ifconf ifc;
-  struct ifreq ifr;
-  struct device *dev;
-  char *pos;
-  int len;
-  int err;
-
-  /* Fetch the caller's info block. */
-  err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
-  if(err)
-  	return err;
-  memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
-  len = ifc.ifc_len;
-  pos = ifc.ifc_buf;
-  err=verify_area(VERIFY_WRITE, pos, len);
-  if(err)
-	return err;
-
-  /* Loop over the interfaces, and write an info block for each. */
-  for (dev = dev_base; dev != NULL; dev = dev->next) {
-        if(!(dev->flags & IFF_UP))
-        	continue;
-	memset(&ifr, 0, sizeof(struct ifreq));
-	strcpy(ifr.ifr_name, dev->name);
-	(*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
-	(*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
-
-	/* Write this block to the caller's space. */
-	memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
-	pos += sizeof(struct ifreq);
-	len -= sizeof(struct ifreq);
-	if (len < sizeof(struct ifreq)) break;
-  }
-
-  /* All done.  Write the updated control block back to the caller. */
-  ifc.ifc_len = (pos - ifc.ifc_buf);
-  ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
-  memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
-  return(pos - arg);
-}
+/*
+ *	Perform a SIOCGIFCONF call. This structure will change
+ *	size shortly, and there is nothing I can do about it.
+ *	Thus we will need a 'compatibility mode'.
+ */
 
-/* Print device statistics. */
-char *sprintf_stats(char *buffer, struct device *dev)
+static int dev_ifconf(char *arg)
 {
-  char *pos = buffer;
-  struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+	struct ifconf ifc;
+	struct ifreq ifr;
+	struct device *dev;
+	char *pos;
+	int len;
+	int err;
+
+	/*
+	 *	Fetch the caller's info block. 
+	 */
+	 
+	err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
+	if(err)
+	  	return err;
+	memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
+	len = ifc.ifc_len;
+	pos = ifc.ifc_buf;
+
+	/*
+	 *	We now walk the device list filling each active device
+	 *	into the array.
+	 */
+	 
+	err=verify_area(VERIFY_WRITE,pos,len);
+	if(err)
+	  	return err;
+  	
+	/*
+	 *	Loop over the interfaces, and write an info block for each. 
+	 */
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+        	if(!(dev->flags & IFF_UP))	/* Downed devices don't count */
+	        	continue;
+		memset(&ifr, 0, sizeof(struct ifreq));
+		strcpy(ifr.ifr_name, dev->name);
+		(*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
+		(*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+
+		/*
+		 *	Write this block to the caller's space. 
+		 */
+		 
+		memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
+		pos += sizeof(struct ifreq);
+		len -= sizeof(struct ifreq);
+		
+		/*
+		 *	Have we run out of space here ?
+		 */
+	
+		if (len < sizeof(struct ifreq)) 
+			break;
+  	}
+
+	/*
+	 *	All done.  Write the updated control block back to the caller. 
+	 */
+	 
+	ifc.ifc_len = (pos - ifc.ifc_buf);
+	ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
+	memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
+	
+	/*
+	 *	Report how much was filled in
+	 */
+	 
+	return(pos - arg);
+}
+
+
+/*
+ *	This is invoked by the /proc filesystem handler to display a device
+ *	in detail.
+ */
 
-  if (stats)
-    pos += sprintf(pos, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
+static int sprintf_stats(char *buffer, struct device *dev)
+{
+	struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+	int size;
+	
+	if (stats)
+		size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
 		   dev->name,
 		   stats->rx_packets, stats->rx_errors,
 		   stats->rx_dropped + stats->rx_missed_errors,
@@ -790,29 +897,61 @@
 		   stats->tx_fifo_errors, stats->collisions,
 		   stats->tx_carrier_errors + stats->tx_aborted_errors
 		   + stats->tx_window_errors + stats->tx_heartbeat_errors);
-  else
-      pos += sprintf(pos, "%6s: No statistics available.\n", dev->name);
+	else
+		size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
 
-  return pos;
+	return size;
 }
 
-/* Called from the PROCfs module. */
-int
-dev_get_info(char *buffer)
-{
-  char *pos = buffer;
-  struct device *dev;
-
-  pos +=
-      sprintf(pos,
-	      "Inter-|   Receive                  |  Transmit\n"
-	      " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
-  for (dev = dev_base; dev != NULL; dev = dev->next) {
-      pos = sprintf_stats(pos, dev);
-  }
-  return pos - buffer;
+/*
+ *	Called from the PROCfs module. This now uses the new arbitary sized /proc/net interface
+ *	to create /proc/net/dev
+ */
+ 
+int dev_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	int len=0;
+	off_t begin=0;
+	off_t pos=0;
+	int size;
+	
+	struct device *dev;
+
+
+	size = sprintf(buffer, "Inter-|   Receive                  |  Transmit\n"
+			    " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
+	
+	pos+=size;
+	len+=size;
+	
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+		size = sprintf_stats(buffer+len, dev);
+		len+=size;
+		pos=begin+len;
+				
+		if(pos<offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos>offset+length)
+			break;
+	}
+	
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Start slop */
+	if(len>length)
+		len=length;		/* Ending slop */
+	return len;
 }
 
+
+/*
+ *	This checks bitmasks for the ioctl calls for devices.
+ */
+ 
 static inline int bad_mask(unsigned long mask, unsigned long addr)
 {
 	if (addr & (mask = ~mask))
@@ -823,238 +962,344 @@
 	return 0;
 }
 
+/*
+ *	Perform the SIOCxIFxxx calls. 
+ *
+ *	The socket layer has seen an ioctl the address family thinks is
+ *	for the device. At this point we get invoked to make a decision
+ */
+ 
+static int dev_ifsioc(void *arg, unsigned int getset)
+{
+	struct ifreq ifr;
+	struct device *dev;
+	int ret;
+
+	/*
+	 *	Fetch the caller's info block into kernel space
+	 */
 
-/* Perform the SIOCxIFxxx calls. */
-static int
-dev_ifsioc(void *arg, unsigned int getset)
-{
-  struct ifreq ifr;
-  struct device *dev;
-  int ret;
-
-  /* Fetch the caller's info block. */
-  int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
-  if(err)
-  	return err;
-  memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
-
-  /* See which interface the caller is talking about. */
-  if ((dev = dev_get(ifr.ifr_name)) == NULL) return(-EINVAL);
-
-  switch(getset) {
-	case SIOCGIFFLAGS:
-		ifr.ifr_flags = dev->flags;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFFLAGS:
-		{
-		  int old_flags = dev->flags;
-		  dev->flags = ifr.ifr_flags & (
-			IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
-			IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
-			IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
-			
-		  if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0))
-		  	dev->set_multicast_list(dev,0,NULL);
-		  if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0))
-		  	dev->set_multicast_list(dev,-1,NULL);
-		  if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) {
-			ret = dev_close(dev);
-		  } else
-		  {
-		      ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP))
-			? dev_open(dev) : 0;
-		      if(ret<0)
-		      	dev->flags&=~IFF_UP;	/* Didnt open so down the if */
-		  }
-	        }
-		break;
-	case SIOCGIFADDR:
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_addr).sin_family = dev->family;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_addr).sin_port = 0;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFADDR:
-		dev->pa_addr = (*(struct sockaddr_in *)
+	int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
+	if(err)
+		return err;
+	
+	memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
+
+	/*
+	 *	See which interface the caller is talking about. 
+	 */
+	 
+	if ((dev = dev_get(ifr.ifr_name)) == NULL) 
+		return(-ENODEV);
+
+	switch(getset) 
+	{
+		case SIOCGIFFLAGS:	/* Get interface flags */
+			ifr.ifr_flags = dev->flags;
+			memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+		case SIOCSIFFLAGS:	/* Set interface flags */
+			{
+				int old_flags = dev->flags;
+				dev->flags = ifr.ifr_flags & (
+					IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
+					IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
+					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+				
+				/*
+				 *	Has promiscuous mode been turned off
+				 */	
+				if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0))
+			 		dev->set_multicast_list(dev,0,NULL);
+			 		
+			 	/*
+			 	 *	Has it been turned on
+			 	 */
+	
+				if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0))
+			  		dev->set_multicast_list(dev,-1,NULL);
+			  		
+			  	/*
+			  	 *	Have we downed the interface
+			  	 */
+		
+				if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) 
+				{
+					ret = dev_close(dev);
+				}
+				else
+				{
+					/*
+					 *	Have we upped the interface 
+					 */
+					 
+			      		ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP))
+						? dev_open(dev) : 0;
+					/* 
+					 *	Check the flags.
+					 */
+					if(ret<0)
+						dev->flags&=~IFF_UP;	/* Didnt open so down the if */
+			  	}
+	        	}
+			break;
+		
+		case SIOCGIFADDR:	/* Get interface address (and family) */
+			(*(struct sockaddr_in *)
+				  &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+			(*(struct sockaddr_in *)
+				  &ifr.ifr_addr).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				  &ifr.ifr_addr).sin_port = 0;
+			memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+	
+		case SIOCSIFADDR:	/* Set interface address (and family) */
+			dev->pa_addr = (*(struct sockaddr_in *)
 				 &ifr.ifr_addr).sin_addr.s_addr;
-		dev->family = ifr.ifr_addr.sa_family;
-		dev->pa_mask = get_mask(dev->pa_addr);
-		dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
-		ret = 0;
-		break;
-	case SIOCGIFBRDADDR:
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_broadaddr).sin_family = dev->family;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_broadaddr).sin_port = 0;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFBRDADDR:
-		dev->pa_brdaddr = (*(struct sockaddr_in *)
-				    &ifr.ifr_broadaddr).sin_addr.s_addr;
-		ret = 0;
-		break;
-	case SIOCGIFDSTADDR:
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_broadaddr).sin_family = dev->family;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_broadaddr).sin_port = 0;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFDSTADDR:
-		dev->pa_dstaddr = (*(struct sockaddr_in *)
-				    &ifr.ifr_dstaddr).sin_addr.s_addr;
-		ret = 0;
-		break;
-	case SIOCGIFNETMASK:
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_netmask).sin_family = dev->family;
-		(*(struct sockaddr_in *)
-		  &ifr.ifr_netmask).sin_port = 0;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFNETMASK: {
-		unsigned long mask = (*(struct sockaddr_in *)
-			&ifr.ifr_netmask).sin_addr.s_addr;
-		ret = -EINVAL;
-		if (bad_mask(mask,0))
-			break;
-		dev->pa_mask = mask;
-		ret = 0;
-		break;
+			dev->family = ifr.ifr_addr.sa_family;
+			dev->pa_mask = ip_get_mask(dev->pa_addr);
+			dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
+			ret = 0;
+			break;
+			
+		case SIOCGIFBRDADDR:	/* Get the broadcast address */
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_port = 0;
+			memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+
+		case SIOCSIFBRDADDR:	/* Set the broadcast address */
+			dev->pa_brdaddr = (*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_addr.s_addr;
+			ret = 0;
+			break;
+			
+		case SIOCGIFDSTADDR:	/* Get the destination address (for point-to-point links) */
+			(*(struct sockaddr_in *)
+				&ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_port = 0;
+				memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+	
+		case SIOCSIFDSTADDR:	/* Set the destination address (for point-to-point links) */
+			dev->pa_dstaddr = (*(struct sockaddr_in *)
+				&ifr.ifr_dstaddr).sin_addr.s_addr;
+			ret = 0;
+			break;
+			
+		case SIOCGIFNETMASK:	/* Get the netmask for the interface */
+			(*(struct sockaddr_in *)
+				&ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_netmask).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_netmask).sin_port = 0;
+			memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+
+		case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
+			{
+				unsigned long mask = (*(struct sockaddr_in *)
+					&ifr.ifr_netmask).sin_addr.s_addr;
+				ret = -EINVAL;
+				/*
+				 *	The mask we set must be legal.
+				 */
+				if (bad_mask(mask,0))
+					break;
+				dev->pa_mask = mask;
+				ret = 0;
+			}
+			break;
+			
+		case SIOCGIFMETRIC:	/* Get the metric on the inteface (currently unused) */
+			
+			ifr.ifr_metric = dev->metric;
+			memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+			
+		case SIOCSIFMETRIC:	/* Set the metric on the interface (currently unused) */
+			dev->metric = ifr.ifr_metric;
+			ret = 0;
+			break;
+	
+		case SIOCGIFMTU:	/* Get the MTU of a device */
+			ifr.ifr_mtu = dev->mtu;
+			memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+			ret = 0;
+			break;
+	
+		case SIOCSIFMTU:	/* Set the MTU of a device */
+		
+			/*
+			 *	MTU must be positive and under the page size problem
+			 */
+			 
+			if(ifr.ifr_mtu<1 || ifr.ifr_mtu>3800)
+				return -EINVAL;
+			dev->mtu = ifr.ifr_mtu;
+			ret = 0;
+			break;
+	
+		case SIOCGIFMEM:	/* Get the per device memory space. We can add this but currently
+					   do not support it */
+			printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg);
+			ret = -EINVAL;
+			break;
+		
+		case SIOCSIFMEM:	/* Set the per device memory buffer space. Not applicable in our case */
+			printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg);
+			ret = -EINVAL;
+			break;
+
+		case OLD_SIOCGIFHWADDR:	/* Get the hardware address. This will change and SIFHWADDR will be added */
+			memcpy(ifr.old_ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN);
+			memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+			ret=0;
+			break;
+
+		case SIOCGIFHWADDR:
+			memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+			ifr.ifr_hwaddr.sa_family=dev->type;			
+			memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+			ret=0;
+			break;
+			
+		case SIOCSIFHWADDR:
+			if(dev->set_mac_address==NULL)
+				return -EOPNOTSUPP;
+			if(ifr.ifr_hwaddr.sa_family!=dev->type)
+				return -EINVAL;
+			ret=dev->set_mac_address(dev,ifr.ifr_hwaddr.sa_data);
+			break;
+			
+		/*
+		 *	Unknown ioctl
+		 */
+
+		default:
+			ret = -EINVAL;
 	}
-	case SIOCGIFMETRIC:
-		ifr.ifr_metric = dev->metric;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFMETRIC:
-		dev->metric = ifr.ifr_metric;
-		ret = 0;
-		break;
-	case SIOCGIFMTU:
-		ifr.ifr_mtu = dev->mtu;
-		memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
-		ret = 0;
-		break;
-	case SIOCSIFMTU:
-		dev->mtu = ifr.ifr_mtu;
-		ret = 0;
-		break;
-	case SIOCGIFMEM:
-		printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg);
-		ret = -EINVAL;
-		break;
-	case SIOCSIFMEM:
-		printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg);
-		ret = -EINVAL;
-		break;
-	case SIOCGIFHWADDR:
-		memcpy(ifr.ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN);
-		memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
-		ret=0;
-		break;
-	default:
-		ret = -EINVAL;
-  }
-  return(ret);
+	return(ret);
 }
 
 
-/* This function handles all "interface"-type I/O control requests. */
-int
-dev_ioctl(unsigned int cmd, void *arg)
-{
-  struct iflink iflink;
-  struct ddi_device *dev;
-
-  switch(cmd) {
-	case IP_SET_DEV:
-		printk("Your network configuration program needs upgrading.\n");
-		return -EINVAL;
-
-	case SIOCGIFCONF:
-		(void) dev_ifconf((char *) arg);
-		return 0;
-
-	case SIOCGIFFLAGS:
-	case SIOCGIFADDR:
-	case SIOCGIFDSTADDR:
-	case SIOCGIFBRDADDR:
-	case SIOCGIFNETMASK:
-	case SIOCGIFMETRIC:
-	case SIOCGIFMTU:
-	case SIOCGIFMEM:
-	case SIOCGIFHWADDR:
-		return dev_ifsioc(arg, cmd);
-
-	case SIOCSIFFLAGS:
-	case SIOCSIFADDR:
-	case SIOCSIFDSTADDR:
-	case SIOCSIFBRDADDR:
-	case SIOCSIFNETMASK:
-	case SIOCSIFMETRIC:
-	case SIOCSIFMTU:
-	case SIOCSIFMEM:
-		if (!suser())
-			return -EPERM;
-		return dev_ifsioc(arg, cmd);
-
-	case SIOCSIFLINK:
-		if (!suser())
-			return -EPERM;
-		memcpy_fromfs(&iflink, arg, sizeof(iflink));
-		dev = ddi_map(iflink.id);
-		if (dev == NULL)
+/*
+ *	This function handles all "interface"-type I/O control requests. The actual
+ *	'doing' part of this is dev_ifsioc above.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
+{
+	switch(cmd) 
+	{
+		/*
+		 *	The old old setup ioctl. Even its name and this entry will soon be
+		 *	just so much ionization on a backup tape.
+		 */
+
+		case SIOCGIFCONF:
+			(void) dev_ifconf((char *) arg);
+			return 0;
+
+		/*
+		 *	Ioctl calls that can be done by all.
+		 */
+		 
+		case SIOCGIFFLAGS:
+		case SIOCGIFADDR:
+		case SIOCGIFDSTADDR:
+		case SIOCGIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCGIFMETRIC:
+		case SIOCGIFMTU:
+		case SIOCGIFMEM:
+		case SIOCGIFHWADDR:
+		case SIOCSIFHWADDR:
+		case OLD_SIOCGIFHWADDR:
+			return dev_ifsioc(arg, cmd);
+
+		/*
+		 *	Ioctl calls requiring the power of a superuser
+		 */
+		 
+		case SIOCSIFFLAGS:
+		case SIOCSIFADDR:
+		case SIOCSIFDSTADDR:
+		case SIOCSIFBRDADDR:
+		case SIOCSIFNETMASK:
+		case SIOCSIFMETRIC:
+		case SIOCSIFMTU:
+		case SIOCSIFMEM:
+			if (!suser())
+				return -EPERM;
+			return dev_ifsioc(arg, cmd);
+	
+		case SIOCSIFLINK:
 			return -EINVAL;
 
-		/* Now allocate an interface and connect it. */
-		printk("AF_INET: DDI \"%s\" linked to stream \"%s\"\n",
-						dev->name, iflink.stream);
-		return 0;
-
-	default:
-		return -EINVAL;
-  }
+		/*
+		 *	Unknown ioctl.
+		 */	
+		 
+		default:
+			return -EINVAL;
+	}
 }
 
 
-/* Initialize the DEV module. */
-void
-dev_init(void)
-{
-  struct device *dev, *dev2;
-
-  /* Add the devices.
-   * If the call to dev->init fails, the dev is removed
-   * from the chain disconnecting the device until the
-   * next reboot.
-   */
-  dev2 = NULL;
-  for (dev = dev_base; dev != NULL; dev=dev->next) {
-	if (dev->init && dev->init(dev)) {
-		if (dev2 == NULL) dev_base = dev->next;
-		  else dev2->next = dev->next;
-	} else {
-		dev2 = dev;
-	}
-  }
+/*
+ *	Initialize the DEV module. At boot time this walks the device list and
+ *	unhooks any devices that fail to initialise (normally hardware not 
+ *	present) and leaves us with a valid list of present and active devices.
+ *
+ *	The PCMICA code may need to change this a little, and add a pair
+ *	of register_inet_device() unregister_inet_device() calls. This will be
+ *	needed for ethernet as modules support.
+ */
+ 
+void dev_init(void)
+{
+	struct device *dev, *dev2;
 
-  /* Set up some IP addresses. */
-  ip_bcast = in_aton("255.255.255.255");
+	/*
+	 *	Add the devices.
+	 *	If the call to dev->init fails, the dev is removed
+	 *	from the chain disconnecting the device until the
+	 *	next reboot.
+	 */
+	 
+	dev2 = NULL;
+	for (dev = dev_base; dev != NULL; dev=dev->next) 
+	{
+		if (dev->init && dev->init(dev)) 
+		{
+			/*
+			 *	It failed to come up. Unhook it.
+			 */
+			 
+			if (dev2 == NULL) 
+				dev_base = dev->next;
+			else 
+				dev2->next = dev->next;
+		} 
+		else
+		{
+			dev2 = dev;
+		}
+	}
 }
diff -u --recursive --new-file v1.1.3/linux/net/inet/devinet.c linux/net/inet/devinet.c
--- v1.1.3/linux/net/inet/devinet.c
+++ linux/net/inet/devinet.c	Wed Apr 13 12:52:22 1994
@@ -0,0 +1,248 @@
+/*
+ *	NET3	IP device support routines.
+ *
+ *		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.
+ *
+ *	Derived from the IP parts of dev.c 1.0.19
+ * 		Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *				Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ *	Additional Authors:
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ */
+ 
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+
+/* 
+ *	Determine a default network mask, based on the IP address. 
+ */
+ 
+unsigned long ip_get_mask(unsigned long addr)
+{
+  	unsigned long dst;
+
+  	if (addr == 0L) 
+  		return(0L);	/* special case */
+
+  	dst = ntohl(addr);
+  	if (IN_CLASSA(dst)) 
+  		return(htonl(IN_CLASSA_NET));
+  	if (IN_CLASSB(dst)) 
+  		return(htonl(IN_CLASSB_NET));
+  	if (IN_CLASSC(dst)) 
+  		return(htonl(IN_CLASSC_NET));
+  
+  	/*
+  	 *	Something else, probably a multicast. 
+  	 */
+  	 
+  	return(0);
+}
+
+/*
+ *	Perform an IP address matching operation
+ */
+
+int ip_addr_match(unsigned long me, unsigned long him)
+{
+	int i;
+	unsigned long mask=0xFFFFFFFF;
+	DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
+	DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
+
+	/*
+	 *	Simple case
+	 */
+	if (me == him) 
+  		return(1);
+  		
+  	/*
+  	 *	Look for a match ending in all 1's 
+  	 */
+  	 
+	for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8) 
+	{
+		if ((me & 0xFF) != (him & 0xFF)) 
+		{
+		/*
+		 * The only way this could be a match is for
+		 * the rest of addr1 to be 0 or 255.
+		 */
+			if (me != 0 && me != mask) 
+				return(0);
+			return(1);
+		}
+	}
+  	return(1);
+}
+
+
+/* 
+ *	Check the address for our address, broadcasts, etc. 
+ *
+ *	I intend to fix this to at the very least cache the last
+ *	resolved entry.
+ */
+ 
+int ip_chk_addr(unsigned long addr)
+{
+	struct device *dev;
+	unsigned long mask;
+
+	/* 
+	 *	Accept both `all ones' and `all zeros' as BROADCAST. 
+	 *	(Support old BSD in other words). This old BSD 
+	 *	support will go very soon as it messes other things
+	 *	up.
+	 */
+
+	if (addr == INADDR_ANY || addr == INADDR_BROADCAST)
+		return IS_BROADCAST;
+
+	mask = ip_get_mask(addr);
+
+	/*
+	 *	Accept all of the `loopback' class A net. 
+	 */
+	 
+	if ((addr & mask) == htonl(0x7F000000L))
+		return IS_MYADDR;
+
+	/*
+	 *	OK, now check the interface addresses. 
+	 */
+	 
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+		if (!(dev->flags & IFF_UP))
+			continue;
+		/*
+		 *	If the protocol address of the device is 0 this is special
+		 *	and means we are address hunting (eg bootp).
+		 */
+		 
+		if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/)
+			return IS_MYADDR;
+		/*
+		 *	Is it the exact IP address? 
+		 */
+		 
+		if (addr == dev->pa_addr)
+			return IS_MYADDR;
+		/*
+		 *	Is it our broadcast address? 
+		 */
+		 
+		if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
+			return IS_BROADCAST;
+		/*
+		 *	Nope. Check for a subnetwork broadcast. 
+		 */
+		 
+		if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0) 
+		{
+			if ((addr & ~dev->pa_mask) == 0)
+				return IS_BROADCAST;
+			if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
+				return IS_BROADCAST;
+		}
+		
+		/*
+	 	 *	Nope. Check for Network broadcast. 
+	 	 */
+	 	 
+		if (((addr ^ dev->pa_addr) & mask) == 0) 
+		{
+			if ((addr & ~mask) == 0)
+				return IS_BROADCAST;
+			if ((addr & ~mask) == ~mask)
+				return IS_BROADCAST;
+		}
+	}
+	return 0;		/* no match at all */
+}
+
+
+/*
+ *	Retrieve our own address.
+ *
+ *	Because the loopback address (127.0.0.1) is already recognized
+ *	automatically, we can use the loopback interface's address as
+ *	our "primary" interface.  This is the addressed used by IP et
+ *	al when it doesn't know which address to use (i.e. it does not
+ *	yet know from or to which interface to go...).
+ */
+ 
+unsigned long ip_my_addr(void)
+{
+  	struct device *dev;
+
+  	for (dev = dev_base; dev != NULL; dev = dev->next) 
+  	{
+		if (dev->flags & IFF_LOOPBACK) 
+			return(dev->pa_addr);
+  	}
+  	return(0);
+}
+
+/*
+ *	Find an interface that can handle addresses for a certain address. 
+ *
+ *	This needs optimising, since its relatively trivial to collapse
+ *	the two loops into one.
+ */
+ 
+struct device * ip_dev_check(unsigned long addr)
+{
+	struct device *dev;
+
+	for (dev = dev_base; dev; dev = dev->next) 
+	{
+		if (!(dev->flags & IFF_UP))
+			continue;
+		if (!(dev->flags & IFF_POINTOPOINT))
+			continue;
+		if (addr != dev->pa_dstaddr)
+			continue;
+		return dev;
+	}
+	for (dev = dev_base; dev; dev = dev->next) 
+	{
+		if (!(dev->flags & IFF_UP))
+			continue;
+		if (dev->flags & IFF_POINTOPOINT)
+			continue;
+		if (dev->pa_mask & (addr ^ dev->pa_addr))
+			continue;
+		return dev;
+	}
+	return NULL;
+}
diff -u --recursive --new-file v1.1.3/linux/net/inet/eth.c linux/net/inet/eth.c
--- v1.1.3/linux/net/inet/eth.c	Wed Jan 19 13:24:38 1994
+++ linux/net/inet/eth.c	Wed Apr 13 12:52:22 1994
@@ -10,6 +10,8 @@
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *		Florian  La Roche, <rzsfl@rz.uni-sb.de>
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  * 
  * Fixes:
  *		Mr Linux	: Arp problems
@@ -18,6 +20,9 @@
  *		Alan Cox	: eth_rebuild_header missing an htons and
  *				  minor other things.
  *		Tegge		: Arp bug fixes. 
+ *		Florian		: Removed many unnecessary functions, code cleanup
+ *				  and changes for new arp and skbuff.
+ *		Alan Cox	: Redid header building to reflect new format.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -33,40 +38,23 @@
 #include <linux/mm.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 #include <linux/errno.h>
 #include "arp.h"
 
-
-/* Display an Ethernet address in readable format. */
-char *eth_print(unsigned char *ptr)
-{
-  static char buff[64];
-
-  if (ptr == NULL) return("[NONE]");
-  sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X",
-	(ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255),
-	(ptr[3] & 255), (ptr[4] & 255), (ptr[5] & 255)
-  );
-  return(buff);
-}
-
 void eth_setup(char *str, int *ints)
 {
 	struct device *d = dev_base;
 
 	if (!str || !*str)
 		return;
-	while (d) {
-		if (!strcmp(str,d->name)) {
+	while (d) 
+	{
+		if (!strcmp(str,d->name)) 
+		{
 			if (ints[0] > 0)
 				d->irq=ints[1];
 			if (ints[0] > 1)
@@ -81,107 +69,102 @@
 	}
 }
 
-/* Display the contents of the Ethernet MAC header. */
-void
-eth_dump(struct ethhdr *eth)
-{
-  if (inet_debug != DBG_ETH) return;
-
-  printk("eth: SRC = %s ", eth_print(eth->h_source));
-  printk("DST = %s ", eth_print(eth->h_dest));
-  printk("TYPE = %04X\n", ntohs(eth->h_proto));
-}
-
-
-/* Create the Ethernet MAC header. */
-int
-eth_header(unsigned char *buff, struct device *dev, unsigned short type,
-	   unsigned long daddr, unsigned long saddr, unsigned len)
-{
-  struct ethhdr *eth;
-
-  DPRINTF((DBG_DEV, "ETH: header(%s, ", in_ntoa(saddr)));
-  DPRINTF((DBG_DEV, "%s, 0x%X)\n", in_ntoa(daddr), type));
-
-  /* Fill in the basic Ethernet MAC header. */
-  eth = (struct ethhdr *) buff;
-  eth->h_proto = htons(type);
-
-  /* We don't ARP for the LOOPBACK device... */
-  if (dev->flags & IFF_LOOPBACK) {
-	DPRINTF((DBG_DEV, "ETH: No header for loopback\n"));
-	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-	memset(eth->h_dest, 0, dev->addr_len);
-	return(dev->hard_header_len);
-  }
-
-  /* Check if we can use the MAC BROADCAST address. */
-  if (chk_addr(daddr) == IS_BROADCAST) {
-	DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n"));
-	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-	memcpy(eth->h_dest, dev->broadcast, dev->addr_len);
-	return(dev->hard_header_len);
-  }
-  cli();
-  memcpy(eth->h_source, &saddr, 4);
-  /* No. Ask ARP to resolve the Ethernet address. */
-  if (arp_find(eth->h_dest, daddr, dev, dev->pa_addr)) 
-  {
-        sti();
-        if(type!=ETH_P_IP)
-        	printk("Erk: protocol %X got into an arp request state!\n",type);
-	return(-dev->hard_header_len);
-  } 
-  else
-  {
-  	memcpy(eth->h_source,dev->dev_addr,dev->addr_len);	/* This was missing causing chaos if the
-  								   header built correctly! */
-  	sti();
-  	return(dev->hard_header_len);
-  }
-}
-
 
-/* Rebuild the Ethernet MAC header. */
-int
-eth_rebuild_header(void *buff, struct device *dev)
-{
-  struct ethhdr *eth;
-  unsigned long src, dst;
+/*
+ *	 Create the Ethernet MAC header for an arbitary protocol layer 
+ *
+ *	saddr=NULL	means use device source address
+ *	daddr=NULL	means leave destination address (eg unresolved arp)
+ */
 
-  DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n"));
-  eth = (struct ethhdr *) buff;
-  src = *(unsigned long *) eth->h_source;
-  dst = *(unsigned long *) eth->h_dest;
-  DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src)));
-  DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst)));
-  if(eth->h_proto!=htons(ETH_P_ARP))	/* This ntohs kind of helps a bit! */
-	  if (arp_find(eth->h_dest, dst, dev, dev->pa_addr /* src */)) return(1);
-  memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-  return(0);
+int eth_header(unsigned char *buff, struct device *dev, unsigned short type,
+	   void *daddr, void *saddr, unsigned len,
+	   struct sk_buff *skb)
+{
+	struct ethhdr *eth = (struct ethhdr *)buff;
+
+	/* 
+	 *	Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+	 *	in here instead. It is up to the 802.2 layer to carry protocol information.
+	 */
+	
+	if(type!=ETH_P_802_3) 
+		eth->h_proto = htons(type);
+	else
+		eth->h_proto = htons(len);
+
+	/*
+	 *	Set the source hardware address. 
+	 */
+	 
+	if(saddr)
+		memcpy(eth->h_source,saddr,dev->addr_len);
+	else
+		memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+	/*
+	 *	Anyway, the loopback-device should never use this function... 
+	 */
+
+	if (dev->flags & IFF_LOOPBACK) 
+	{
+		memset(eth->h_dest, 0, dev->addr_len);
+		return(dev->hard_header_len);
+	}
+	
+	if(daddr)
+	{
+		memcpy(eth->h_dest,daddr,dev->addr_len);
+		return dev->hard_header_len;
+	}
+	
+	return -dev->hard_header_len;
 }
 
 
-/* Add an ARP entry for a host on this interface. */
-void
-eth_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
-{
-  struct ethhdr *eth;
+/*
+ *	Rebuild the Ethernet MAC header. This is called after an ARP
+ *	(or in future other address resolution) has completed on this
+ *	sk_buff. We now let ARP fill in the other fields.
+ */
+ 
+int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+			struct sk_buff *skb)
+{
+	struct ethhdr *eth = (struct ethhdr *)buff;
+
+	/*
+	 *	Only ARP/IP is currently supported
+	 */
+	 
+	if(eth->h_proto != htons(ETH_P_IP)) 
+	{
+		printk("eth_rebuild_header: Don't know how to resolve type %d addreses?\n",(int)eth->h_proto);
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+		return 0;
+	}
 
-  eth = (struct ethhdr *) skb->data;
-  arp_add(addr, eth->h_source, dev);
+	/*
+	 *	Try and get ARP to resolve the header.
+	 */
+	 
+	return arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
 }
 
 
-/* Determine the packet's protocol ID. */
-unsigned short
-eth_type_trans(struct sk_buff *skb, struct device *dev)
+/*
+ *	Determine the packet's protocol ID. The rule here is that we 
+ *	assume 802.3 if the type field is short enough to be a length.
+ *	This is normal practice and works for any 'now in use' protocol.
+ */
+ 
+unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
 {
-  struct ethhdr *eth;
+	struct ethhdr *eth = (struct ethhdr *) skb->data;
 
-  eth = (struct ethhdr *) skb->data;
+	if (ntohs(eth->h_proto) < 1536)
+		return htons(ETH_P_802_3);
 
-  if(ntohs(eth->h_proto)<1536)
-  	return(htons(ETH_P_802_3));
-  return(eth->h_proto);
+	return eth->h_proto;
 }
+
diff -u --recursive --new-file v1.1.3/linux/net/inet/icmp.c linux/net/inet/icmp.c
--- v1.1.3/linux/net/inet/icmp.c	Wed Apr 13 11:33:58 1994
+++ linux/net/inet/icmp.c	Wed Apr 13 12:52:23 1994
@@ -10,16 +10,25 @@
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  *
  * Fixes:	
  *		Alan Cox	:	Generic queue usage.
  *		Gerhard Koerting:	ICMP addressing corrected
  *		Alan Cox	:	Use tos/ttl settings
+ *		Alan Cox	:	Protocol violations
+ *		Alan Cox	:	SNMP Statistics		
+ *		Alan Cox	:	Routing errors
  *
+ * 
+ *	FIXME:
+ *		When 1.0.6 is out merge in the NET channel diffs for TIMESTAMP
  *
+ *
  *		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
+#include <linux/string.h>
  *		2 of the License, or (at your option) any later version.
  */
 #include <linux/types.h>
@@ -28,15 +37,16 @@
 #include <linux/fcntl.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include <linux/string.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "snmp.h"
 #include "ip.h"
 #include "route.h"
 #include "protocol.h"
 #include "icmp.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include "snmp.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include <linux/errno.h>
 #include <linux/timer.h>
@@ -47,6 +57,13 @@
 #define min(a,b)	((a)<(b)?(a):(b))
 
 
+/*
+ *	Statistics
+ */
+ 
+struct icmp_mib	icmp_statistics={0,};
+
+
 /* An array of errno for error messages from dest unreach. */
 struct icmp_err icmp_err_convert[] = {
   { ENETUNREACH,	1 },	/*	ICMP_NET_UNREACH	*/
@@ -65,446 +82,646 @@
 };
 
 
-/* Display the contents of an ICMP header. */
-static void
-print_icmp(struct icmphdr *icmph)
+/* 
+ *	Display the contents of an ICMP header. 
+ */
+ 
+static void print_icmp(struct icmphdr *icmph)
 {
-  if (inet_debug != DBG_ICMP) return;
+  	if (inet_debug != DBG_ICMP) 
+  		return;
 
-  printk("ICMP: type = %d, code = %d, checksum = %X\n",
+  	printk("ICMP: type = %d, code = %d, checksum = %X\n",
 			icmph->type, icmph->code, icmph->checksum);
-  printk("      gateway = %s\n", in_ntoa(icmph->un.gateway));
+  	printk("      gateway = %s\n", in_ntoa(icmph->un.gateway));
 }
 
 
-/* Send an ICMP message. */
-void
-icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
+/*
+ *	Send an ICMP message in response to a situation
+ *
+ *	Fixme: Fragment handling is wrong really.
+ */
+ 
+void icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
 {
-  struct sk_buff *skb;
-  struct iphdr *iph;
-  int offset;
-  struct icmphdr *icmph;
-  int len;
+	struct sk_buff *skb;
+	struct iphdr *iph;
+	int offset;
+	struct icmphdr *icmph;
+	int len;
+	struct device *ndev=NULL;	/* Make this =dev to force replies on the same interface */
 
-  DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
+	DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
 	   					skb_in, type, code, dev));
 
-  /* Get some memory for the reply. */
-  len = sizeof(struct sk_buff) + dev->hard_header_len +
-	sizeof(struct iphdr) + sizeof(struct icmphdr) +
-	sizeof(struct iphdr) + 8;	/* amount of header to return */
+	/*
+	 *	Find the original IP header.
+	 */
+	 
+	iph = (struct iphdr *) (skb_in->data + dev->hard_header_len);
+	
+	/*
+	 *	We must NEVER NEVER send an ICMP error to an ICMP error message
+	 */
+	 
+	if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED)
+	{
+		if(iph->protocol==IPPROTO_ICMP)
+			return;
+
+	}
+	icmp_statistics.IcmpOutMsgs++;
+	
+	/*
+	 *	This needs a tidy.	
+	 */
+		
+	switch(type)
+	{
+		case ICMP_DEST_UNREACH:
+			icmp_statistics.IcmpOutDestUnreachs++;
+			break;
+		case ICMP_SOURCE_QUENCH:
+			icmp_statistics.IcmpOutSrcQuenchs++;
+			break;
+		case ICMP_REDIRECT:
+			icmp_statistics.IcmpOutRedirects++;
+			break;
+		case ICMP_ECHO:
+			icmp_statistics.IcmpOutEchos++;
+			break;
+		case ICMP_ECHOREPLY:
+			icmp_statistics.IcmpOutEchoReps++;
+			break;
+		case ICMP_TIME_EXCEEDED:
+			icmp_statistics.IcmpOutTimeExcds++;
+			break;
+		case ICMP_PARAMETERPROB:
+			icmp_statistics.IcmpOutParmProbs++;
+			break;
+		case ICMP_TIMESTAMP:
+			icmp_statistics.IcmpOutTimestamps++;
+			break;
+		case ICMP_TIMESTAMPREPLY:
+			icmp_statistics.IcmpOutTimestampReps++;
+			break;
+		case ICMP_ADDRESS:
+			icmp_statistics.IcmpOutAddrMasks++;
+			break;
+		case ICMP_ADDRESSREPLY:
+			icmp_statistics.IcmpOutAddrMaskReps++;
+			break;
+	}		
+	/*
+	 *	Get some memory for the reply. 
+	 */
+	 
+	len = dev->hard_header_len + sizeof(struct iphdr) + sizeof(struct icmphdr) +
+		sizeof(struct iphdr) + 8;	/* amount of header to return */
 	   
-  skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);
-  if (skb == NULL) 
-  	return;
-
-  skb->sk = NULL;
-  skb->mem_addr = skb;
-  skb->mem_len = len;
-  len -= sizeof(struct sk_buff);
-
-  /* Find the IP header. */
-  iph = (struct iphdr *) (skb_in->data + dev->hard_header_len);
-
-  /* Build Layer 2-3 headers for message back to source. */
-  offset = ip_build_header(skb, dev->pa_addr, iph->saddr,
-			   &dev, IPPROTO_ICMP, NULL, len, skb_in->ip_hdr->tos,255);
-  if (offset < 0) {
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_READ);
-	return;
-  }
+	skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC);
+	if (skb == NULL) 
+	{
+		icmp_statistics.IcmpOutErrors++;
+		return;
+	}
+
+	/*
+	 *	Build Layer 2-3 headers for message back to source. 
+	 */
+
+	offset = ip_build_header(skb, dev->pa_addr, iph->saddr,
+			   &ndev, IPPROTO_ICMP, NULL, len, skb_in->ip_hdr->tos,255);
+
+	if (offset < 0) 
+	{
+		icmp_statistics.IcmpOutErrors++;
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
 
-  /* Re-adjust length according to actual IP header size. */
-  skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;
-  icmph = (struct icmphdr *) (skb->data + offset);
-  icmph->type = type;
-  icmph->code = code;
-  icmph->checksum = 0;
-  icmph->un.gateway = 0;
-  memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
+	/* 
+	 *	Re-adjust length according to actual IP header size. 
+	 */
+
+	skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;
+	
+	/*
+	 *	Fill in the frame
+	 */
+	 
+	icmph = (struct icmphdr *) (skb->data + offset);
+	icmph->type = type;
+	icmph->code = code;
+	icmph->checksum = 0;
+	icmph->un.gateway = 0;
+	memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
 
-  icmph->checksum = ip_compute_csum((unsigned char *)icmph,
+	icmph->checksum = ip_compute_csum((unsigned char *)icmph,
                          sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);
 
-  DPRINTF((DBG_ICMP, ">>\n"));
-  print_icmp(icmph);
+	DPRINTF((DBG_ICMP, ">>\n"));
+	print_icmp(icmph);
 
-  /* Send it and free it. */
-  ip_queue_xmit(NULL, dev, skb, 1);
+	/*
+	 *	Send it and free it once sent.
+	 */
+	ip_queue_xmit(NULL, dev, skb, 1);
 }
 
 
-/* Handle ICMP_UNREACH and ICMP_QUENCH. */
-static void
-icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
+/* 
+ *	Handle ICMP_UNREACH and ICMP_QUENCH. 
+ */
+ 
+static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
 {
-  struct inet_protocol *ipprot;
-  struct iphdr *iph;
-  unsigned char hash;
-  int err;
-
-  err = (icmph->type << 8) | icmph->code;
-  iph = (struct iphdr *) (icmph + 1);
-  switch(icmph->code & 7) {
-	case ICMP_NET_UNREACH:
-		DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
+	struct inet_protocol *ipprot;
+	struct iphdr *iph;
+	unsigned char hash;
+	int err;
+
+	err = (icmph->type << 8) | icmph->code;
+	iph = (struct iphdr *) (icmph + 1);
+	
+	switch(icmph->code & 7) 
+	{
+		case ICMP_NET_UNREACH:
+			DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
+								in_ntoa(iph->daddr)));
+			break;
+		case ICMP_HOST_UNREACH:
+			DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
 							in_ntoa(iph->daddr)));
-		break;
-	case ICMP_HOST_UNREACH:
-		DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
-						in_ntoa(iph->daddr)));
-		break;
-	case ICMP_PROT_UNREACH:
-		printk("ICMP: %s:%d: protocol unreachable.\n",
-			in_ntoa(iph->daddr), ntohs(iph->protocol));
-		break;
-	case ICMP_PORT_UNREACH:
-		DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
-			in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
-		break;
-	case ICMP_FRAG_NEEDED:
-		printk("ICMP: %s: fragmentation needed and DF set.\n",
-							in_ntoa(iph->daddr));
-		break;
-	case ICMP_SR_FAILED:
-		printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
-		break;
-	default:
-		DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
-		    		(icmph->code & 7), in_ntoa(iph->daddr)));
-		break;
-  }
-
-  /* Get the protocol(s). */
-  hash = iph->protocol & (MAX_INET_PROTOS -1);
-
-  /* This can change while we are doing it. */
-  ipprot = (struct inet_protocol *) inet_protos[hash];
-  while(ipprot != NULL) {
-	struct inet_protocol *nextip;
-
-	nextip = (struct inet_protocol *) ipprot->next;
-
-	/* Pass it off to everyone who wants it. */
-	if (iph->protocol == ipprot->protocol && ipprot->err_handler) {
-		ipprot->err_handler(err, (unsigned char *)(icmph + 1),
-				    iph->daddr, iph->saddr, ipprot);
+			break;
+		case ICMP_PROT_UNREACH:
+			printk("ICMP: %s:%d: protocol unreachable.\n",
+				in_ntoa(iph->daddr), ntohs(iph->protocol));
+			break;
+		case ICMP_PORT_UNREACH:
+			DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
+				in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
+			break;
+		case ICMP_FRAG_NEEDED:
+			printk("ICMP: %s: fragmentation needed and DF set.\n",
+								in_ntoa(iph->daddr));
+			break;
+		case ICMP_SR_FAILED:
+			printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+			break;
+		default:
+			DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
+			    		(icmph->code & 7), in_ntoa(iph->daddr)));
+			break;
 	}
 
-	ipprot = nextip;
-  }
-  skb->sk = NULL;
-  kfree_skb(skb, FREE_READ);
+	/*
+	 *	Get the protocol(s). 
+	 */
+	 
+	hash = iph->protocol & (MAX_INET_PROTOS -1);
+
+	/*
+	 *	This can't change while we are doing it. 
+	 */
+	 
+	ipprot = (struct inet_protocol *) inet_protos[hash];
+	while(ipprot != NULL) 
+	{
+		struct inet_protocol *nextip;
+
+		nextip = (struct inet_protocol *) ipprot->next;
+	
+		/* 
+		 *	Pass it off to everyone who wants it. 
+		 */
+		if (iph->protocol == ipprot->protocol && ipprot->err_handler) 
+		{
+			ipprot->err_handler(err, (unsigned char *)(icmph + 1),
+					    iph->daddr, iph->saddr, ipprot);
+		}
+
+		ipprot = nextip;
+  	}
+	kfree_skb(skb, FREE_READ);
 }
 
 
-/* Handle ICMP_REDIRECT. */
-static void
-icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev)
+/*
+ *	Handle ICMP_REDIRECT. 
+ */
+
+static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev)
 {
-  struct iphdr *iph;
-  unsigned long ip;
+	struct iphdr *iph;
+	unsigned long ip;
 
-  iph = (struct iphdr *) (icmph + 1);
-  ip = iph->daddr;
-  switch(icmph->code & 7) {
-	case ICMP_REDIR_NET:
+	/*
+	 *	Get the copied header of the packet that caused the redirect
+	 */
+	 
+	iph = (struct iphdr *) (icmph + 1);
+	ip = iph->daddr;
+
+	switch(icmph->code & 7) 
+	{
+		case ICMP_REDIR_NET:
+			/*
+			 *	This causes a problem with subnetted networks. What we should do
+			 *	is use ICMP_ADDRESS to get the subnet mask of the problem route
+			 *	and set both. But we don't..
+			 */
 #ifdef not_a_good_idea
-		rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
-			ip, 0, icmph->un.gateway, dev);
-		break;
+			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
+				ip, 0, icmph->un.gateway, dev);
+			break;
 #endif
-	case ICMP_REDIR_HOST:
-		rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
-			ip, 0, icmph->un.gateway, dev);
-		break;
-	case ICMP_REDIR_NETTOS:
-	case ICMP_REDIR_HOSTTOS:
-		printk("ICMP: cannot handle TOS redirects yet!\n");
-		break;
-	default:
-		DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
+		case ICMP_REDIR_HOST:
+			/*
+			 *	Add better route to host
+			 */
+			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
+				ip, 0, icmph->un.gateway, dev);
+			break;
+		case ICMP_REDIR_NETTOS:
+		case ICMP_REDIR_HOSTTOS:
+			printk("ICMP: cannot handle TOS redirects yet!\n");
+			break;
+		default:
+			DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
 						(icmph->code & 7)));
 		break;
-  }
-  skb->sk = NULL;
-  kfree_skb(skb, FREE_READ);
+  	}
+  	
+  	/*
+  	 *	Discard the original packet
+  	 */
+  	 
+  	kfree_skb(skb, FREE_READ);
 }
 
 
-/* Handle ICMP_ECHO ("ping") requests. */
-static void
-icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ *	Handle ICMP_ECHO ("ping") requests. 
+ */
+ 
+static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
 	  unsigned long saddr, unsigned long daddr, int len,
 	  struct options *opt)
 {
-  struct icmphdr *icmphr;
-  struct sk_buff *skb2;
-  int size, offset;
-
-  size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
-  skb2 = alloc_skb(size, GFP_ATOMIC);
-  if (skb2 == NULL) {
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_READ);
-	return;
-  }
-  skb2->sk = NULL;
-  skb2->mem_addr = skb2;
-  skb2->mem_len = size;
-  skb2->free = 1;
-
-  /* Build Layer 2-3 headers for message back to source */
-  offset = ip_build_header(skb2, daddr, saddr, &dev,
-			 	IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
-  if (offset < 0) {
-	printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
-	kfree_skb(skb2,FREE_WRITE);
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_READ);
-	return;
-  }
-
-  /* Re-adjust length according to actual IP header size. */
-  skb2->len = offset + len;
-
-  /* Build ICMP_ECHO Response message. */
-  icmphr = (struct icmphdr *) (skb2->data + offset);
-  memcpy((char *) icmphr, (char *) icmph, len);
-  icmphr->type = ICMP_ECHOREPLY;
-  icmphr->code = 0;
-  icmphr->checksum = 0;
-  icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+	struct icmphdr *icmphr;
+	struct sk_buff *skb2;
+	struct device *ndev=NULL;
+	int size, offset;
+
+	icmp_statistics.IcmpOutEchoReps++;
+	icmp_statistics.IcmpOutMsgs++;
+	
+	size = dev->hard_header_len + 64 + len;
+	skb2 = alloc_skb(size, GFP_ATOMIC);
+
+	if (skb2 == NULL) 
+	{
+		icmp_statistics.IcmpOutErrors++;
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+	skb2->free = 1;
 
-  /* Ship it out - free it when done */
-  ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+	/* Build Layer 2-3 headers for message back to source */
+	offset = ip_build_header(skb2, daddr, saddr, &ndev,
+	 	IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
+	if (offset < 0) 
+	{
+		icmp_statistics.IcmpOutErrors++;
+		printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
+		kfree_skb(skb2,FREE_WRITE);
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
 
-  skb->sk = NULL;
-  kfree_skb(skb, FREE_READ);
+	/*
+	 *	Re-adjust length according to actual IP header size. 
+	 */
+	 
+	skb2->len = offset + len;
+
+	/*
+	 *	Build ICMP_ECHO Response message. 
+	 */
+	icmphr = (struct icmphdr *) (skb2->data + offset);
+	memcpy((char *) icmphr, (char *) icmph, len);
+	icmphr->type = ICMP_ECHOREPLY;
+	icmphr->code = 0;
+	icmphr->checksum = 0;
+	icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+
+	/*
+	 *	Ship it out - free it when done 
+	 */
+	ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+
+	/*
+	 *	Free the received frame
+	 */
+	 
+	kfree_skb(skb, FREE_READ);
 }
 
-
-/* Handle ICMP Timestamp requests. */
-static void
-icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ *	Handle ICMP Timestamp requests. 
+ */
+ 
+static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
 	  unsigned long saddr, unsigned long daddr, int len,
 	  struct options *opt)
 {
-  struct icmphdr *icmphr;
-  struct sk_buff *skb2;
-  int size, offset;
-  unsigned long *timeptr, midtime;
-  extern struct timeval xtime;			/* kernel/time.c */
-
-  size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
-  if (! (skb2 = alloc_skb(size, GFP_ATOMIC))) {
-    skb->sk = NULL;
-    kfree_skb(skb, FREE_READ);
-    return;
-  }
-  skb2->sk = NULL;
-  skb2->mem_addr = skb2;
-  skb2->mem_len = size;
-  skb2->free = 1;
-
-  /* Build Layer 2-3 headers for message back to source */
-  offset = ip_build_header(skb2, daddr, saddr, &dev, IPPROTO_ICMP, opt, len, 
+	struct icmphdr *icmphr;
+	struct sk_buff *skb2;
+	int size, offset;
+	unsigned long *timeptr, midtime;
+	extern struct timeval xtime;			/* kernel/time.c */
+	struct device *ndev=NULL;
+ 
+	size = dev->hard_header_len + 64 + len;
+
+	if (! (skb2 = alloc_skb(size, GFP_ATOMIC))) 
+	{
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+		icmp_statistics.IcmpOutErrors++;		
+		return;
+	}
+	skb2->free = 1;
+ 
+/*
+ *	Build Layer 2-3 headers for message back to source 
+ */
+ 
+	offset = ip_build_header(skb2, daddr, saddr, &ndev, IPPROTO_ICMP, opt, len, 
 				skb->ip_hdr->tos, 255);
-  if (offset < 0) {
-    printk("ICMP: Could not build IP Header for ICMP TIMESTAMP Response\n");
-    kfree_skb(skb2, FREE_WRITE);
-    skb->sk = NULL;
-    kfree_skb(skb, FREE_READ);
-    return;
-  }
-
-  /* Re-adjust length according to actual IP header size. */
-  skb2->len = offset + len;
-
-  /* Build ICMP_TIMESTAMP Response message. */
-  icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
-  memcpy((char *) icmphr, (char *) icmph, len);
-  icmphr->type = ICMP_TIMESTAMPREPLY;
-  icmphr->code = icmphr->checksum = 0;
-
-  /* fill in the current time as ms since midnight UT: */
-  midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000;
-  timeptr = (unsigned long *) (icmphr + 1);
-  /* the originate timestamp (timeptr [0]) is still in the copy: */
-  timeptr [1] = timeptr [2] = htonl(midtime);
-
-  icmphr->checksum = ip_compute_csum((unsigned char *) icmphr, len);
-
-  /* Ship it out - free it when done */
-  ip_queue_xmit((struct sock *) NULL, dev, skb2, 1);
+	if (offset < 0) 
+	{
+		printk("ICMP: Could not build IP Header for ICMP TIMESTAMP Response\n");
+		kfree_skb(skb2, FREE_WRITE);
+		kfree_skb(skb, FREE_READ);
+		icmp_statistics.IcmpOutErrors++;
+		return;
+	}
+ 
+	/*
+	 *	Re-adjust length according to actual IP header size. 
+	 */
+	skb2->len = offset + len;
+ 
+	/*
+	 *	Build ICMP_TIMESTAMP Response message. 
+	 */
+
+	icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
+	memcpy((char *) icmphr, (char *) icmph, len);
+	icmphr->type = ICMP_TIMESTAMPREPLY;
+	icmphr->code = icmphr->checksum = 0;
+
+	/* fill in the current time as ms since midnight UT: */
+	midtime = (xtime.tv_sec % 86400) * 1000 + xtime.tv_usec / 1000;
+	timeptr = (unsigned long *) (icmphr + 1);
+	/*
+	 *	the originate timestamp (timeptr [0]) is still in the copy: 
+	 */
+	timeptr [1] = timeptr [2] = htonl(midtime);
+
+	icmphr->checksum = ip_compute_csum((unsigned char *) icmphr, len);
+
+	/*
+	 *	Ship it out - free it when done 
+	 */
 
-  skb->sk = NULL;
-  kfree_skb(skb, FREE_READ);
+	ip_queue_xmit((struct sock *) NULL, dev, skb2, 1);
+	icmp_statistics.IcmpOutTimestampReps++;
+	kfree_skb(skb, FREE_READ);
 }
+ 
+ 
 
 
-/* Handle the ICMP INFORMATION REQUEST. */
-static void
-icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/*
+ *	Handle the ICMP INFORMATION REQUEST. 
+ */
+ 
+static void icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
 	  unsigned long saddr, unsigned long daddr, int len,
 	  struct options *opt)
 {
-  /* NOT YET */
-  skb->sk = NULL;
-  kfree_skb(skb, FREE_READ);
+	/* Obsolete */
+	kfree_skb(skb, FREE_READ);
 }
 
 
-/* Handle ICMP_ADRESS_MASK requests. */
-static void
-icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+/* 
+ *	Handle ICMP_ADRESS_MASK requests. 
+ */
+ 
+static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
 	  unsigned long saddr, unsigned long daddr, int len,
 	  struct options *opt)
 {
-  struct icmphdr *icmphr;
-  struct sk_buff *skb2;
-  int size, offset;
-
-  size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
-  skb2 = alloc_skb(size, GFP_ATOMIC);
-  if (skb2 == NULL) {
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_READ);
-	return;
-  }
-  skb2->sk = NULL;
-  skb2->mem_addr = skb2;
-  skb2->mem_len = size;
-  skb2->free = 1;
-
-  /* Build Layer 2-3 headers for message back to source */
-  offset = ip_build_header(skb2, daddr, saddr, &dev,
-			 	IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
-  if (offset < 0) {
-	printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
-	kfree_skb(skb2,FREE_WRITE);
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_READ);
-	return;
-  }
-
-  /* Re-adjust length according to actual IP header size. */
-  skb2->len = offset + len;
+	struct icmphdr *icmphr;
+	struct sk_buff *skb2;
+	int size, offset;
+	struct device *ndev=NULL;
+
+	icmp_statistics.IcmpOutMsgs++;
+	icmp_statistics.IcmpOutAddrMaskReps++;
+	
+	size = dev->hard_header_len + 64 + len;
+	skb2 = alloc_skb(size, GFP_ATOMIC);
+	if (skb2 == NULL) 
+	{
+		icmp_statistics.IcmpOutErrors++;
+		kfree_skb(skb, FREE_READ);
+		return;
+  	}
+  	skb2->free = 1;
+	
+	/* 
+	 *	Build Layer 2-3 headers for message back to source 
+	 */
+
+	offset = ip_build_header(skb2, daddr, saddr, &ndev,
+		 	IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
+	if (offset < 0) 
+	{
+		icmp_statistics.IcmpOutErrors++;
+		printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
+		kfree_skb(skb2,FREE_WRITE);
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
 
-  /* Build ICMP ADDRESS MASK Response message. */
-  icmphr = (struct icmphdr *) (skb2->data + offset);
-  icmphr->type = ICMP_ADDRESSREPLY;
-  icmphr->code = 0;
-  icmphr->checksum = 0;
-  icmphr->un.echo.id = icmph->un.echo.id;
-  icmphr->un.echo.sequence = icmph->un.echo.sequence;
-  memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask));
+	/*
+	 *	Re-adjust length according to actual IP header size. 
+	 */
+
+	skb2->len = offset + len;
+
+	/*
+	 *	Build ICMP ADDRESS MASK Response message. 
+	 */
+
+	icmphr = (struct icmphdr *) (skb2->data + offset);
+	icmphr->type = ICMP_ADDRESSREPLY;
+	icmphr->code = 0;
+	icmphr->checksum = 0;
+	icmphr->un.echo.id = icmph->un.echo.id;
+	icmphr->un.echo.sequence = icmph->un.echo.sequence;
+	memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask));
 
-  icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
+	icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
 
-  /* Ship it out - free it when done */
-  ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+	/* Ship it out - free it when done */
+	ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
 
-  skb->sk = NULL;
-  kfree_skb(skb, FREE_READ);
+	skb->sk = NULL;
+	kfree_skb(skb, FREE_READ);
 }
 
 
-/* Deal with incoming ICMP packets. */
-int
-icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
+/* 
+ *	Deal with incoming ICMP packets. 
+ */
+ 
+int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
 	 unsigned long daddr, unsigned short len,
 	 unsigned long saddr, int redo, struct inet_protocol *protocol)
 {
-  struct icmphdr *icmph;
-  unsigned char *buff;
+	struct icmphdr *icmph;
+	unsigned char *buff;
 
-  /* Drop broadcast packets. */
-  if (chk_addr(daddr) == IS_BROADCAST) {
-	DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
+	/*
+	 *	Drop broadcast packets. IP has done a broadcast check and ought one day
+	 *	to pass on that information.
+	 */
+	
+	icmp_statistics.IcmpInMsgs++;
+	 
+	if (ip_chk_addr(daddr) == IS_BROADCAST) 
+	{
+		DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
 							in_ntoa(saddr)));
-	skb1->sk = NULL;
-	kfree_skb(skb1, FREE_READ);
-	return(0);
-  }
-
-  buff = skb1->h.raw;
-  icmph = (struct icmphdr *) buff;
-
-  /* Validate the packet first */
-  if (ip_compute_csum((unsigned char *) icmph, len)) {
-	/* Failed checksum! */
-	printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
-	skb1->sk = NULL;
-	kfree_skb(skb1, FREE_READ);
-	return(0);
-  }
-  print_icmp(icmph);
-
-  /* Parse the ICMP message */
-  switch(icmph->type) {
-	case ICMP_TIME_EXCEEDED:
-	case ICMP_DEST_UNREACH:
-	case ICMP_SOURCE_QUENCH:
-		icmp_unreach(icmph, skb1);
-		return(0);
-	case ICMP_REDIRECT:
-		icmp_redirect(icmph, skb1, dev);
-		return(0);
-	case ICMP_ECHO: 
-		icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
-		return 0;
-	case ICMP_ECHOREPLY:
-		skb1->sk = NULL;
+		icmp_statistics.IcmpInErrors++;
 		kfree_skb(skb1, FREE_READ);
 		return(0);
-	case ICMP_TIMESTAMP:
-		icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt);
-		return 0;
-	case ICMP_TIMESTAMPREPLY:
-		skb1->sk = NULL;
+  	}
+  	
+  	/*
+  	 *	Grab the packet as an icmp object
+  	 */
+
+	buff = skb1->h.raw;
+	icmph = (struct icmphdr *) buff;
+
+	/*
+	 *	Validate the packet first 
+	 */
+
+	if (ip_compute_csum((unsigned char *) icmph, len)) 
+	{
+		/* Failed checksum! */
+		icmp_statistics.IcmpInErrors++;
+		printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
 		kfree_skb(skb1, FREE_READ);
 		return(0);
-	case ICMP_INFO_REQUEST:
-		icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
-		return 0;
-	case ICMP_INFO_REPLY:
-		skb1->sk = NULL;
-		kfree_skb(skb1, FREE_READ);
-		return(0);
-	case ICMP_ADDRESS:
-		icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
-		return 0;
-	case ICMP_ADDRESSREPLY:
-		skb1->sk = NULL;
-		kfree_skb(skb1, FREE_READ);
-		return(0);
-	default:
-		DPRINTF((DBG_ICMP,
-			"ICMP: Unsupported ICMP from %s, type = 0x%X\n",
-						in_ntoa(saddr), icmph->type));
-		skb1->sk = NULL;
-		kfree_skb(skb1, FREE_READ);
-		return(0);
-  }
+	}
+	print_icmp(icmph);
+
+	/*
+	 *	Parse the ICMP message 
+	 */
+
+	switch(icmph->type) 
+	{
+		case ICMP_TIME_EXCEEDED:
+			icmp_statistics.IcmpInTimeExcds++;
+			icmp_unreach(icmph, skb1);
+			return 0;
+		case ICMP_DEST_UNREACH:
+			icmp_statistics.IcmpInDestUnreachs++;
+			icmp_unreach(icmph, skb1);
+			return 0;
+		case ICMP_SOURCE_QUENCH:
+			icmp_statistics.IcmpInSrcQuenchs++;
+			icmp_unreach(icmph, skb1);
+			return(0);
+		case ICMP_REDIRECT:
+			icmp_statistics.IcmpInRedirects++;
+			icmp_redirect(icmph, skb1, dev);
+			return(0);
+		case ICMP_ECHO: 
+			icmp_statistics.IcmpInEchos++;
+			icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
+			return 0;
+		case ICMP_ECHOREPLY:
+			icmp_statistics.IcmpInEchoReps++;
+			kfree_skb(skb1, FREE_READ);
+			return(0);
+		case ICMP_TIMESTAMP:
+			icmp_statistics.IcmpInTimestamps++;
+			icmp_timestamp(icmph, skb1, dev, saddr, daddr, len, opt);
+			return 0;
+		case ICMP_TIMESTAMPREPLY:
+			icmp_statistics.IcmpInTimestampReps++;
+			kfree_skb(skb1,FREE_READ);
+			return 0;
+		/* INFO is obsolete and doesn't even feature in the SNMP stats */
+		case ICMP_INFO_REQUEST:
+			icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
+			return 0;
+		case ICMP_INFO_REPLY:
+			skb1->sk = NULL;
+			kfree_skb(skb1, FREE_READ);
+			return(0);
+		case ICMP_ADDRESS:
+			icmp_statistics.IcmpInAddrMasks++;
+			icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
+			return 0;
+		case ICMP_ADDRESSREPLY:
+			/*
+			 *	We ought to set our netmask on receiving this, but 
+			 *	experience shows its a waste of effort.
+			 */
+			icmp_statistics.IcmpInAddrMaskReps++;
+			kfree_skb(skb1, FREE_READ);
+			return(0);
+		default:
+			icmp_statistics.IcmpInErrors++;
+			DPRINTF((DBG_ICMP,
+				"ICMP: Unsupported ICMP from %s, type = 0x%X\n",
+							in_ntoa(saddr), icmph->type));
+			kfree_skb(skb1, FREE_READ);
+			return(0);
+ 	}
   /*NOTREACHED*/
-  skb1->sk = NULL;
-  kfree_skb(skb1, FREE_READ);
-  return(-1);
+	kfree_skb(skb1, FREE_READ);
+	return(-1);
 }
 
 
-/* Perform any ICMP-related I/O control requests. */
-int
-icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+/*
+ *	Perform any ICMP-related I/O control requests. 
+ *	[to vanish soon]
+ */
+ 
+int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
-  switch(cmd) {
-	case DDIOCSDBG:
-		return(dbg_ioctl((void *) arg, DBG_ICMP));
-	default:
-		return(-EINVAL);
-  }
-  return(0);
+  	switch(cmd) 
+  	{
+		case DDIOCSDBG:
+			return(dbg_ioctl((void *) arg, DBG_ICMP));
+		default:
+			return(-EINVAL);
+  	}
+ 	return(0);
 }
diff -u --recursive --new-file v1.1.3/linux/net/inet/icmp.h linux/net/inet/icmp.h
--- v1.1.3/linux/net/inet/icmp.h	Wed Dec  1 14:44:15 1993
+++ linux/net/inet/icmp.h	Wed Apr 13 12:52:23 1994
@@ -22,6 +22,8 @@
 
 
 extern struct icmp_err icmp_err_convert[];
+extern struct icmp_mib icmp_statistics;
+
 
 extern void	icmp_send(struct sk_buff *skb_in,  int type, int code,
 			  struct device *dev);
diff -u --recursive --new-file v1.1.3/linux/net/inet/ip.c linux/net/inet/ip.c
--- v1.1.3/linux/net/inet/ip.c	Wed Apr 13 11:33:59 1994
+++ linux/net/inet/ip.c	Wed Apr 13 12:52:24 1994
@@ -45,6 +45,11 @@
  *		Alan Cox	:	handling.
  *		Gerhard Koerting:	Forwarding uses IP priority hints
  *		Teemu Rantanen	:	Fragment problems.
+ *		Alan Cox	:	General cleanup, comments and reformat
+ *		Alan Cox	:	SNMP statistics
+ *		Alan Cox	:	BSD address rule semantics. Also see
+ *					UDP as there is a nasty checksum issue
+ *					if you do things the wrong way.
  *
  * To Fix:
  *		IP option processing is mostly not needed. ip_forward needs to know about routing rules
@@ -65,14 +70,15 @@
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "snmp.h"
 #include "ip.h"
 #include "protocol.h"
 #include "route.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "arp.h"
 #include "icmp.h"
@@ -85,63 +91,93 @@
 
 #define min(a,b)	((a)<(b)?(a):(b))
 
-void
-ip_print(struct iphdr *ip)
+/*
+ *	SNMP management statistics
+ */
+ 
+struct ip_mib ip_statistics={1,64,};	/* Forwarding=Yes, Default TTL=64 */
+ 
+/* 
+ *	Print an IP packet for debugging purposes.
+ *
+ *	This function is exported for the IP
+ *	upper layers to use also.
+ */
+ 
+void ip_print(const struct iphdr *ip)
 {
-  unsigned char buff[32];
-  unsigned char *ptr;
-  int addr, len, i;
+	unsigned char buff[32];
+	unsigned char *ptr;
+	int addr;
+	int len;
+	int i;
 
-  if (inet_debug != DBG_IP) return;
+	/* Are we debugging IP frames */
+	
+  	if (inet_debug != DBG_IP) 
+  		return;
 
-  /* Dump the IP header. */
-  printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n",
-	   ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len));
-  printk("    id=%X, ttl=%d, prot=%d, check=%X\n",
-	   ip->id, ip->ttl, ip->protocol, ip->check);
-  printk("    frag_off=%d\n", ip->frag_off);
-  printk("    soucre=%s ", in_ntoa(ip->saddr));
-  printk("dest=%s\n", in_ntoa(ip->daddr));
-  printk("    ----\n");
-
-  /* Dump the data. */
-  ptr = (unsigned char *)(ip + 1);
-  addr = 0;
-  len = ntohs(ip->tot_len) - (4 * ip->ihl);
-  while (len > 0) {
-	printk("    %04X: ", addr);
-	for(i = 0; i < 16; i++) {
-		if (len > 0) {
-			printk("%02X ", (*ptr & 0xFF));
-			buff[i] = *ptr++;
-			if (buff[i] < 32 || buff[i] > 126) buff[i] = '.';
-		} else {
-			printk("   ");
-			buff[i] = ' ';
-		}
-		addr++;
-		len--;
-	};
-	buff[i] = '\0';
-	printk("  \"%s\"\n", buff);
-  }
-  printk("    ----\n\n");
+	/* Dump the IP header. */
+	printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n",
+		ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len));
+	printk("    id=%X, ttl=%d, prot=%d, check=%X\n",
+		ip->id, ip->ttl, ip->protocol, ip->check);
+	printk("    frag_off=%d\n", ip->frag_off);
+	printk("    soucre=%s ", in_ntoa(ip->saddr));
+	printk("dest=%s\n", in_ntoa(ip->daddr));
+	printk("    ----\n");
+
+	/* Dump the data. */
+	ptr = (unsigned char *)(ip + 1);
+	addr = 0;
+	len = ntohs(ip->tot_len) - (4 * ip->ihl);
+
+	while (len > 0) 
+	{
+		printk("    %04X: ", addr);
+		for(i = 0; i < 16; i++) 
+		{
+			if (len > 0) 
+			{
+				printk("%02X ", (*ptr & 0xFF));
+				buff[i] = *ptr++;
+				if (buff[i] < 32 || buff[i] > 126) 
+					buff[i] = '.';
+			} 
+			else 
+			{
+				printk("   ");
+				buff[i] = ' ';
+			}
+			addr++;
+			len--;
+		};
+		buff[i] = '\0';
+		printk("  \"%s\"\n", buff);
+  	}
+  	printk("    ----\n\n");
 }
 
+/*
+ *	Handle the issuing of an ioctl() request 
+ *	for the ip device. This is scheduled to
+ *	disappear
+ */
 
-int
-ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
+int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
-  switch(cmd) {
-	case DDIOCSDBG:
-		return(dbg_ioctl((void *) arg, DBG_IP));
-	default:
-		return(-EINVAL);
-  }
+  	switch(cmd) 
+  	{
+		case DDIOCSDBG:
+			return(dbg_ioctl((void *) arg, DBG_IP));
+		default:
+			return(-EINVAL);
+  	}
 }
 
 
-/* these two routines will do routining. */
+/* these two routines will do routing. */
+
 static void
 strict_route(struct iphdr *iph, struct options *opt)
 {
@@ -183,26 +219,31 @@
 #endif
 
 
-/* Take an skb, and fill in the MAC header. */
-static int
-ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev,
-	unsigned long saddr)
+/*
+ *	Take an skb, and fill in the MAC header. 
+ */
+ 
+static int ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, unsigned long saddr)
 {
-  unsigned char *ptr;
-  int mac;
+	int mac = 0;
 
-  ptr = skb->data;
-  mac = 0;
-  skb->arp = 1;
-  if (dev->hard_header) {
-	mac = dev->hard_header(ptr, dev, ETH_P_IP, daddr, saddr, len);
-  }
-  if (mac < 0) {
-	mac = -mac;
-	skb->arp = 0;
-  }
-  skb->dev = dev;
-  return(mac);
+	skb->dev = dev;
+	skb->arp = 1;
+	if (dev->hard_header) 
+	{
+  		/*
+  		 *	Build a hardware header. Source address is our mac, destination unknown
+  		 *  	(rebuild header will sort this out) 
+  		 */
+		mac = dev->hard_header(skb->data, dev, ETH_P_IP, NULL, NULL, len, skb);
+		if (mac < 0) 
+		{
+			mac = -mac;
+			skb->arp = 0;
+			skb->raddr = daddr;	/* next routing address */
+		}	
+	}
+	return mac;
 }
 
 
@@ -212,81 +253,124 @@
  * protocol knows what it's doing, otherwise it uses the
  * routing/ARP tables to select a device struct.
  */
-int
-ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
+int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
 		struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
 {
-  static struct options optmem;
-  struct iphdr *iph;
-  struct rtable *rt;
-  unsigned char *buff;
-  unsigned long raddr;
-  static int count = 0;
-  int tmp;
+  	static struct options optmem;
+  	struct iphdr *iph;
+  	struct rtable *rt;
+  	unsigned char *buff;
+  	unsigned long raddr;
+  	static int count = 0;
+  	int tmp;
+  	unsigned long src;
 
-  if (saddr == 0) 
-  	saddr = my_addr();
-  	
-  DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n"
-	   "                 type=%d, opt=%X, len = %d)\n",
-	   skb, saddr, daddr, *dev, type, opt, len));
+	/*
+	 *	If there is no 'from' address as yet, then make it our loopback
+	 */
+	 
+  	if (saddr == 0) 
+  		saddr = ip_my_addr();
+ 	
+	DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n"
+		   "                 type=%d, opt=%X, len = %d)\n",
+		   skb, saddr, daddr, *dev, type, opt, len));
 	   
-  buff = skb->data;
+	buff = skb->data;
 
-  /* See if we need to look up the device. */
-  if (*dev == NULL) {
-	rt = rt_route(daddr, &optmem);
-	if (rt == NULL) 
-		return(-ENETUNREACH);
+	/* 
+	 *	See if we need to look up the device. 
+	 */
+	 
+	if (*dev == NULL) 
+	{
+		rt = ip_rt_route(daddr, &optmem, &src);
+		if (rt == NULL) 
+		{
+			ip_statistics.IpOutNoRoutes++;
+			return(-ENETUNREACH);
+		}
+	
+		*dev = rt->rt_dev;
+		/*
+		 *	If the frame is from us and going off machine it MUST MUST MUST
+		 *	have the output device ip address and never the loopback
+		 */
+		if (saddr == 0x0100007FL && daddr != 0x0100007FL) 
+			saddr = src;/*rt->rt_dev->pa_addr;*/
+		raddr = rt->rt_gateway;
 
-	*dev = rt->rt_dev;
-	if (saddr == 0x0100007FL && daddr != 0x0100007FL) 
-		saddr = rt->rt_dev->pa_addr;
-	raddr = rt->rt_gateway;
+		DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr)));
+		opt = &optmem;
+	} 
+	else 
+	{
+		/* 
+		 *	We still need the address of the first hop. 
+		 */
+		rt = ip_rt_route(daddr, &optmem, &src);
+		/*
+		 *	If the frame is from us and going off machine it MUST MUST MUST
+		 *	have the output device ip address and never the loopback
+		 */
+		if (saddr == 0x0100007FL && daddr != 0x0100007FL) 
+			saddr = src;/*rt->rt_dev->pa_addr;*/
 
-	DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr)));
-	opt = &optmem;
-  } else {
-	/* We still need the address of the first hop. */
-	rt = rt_route(daddr, &optmem);
-	raddr = (rt == NULL) ? 0 : rt->rt_gateway;
-  }
-  if (raddr == 0)
-  	raddr = daddr;
+		raddr = (rt == NULL) ? 0 : rt->rt_gateway;
+	}
+  
+	  /*
+	   *	No gateway so aim at the real destination
+	   */
+  	if (raddr == 0)
+  		raddr = daddr;
+
+  	/* 
+  	 *	Now build the MAC header. 
+  	 */
+  	 
+  	tmp = ip_send(skb, raddr, len, *dev, saddr);
+  	buff += tmp;
+	len -= tmp;
+
+	/*
+	 *	Book keeping
+	 */
+
+	skb->dev = *dev;
+	skb->saddr = saddr;
+	if (skb->sk) 
+		skb->sk->saddr = saddr;
+
+	/*
+	 *	Now build the IP header. 
+	 */
 
-  /* Now build the MAC header. */
-  tmp = ip_send(skb, raddr, len, *dev, saddr);
-  buff += tmp;
-  len -= tmp;
-
-  skb->dev = *dev;
-  skb->saddr = saddr;
-  if (skb->sk) skb->sk->saddr = saddr;
-
-  /* Now build the IP header. */
-
-  /* If we are using IPPROTO_RAW, then we don't need an IP header, since
-     one is being supplied to us by the user */
-
-  if(type == IPPROTO_RAW) return (tmp);
-
-  iph = (struct iphdr *)buff;
-  iph->version  = 4;
-  iph->tos      = tos;
-  iph->frag_off = 0;
-  iph->ttl      = ttl;
-  iph->daddr    = daddr;
-  iph->saddr    = saddr;
-  iph->protocol = type;
-  iph->ihl      = 5;
-  iph->id       = htons(count++);
+  	/* 
+  	 *	If we are using IPPROTO_RAW, then we don't need an IP header, since
+     	 *	one is being supplied to us by the user 
+     	 */
+
+  	if(type == IPPROTO_RAW) 
+  		return (tmp);
+
+	iph = (struct iphdr *)buff;
+	iph->version  = 4;
+	iph->tos      = tos;
+	iph->frag_off = 0;
+	iph->ttl      = ttl;
+	iph->daddr    = daddr;
+	iph->saddr    = saddr;
+	iph->protocol = type;
+	iph->ihl      = 5;
+	iph->id       = htons(count++);
 
-  /* Setup the IP options. */
+	/* Setup the IP options. */
 #ifdef Not_Yet_Avail
-  build_options(iph, opt);
+	build_options(iph, opt);
 #endif
 
-  return(20 + tmp);	/* IP header plus MAC header size */
+	return(20 + tmp);	/* IP header plus MAC header size */
 }
 
 
@@ -437,14 +521,17 @@
   return(0);
 }
 
-/* This is a version of ip_compute_csum() optimized for IP headers, which
-   always checksum on 4 octet boundaries. */
-static inline unsigned short
-ip_fast_csum(unsigned char * buff, int wlen)
+/* 
+ *	This is a version of ip_compute_csum() optimized for IP headers, which
+ *	always checksum on 4 octet boundaries. 
+ */
+ 
+static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
 {
-    unsigned long sum = 0;
+	unsigned long sum = 0;
 
-    if (wlen) {
+    	if (wlen) 
+    	{
     	unsigned long bogus;
 	 __asm__("clc\n"
 		"1:\t"
@@ -459,22 +546,23 @@
 		"adcw $0, %w0"
 	    : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus)
 	    : "0"  (sum),  "1" (buff),  "2" (wlen));
-    }
-    return (~sum) & 0xffff;
+    	}
+	return (~sum) & 0xffff;
 }
 
 /*
  * This routine does all the checksum computations that don't
  * require anything special (like copying or special headers).
  */
-unsigned short
-ip_compute_csum(unsigned char * buff, int len)
+
+unsigned short ip_compute_csum(unsigned char * buff, int len)
 {
-  unsigned long sum = 0;
+	unsigned long sum = 0;
 
-  /* Do the first multiple of 4 bytes and convert to 16 bits. */
-  if (len > 3) {
-	__asm__("clc\n"
+	/* Do the first multiple of 4 bytes and convert to 16 bits. */
+	if (len > 3) 
+	{
+		__asm__("clc\n"
 	        "1:\t"
 	    	"lodsl\n\t"
 	    	"adcl %%eax, %%ebx\n\t"
@@ -487,47 +575,63 @@
 	        : "=b" (sum) , "=S" (buff)
 	        : "0" (sum), "c" (len >> 2) ,"1" (buff)
 	        : "ax", "cx", "si", "bx" );
-  }
-  if (len & 2) {
-	__asm__("lodsw\n\t"
+  	}
+  	if (len & 2) 
+  	{
+		__asm__("lodsw\n\t"
 	    	"addw %%ax, %%bx\n\t"
 	    	"adcw $0, %%bx"
 	        : "=b" (sum), "=S" (buff)
 	        : "0" (sum), "1" (buff)
 	        : "bx", "ax", "si");
-  }
-  if (len & 1) {
-	__asm__("lodsb\n\t"
+  	}
+  	if (len & 1) 
+  	{
+		__asm__("lodsb\n\t"
 	    	"movb $0, %%ah\n\t"
 	    	"addw %%ax, %%bx\n\t"
 	    	"adcw $0, %%bx"
 	        : "=b" (sum), "=S" (buff)
 	        : "0" (sum), "1" (buff)
 	        : "bx", "ax", "si");
-  }
-  sum =~sum;
-  return(sum & 0xffff);
+  	}
+  	sum =~sum;
+  	return(sum & 0xffff);
 }
 
-/* Check the header of an incoming IP datagram.  This version is still used in slhc.c. */
-int
-ip_csum(struct iphdr *iph)
+/* 
+ *	Check the header of an incoming IP datagram.  This version is still used in slhc.c. 
+ */
+ 
+int ip_csum(struct iphdr *iph)
 {
-  return ip_fast_csum((unsigned char *)iph, iph->ihl);
+  	return ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
-/* Generate a checksym for an outgoing IP datagram. */
-static void
-ip_send_check(struct iphdr *iph)
+/* 
+ *	Generate a checksym for an outgoing IP datagram. 
+ */
+
+static void ip_send_check(struct iphdr *iph)
 {
-   iph->check = 0;
-   iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+	iph->check = 0;
+	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
 /************************ Fragment Handlers From NET2E not yet with tweaks to beat 4K **********************************/
 
+
+/*
+ *	This fragment handler is a bit of a heap. On the other hand it works quite
+ *	happily and handles things quite well.
+ */
+ 
 static struct ipq *ipqueue = NULL;		/* IP fragment queue	*/
- /* Create a new fragment entry. */
+
+/*
+ *	Create a new fragment entry. 
+ */
+ 
 static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
 {
    	struct ipfrag *fp;
@@ -552,9 +656,10 @@
  
  
 /*
- * Find the correct entry in the "incomplete datagrams" queue for
- * this IP datagram, and return the queue entry address if found.
+ *	Find the correct entry in the "incomplete datagrams" queue for
+ *	this IP datagram, and return the queue entry address if found.
  */
+ 
 static struct ipq *ip_find(struct iphdr *iph)
 {
 	struct ipq *qp;
@@ -567,7 +672,7 @@
  		if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
 			iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol) 
 		{
-			del_timer(&qp->timer);	/* So it doesnt vanish on us. The timer will be reset anyway */
+			del_timer(&qp->timer);	/* So it doesn't vanish on us. The timer will be reset anyway */
  			sti();
  			return(qp);
  		}
@@ -578,9 +683,9 @@
  
  
 /*
- * Remove an entry from the "incomplete datagrams" queue, either
- * because we completed, reassembled and processed it, or because
- * it timed out.
+ *	Remove an entry from the "incomplete datagrams" queue, either
+ *	because we completed, reassembled and processed it, or because
+ *	it timed out.
  */
 
 static void ip_free(struct ipq *qp)
@@ -588,8 +693,10 @@
 	struct ipfrag *fp;
 	struct ipfrag *xp;
 
-	/* Stop the timer for this entry. */
-/*	printk("ip_free\n");*/
+	/*
+	 * Stop the timer for this entry. 
+	 */
+	 
 	del_timer(&qp->timer);
 
 	/* Remove this entry from the "incomplete datagrams" queue. */
@@ -608,7 +715,7 @@
    	}
  
    	/* Release all fragment data. */
-/*   	printk("ip_free: kill frag data\n");*/
+
    	fp = qp->fragments;
    	while (fp != NULL) 
    	{
@@ -619,8 +726,6 @@
  		fp = xp;
    	}
    	
-/*   	printk("ip_free: cleanup\n");*/
- 
    	/* Release the MAC header. */
    	kfree_s(qp->mac, qp->maclen);
  
@@ -634,7 +739,9 @@
  }
  
  
- /* Oops- a fragment queue timed out.  Kill it and send an ICMP reply. */
+/*
+ *	Oops- a fragment queue timed out.  Kill it and send an ICMP reply. 
+ */
  
 static void ip_expire(unsigned long arg)
 {
@@ -643,25 +750,29 @@
    	qp = (struct ipq *)arg;
    	DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp));
  
-   	/* Send an ICMP "Fragment Reassembly Timeout" message. */
-#if 0   	
-   	icmp_send(qp->iph->ip_src.s_addr, ICMP_TIME_EXCEEDED,
- 		    ICMP_EXC_FRAGTIME, qp->iph);
-#endif 		 
+   	/*
+   	 *	Send an ICMP "Fragment Reassembly Timeout" message. 
+   	 */
+
+	ip_statistics.IpReasmTimeout++;
+	ip_statistics.IpReasmFails++;   	 
+   	/* This if is always true... shrug */
  	if(qp->fragments!=NULL)
  		icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
  				ICMP_EXC_FRAGTIME, qp->dev);
  
-   	/* Nuke the fragment queue. */
+   	/* 
+   	 *	Nuke the fragment queue. 
+   	 */
 	ip_free(qp);
 }
  
  
 /*
- * Add an entry to the 'ipq' queue for a newly received IP datagram.
- * We will (hopefully :-) receive all other fragments of this datagram
- * in time, so we just create a queue for this datagram, in which we
- * will insert the received fragments at their respective positions.
+ * 	Add an entry to the 'ipq' queue for a newly received IP datagram.
+ * 	We will (hopefully :-) receive all other fragments of this datagram
+ * 	in time, so we just create a queue for this datagram, in which we
+ * 	will insert the received fragments at their respective positions.
  */
 
 static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
@@ -675,10 +786,17 @@
   	{
 		printk("IP: create: no memory left !\n");
 		return(NULL);
+   	skb->dev = qp->dev;
   	}
  	memset(qp, 0, sizeof(struct ipq));
 
-  	/* Allocate memory for the MAC header. */
+  	/*
+  	 *	Allocate memory for the MAC header. 
+  	 *
+  	 *	FIXME: We have a maximum MAC address size limit and define 
+  	 *	elsewhere. We should use it here and avoid the 3 kmalloc() calls
+  	 */
+  	 
   	maclen = ((unsigned long) iph) - ((unsigned long) skb->data);
   	qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
   	if (qp->mac == NULL) 
@@ -688,7 +806,10 @@
 		return(NULL);
   	}
 
-  	/* Allocate memory for the IP header (plus 8 octects for ICMP). */
+  	/* 
+  	 *	Allocate memory for the IP header (plus 8 octects for ICMP). 
+  	 */
+  	 
   	ihlen = (iph->ihl * sizeof(unsigned long));
   	qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC);
   	if (qp->iph == NULL) 
@@ -707,7 +828,6 @@
   	qp->maclen = maclen;
   	qp->fragments = NULL;
   	qp->dev = dev;
-/*  	printk("Protocol = %d\n",qp->iph->protocol);*/
 	
   	/* Start a timer for this entry. */
   	qp->timer.expires = IP_FRAG_TIME;		/* about 30 seconds	*/
@@ -727,7 +847,10 @@
 }
  
  
- /* See if a fragment queue is complete. */
+/*
+ *	See if a fragment queue is complete. 
+ */
+ 
 static int ip_done(struct ipq *qp)
 {
 	struct ipfrag *fp;
@@ -753,7 +876,15 @@
  }
  
  
-/* Build a new IP datagram from all its fragments. */
+/* 
+ *	Build a new IP datagram from all its fragments. 
+ *
+ *	FIXME: We copy here because we lack an effective way of handling lists
+ *	of bits on input. Until the new skb data handling is in I'm not going
+ *	to touch this with a bargepole. This also causes a 4Kish limit on
+ *	packet sizes.
+ */
+ 
 static struct sk_buff *ip_glue(struct ipq *qp)
 {
 	struct sk_buff *skb;
@@ -762,10 +893,15 @@
    	unsigned char *ptr;
    	int count, len;
  
-   	/* Allocate a new buffer for the datagram. */
-   	len = sizeof(struct sk_buff)+qp->maclen + qp->ihlen + qp->len;
+   	/*
+   	 *	Allocate a new buffer for the datagram. 
+   	 */
+   	 
+   	len = qp->maclen + qp->ihlen + qp->len;
+
    	if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) 
    	{
+   		ip_statistics.IpReasmFails++;
  		printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp);
  		ip_free(qp);
  		return(NULL);
@@ -775,19 +911,15 @@
    	skb->len = (len - qp->maclen);
    	skb->h.raw = skb->data;
    	skb->free = 1;
-   	skb->dev = qp->dev;
  
    	/* Copy the original MAC and IP headers into the new buffer. */
    	ptr = (unsigned char *) skb->h.raw;
    	memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen);
-/*   	printk("Copied %d bytes of mac header.\n",qp->maclen);*/
    	ptr += qp->maclen;
    	memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
-/*   	printk("Copied %d byte of ip header.\n",qp->ihlen);*/
    	ptr += qp->ihlen;
    	skb->h.raw += qp->maclen;
    	
-/*   	printk("Protocol = %d\n",skb->h.iph->protocol);*/
    	count = 0;
  
    	/* Copy the data portions of all fragments into the new buffer. */
@@ -799,9 +931,9 @@
    			printk("Invalid fragment list: Fragment over size.\n");
    			ip_free(qp);
    			kfree_skb(skb,FREE_WRITE);
+   			ip_statistics.IpReasmFails++;
    			return NULL;
    		}
-/*   		printk("Fragment %d size %d\n",fp->offset,fp->len);*/
  		memcpy((ptr + fp->offset), fp->ptr, fp->len);
  		count += fp->len;
  		fp = fp->next;
@@ -815,11 +947,16 @@
    	iph->frag_off = 0;
    	iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count);
    	skb->ip_hdr = iph;
+   	
+   	ip_statistics.IpReasmOKs++;
    	return(skb);
 }
  
 
-/* Process an incoming IP datagram fragment. */
+/*
+ *	Process an incoming IP datagram fragment. 
+ */
+ 
 static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
 {
 	struct ipfrag *prev, *next;
@@ -830,6 +967,8 @@
 	int flags, offset;
 	int i, ihl, end;
 
+	ip_statistics.IpReasmReqds++;
+	
 	/* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
    	qp = ip_find(iph);
  
@@ -837,11 +976,13 @@
    	offset = ntohs(iph->frag_off);
    	flags = offset & ~IP_OFFSET;
    	offset &= IP_OFFSET;
-   	if (((flags & IP_MF) == 0) && (offset == 0)) {
+   	if (((flags & IP_MF) == 0) && (offset == 0)) 
+   	{
 		if (qp != NULL)
  			ip_free(qp);	/* Huh? How could this exist?? */
  		return(skb);
    	}
+
    	offset <<= 3;		/* offset is in 8-byte chunks */
  
    	/*
@@ -849,36 +990,55 @@
    	 * as we still are receiving fragments.  Otherwise, create a fresh
     	 * queue entry.
     	 */
-	if (qp != NULL) {
+
+	if (qp != NULL) 
+	{
 		del_timer(&qp->timer);
 		qp->timer.expires = IP_FRAG_TIME;	/* about 30 seconds */
 		qp->timer.data = (unsigned long) qp;	/* pointer to queue */
 		qp->timer.function = ip_expire;		/* expire function */
 		add_timer(&qp->timer);
-	} else {
-		if ((qp = ip_create(skb, iph, dev)) == NULL) {
+	} 
+	else 
+	{
+		/*
+		 *	If we failed to create it, then discard the frame
+		 */
+		if ((qp = ip_create(skb, iph, dev)) == NULL) 
+		{
 			skb->sk = NULL;
 			kfree_skb(skb, FREE_READ);
+			ip_statistics.IpReasmFails++;
 			return NULL;
 		}
 	}
 
-   	/* Determine the position of this fragment. */
+   	/*
+   	 *	Determine the position of this fragment. 
+   	 */
+   	 
    	ihl = (iph->ihl * sizeof(unsigned long));
    	end = offset + ntohs(iph->tot_len) - ihl;
  
-   	/* Point into the IP datagram 'data' part. */
+   	/*
+   	 *	Point into the IP datagram 'data' part. 
+   	 */
+
    	ptr = skb->data + dev->hard_header_len + ihl;
  
-   	/* Is this the final fragment? */
+   	/* 
+   	 *	Is this the final fragment? 
+   	 */
+
    	if ((flags & IP_MF) == 0) 
    		qp->len = end;
  
    	/*
-   	 * Find out which fragments are in front and at the back of us
-   	 * in the chain of fragments so far.  We must know where to put
-   	 * this fragment, right?
+   	 * 	Find out which fragments are in front and at the back of us
+   	 * 	in the chain of fragments so far.  We must know where to put
+   	 * 	this fragment, right?
    	 */
+   	 
    	prev = NULL;
    	for(next = qp->fragments; next != NULL; next = next->next) 
    	{
@@ -888,9 +1048,9 @@
    	}	
  
    	/*
-   	 * We found where to put this one.
-   	 * Check for overlap with preceeding fragment, and, if needed,
-   	 * align things so that any overlaps are eliminated.
+   	 * 	We found where to put this one.
+   	 * 	Check for overlap with preceeding fragment, and, if needed,
+   	 * 	align things so that any overlaps are eliminated.
    	 */
    	if (prev != NULL && offset < prev->end) 
    	{
@@ -916,7 +1076,10 @@
  		next->offset += i;			/* next fragment	*/
  		next->ptr += i;
  		
- 		/* If we get a frag size of <= 0, remove it. */
+ 		/* 
+ 		 *	If we get a frag size of <= 0, remove it and the packet
+ 		 *	that it goes with.
+ 		 */
  		if (next->len <= 0) 
  		{
  			DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n",
@@ -928,17 +1091,26 @@
  		
  			if (tfp->next != NULL) 
  				next->next->prev = next->prev;
- 			
- 			kfree_skb(next->skb, FREE_READ);
+ 				
+			kfree_skb(next->skb,FREE_READ); 			
  			kfree_s(next, sizeof(struct ipfrag));
  		}
  		DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i));
    	}
  
-   	/* Insert this fragment in the chain of fragments. */
+   	/* 
+   	 *	Insert this fragment in the chain of fragments. 
+   	 */
+   	 
    	tfp = NULL;
    	tfp = ip_frag_create(offset, end, skb, ptr);
-   	if (!tfp) {
+   	
+   	/*
+   	 *	No memory to save the fragment - so throw the lot
+   	 */
+   	
+   	if (!tfp) 
+   	{
    		skb->sk = NULL;
    		kfree_skb(skb, FREE_READ);
    		return NULL;
@@ -954,9 +1126,9 @@
    		next->prev = tfp;
  
    	/*
-    	 * OK, so we inserted this new fragment into the chain.
-    	 * Check if we now have a full IP datagram which we can
-    	 * bump up to the IP layer...
+    	 * 	OK, so we inserted this new fragment into the chain.
+    	 * 	Check if we now have a full IP datagram which we can
+    	 * 	bump up to the IP layer...
     	 */
    
    	if (ip_done(qp)) 
@@ -969,13 +1141,19 @@
  
  
  /*
-  * This IP datagram is too large to be sent in one piece.  Break it up into
-  * smaller pieces (each of size equal to the MAC header plus IP header plus
-  * a block of the data of the original IP data part) that will yet fit in a
-  * single device frame, and queue such a frame for sending by calling the
-  * ip_queue_xmit().  Note that this is recursion, and bad things will happen
-  * if this function causes a loop...
+  *	This IP datagram is too large to be sent in one piece.  Break it up into
+  *	smaller pieces (each of size equal to the MAC header plus IP header plus
+  *	a block of the data of the original IP data part) that will yet fit in a
+  *	single device frame, and queue such a frame for sending by calling the
+  *	ip_queue_xmit().  Note that this is recursion, and bad things will happen
+  *	if this function causes a loop...
+  *
+  *	Yes this is inefficient, feel free to submit a quicker one.
+  *
+  *	**Protocol Violation**
+  *	We copy all the options to each fragment. !FIXME!
   */
+  
  void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
  {
    	struct iphdr *iph;
@@ -985,27 +1163,34 @@
    	int left, mtu, hlen, len;
    	int offset;
  
-   	/* Point into the IP datagram header. */
+   	/* 
+   	 *	Point into the IP datagram header. 
+   	 */
+   	 
    	raw = skb->data;
    	iph = (struct iphdr *) (raw + dev->hard_header_len);
 
 	skb->ip_hdr = iph;
 	 	
-   	/* Setup starting values. */
+   	/* 
+   	 *	Setup starting values. 
+   	 */
+   	 
    	hlen = (iph->ihl * sizeof(unsigned long));
-   	left = ntohs(iph->tot_len) - hlen;
-   	hlen += dev->hard_header_len;
-   	mtu = (dev->mtu - hlen);
-   	ptr = (raw + hlen);
+   	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
+   	hlen += dev->hard_header_len;		/* Total header size */
+   	mtu = (dev->mtu - hlen);		/* Size of data space */
+   	ptr = (raw + hlen);			/* Where to start from */
  	
    	DPRINTF((DBG_IP, "IP: Fragmentation Desired\n"));
    	DPRINTF((DBG_IP, "    DEV=%s, MTU=%d, LEN=%d SRC=%s",
  		dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
    	DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
-
-	if (mtu < 8)
-		return;
-   	/* Check for any "DF" flag. */
+ 
+   	/*
+   	 *	Check for any "DF" flag. [DF means do not fragment]
+   	 */
+   	 
    	if (ntohs(iph->frag_off) & IP_DF) 
    	{
  		DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n"));
@@ -1013,15 +1198,44 @@
  			dev->name, dev->mtu, left, in_ntoa(iph->saddr)));
  		DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr)));
  
+ 		ip_statistics.IpFragFails++;
  		icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); 
  		return;
    	}
  
-   	/* Fragment the datagram. */
+ 	/*
+ 	 *	The protocol doesn't seem to say what to do in the case that the
+ 	 *	frame + options doesn't fit the mtu. As it used to fall down dead
+ 	 *	in this case we were fortunate it didn't happen
+ 	 */
+ 	 
+ 	if(mtu<8)
+ 	{
+ 		/* It's wrong but its better than nothing */
+ 		icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev);
+ 		ip_statistics.IpFragFails++;
+ 		return;
+ 	}
+ 	
+   	/* 
+   	 *	Fragment the datagram. 
+   	 */
+   	 
+   	/*
+   	 *	The initial offset is 0 for a complete frame. When
+   	 *	fragmenting fragments its wherever this one starts.
+   	 */
+
 	if (is_frag & 2)
-	  offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
+		offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
 	else
-   	  offset = 0;
+   		offset = 0;
+
+
+	/*
+	 *	Keep copying data until we run out.
+	 */
+	    		
    	while(left > 0) 
    	{
  		len = left;
@@ -1038,332 +1252,496 @@
  		DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n",
  							len, len + hlen));
  
- 		/* Allocate buffer. */
- 		if ((skb2 = alloc_skb(sizeof(struct sk_buff) + len + hlen,GFP_ATOMIC)) == NULL) 
+ 		/*
+ 		 *	Allocate buffer. 
+ 		 */
+ 		 
+ 		if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL) 
  		{
  			printk("IP: frag: no memory for new fragment!\n");
+ 			ip_statistics.IpFragFails++;
  			return;
  		}
+ 		
+ 		/*
+ 		 *	Set up data on packet
+ 		 */
+
  		skb2->arp = skb->arp;
  		skb2->free = skb->free;
  		skb2->len = len + hlen;
  		skb2->h.raw=(char *) skb2->data;
  
+		/*
+		 *	Charge the memory for the fragment to any owner
+		 *	it might posess
+		 */
+		 
  		if (sk) 
  			sk->wmem_alloc += skb2->mem_len;
  
- 		/* Copy the packet header into the new buffer. */
+ 		/* 
+ 		 *	Copy the packet header into the new buffer. 
+ 		 */
+ 		 
  		memcpy(skb2->h.raw, raw, hlen);
  
- 		/* Copy a block of the IP datagram. */
+ 		/*
+ 		 *	Copy a block of the IP datagram. 
+ 		 */
  		memcpy(skb2->h.raw + hlen, ptr, len);
  		left -= len;
 
 		skb2->h.raw+=dev->hard_header_len; 
- 		/* Fill in the new header fields. */
+
+ 		/*
+ 		 *	Fill in the new header fields. 
+ 		 */
  		iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
  		iph->frag_off = htons((offset >> 3));
- 		/* Added AC : If we are fragmenting a fragment thats not the
- 		   last fragment then keep MF on each bit */
+ 		/* 
+ 		 *	Added AC : If we are fragmenting a fragment thats not the
+ 		 *		   last fragment then keep MF on each bit 
+ 		 */
  		if (left > 0 || (is_frag & 1)) 
  			iph->frag_off |= htons(IP_MF);
  		ptr += len;
  		offset += len;
-/* 		printk("Queue frag\n");*/
  
- 		/* Put this fragment into the sending queue. */
+ 		/* 
+ 		 *	Put this fragment into the sending queue. 
+ 		 */
+ 		 
+ 		ip_statistics.IpFragCreates++;
+ 		
  		ip_queue_xmit(sk, dev, skb2, 1);
-/* 		printk("Queued\n");*/
    	}
- }
+   	ip_statistics.IpFragOKs++;
+}
  
 
 
 #ifdef CONFIG_IP_FORWARD
 
-/* Forward an IP datagram to its next destination. */
-static void
-ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
+/* 
+ *	Forward an IP datagram to its next destination. 
+ */
+
+static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
 {
-  struct device *dev2;
-  struct iphdr *iph;
-  struct sk_buff *skb2;
-  struct rtable *rt;
-  unsigned char *ptr;
-  unsigned long raddr;
+	struct device *dev2;	/* Output device */
+	struct iphdr *iph;	/* Our header */
+	struct sk_buff *skb2;	/* Output packet */
+	struct rtable *rt;	/* Route we use */
+	unsigned char *ptr;	/* Data pointer */
+	unsigned long raddr;	/* Router IP address */
 
-  /*
-   * Only forward packets that were fired at us when we are in promiscuous
-   * mode. In standard mode we rely on the driver to filter for us.
-   */
+	/*
+	 * Only forward packets that were fired at us when we are in promiscuous
+	 * mode. In standard mode we rely on the driver to filter for us.
+	 */
    
-  if(dev->flags&IFF_PROMISC)
-  {
-  	if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len))
-  		return;
-  }
-  
-  /*
-   * According to the RFC, we must first decrease the TTL field. If
-   * that reaches zero, we must reply an ICMP control message telling
-   * that the packet's lifetime expired.
-   */
-  iph = skb->h.iph;
-  iph->ttl--;
-  if (iph->ttl <= 0) {
-	DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n"));
-	DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->saddr)));
-	DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->daddr)));
-
-	/* Tell the sender its packet died... */
-	icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
-	return;
-  }
+	if(dev->flags&IFF_PROMISC)
+	{
+  		if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len))
+  			return;
+	}
+	
 
-  /* Re-compute the IP header checksum. */
-  ip_send_check(iph);
+  
+  	/*
+  	 *	According to the RFC, we must first decrease the TTL field. If
+  	 *	that reaches zero, we must reply an ICMP control message telling
+ 	 *	that the packet's lifetime expired.
+ 	 *
+ 	 *	Exception:
+ 	 *	We may not generate an ICMP for an ICMP. icmp_send does the
+ 	 *	enforcement of this so we can forget it here. It is however
+ 	 *	sometimes VERY important.
+ 	 */
+
+	iph = skb->h.iph;
+	iph->ttl--;
+	if (iph->ttl <= 0) 
+	{
+		DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n"));
+		DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->saddr)));
+		DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->daddr)));
+	
+		/* Tell the sender its packet died... */
+		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
+		return;
+	}
 
-  /*
-   * OK, the packet is still valid.  Fetch its destination address,
-   * and give it to the IP sender for further processing.
-   */
-  rt = rt_route(iph->daddr, NULL);
-  if (rt == NULL) {
-	DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n"));
-
-	/* Tell the sender its packet cannot be delivered... */
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev);
-	return;
-  }
+	/* 
+	 *	Re-compute the IP header checksum. 
+	 *	This is inefficient. We know what has happened to the header
+	 *	and could thus adjust the checksum as Phil Karn does in KA9Q
+	 */
+	 
+	ip_send_check(iph);
 
+	/*
+	 * OK, the packet is still valid.  Fetch its destination address,
+   	 * and give it to the IP sender for further processing.
+	 */
 
-  /*
-   * Gosh.  Not only is the packet valid; we even know how to
-   * forward it onto its final destination.  Can we say this
-   * is being plain lucky?
-   * If the router told us that there is no GW, use the dest.
-   * IP address itself- we seem to be connected directly...
-   */
-  raddr = rt->rt_gateway;
-  if (raddr != 0) {
-	rt = rt_route(raddr, NULL);
-	if (rt == NULL) {
-		DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n"));
+	rt = ip_rt_route(iph->daddr, NULL, NULL);
+	if (rt == NULL) 
+	{
+		DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n"));
 
-		/* Tell the sender its packet cannot be delivered... */
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
+		/*
+		 *	Tell the sender its packet cannot be delivered. Again
+		 *	ICMP is screened later.
+		 */
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev);
 		return;
 	}
-	if (rt->rt_gateway != 0) raddr = rt->rt_gateway;
-  } else raddr = iph->daddr;
-  dev2 = rt->rt_dev;
 
 
-  if (dev == dev2)
-	return;
-  /*
-   * We now allocate a new buffer, and copy the datagram into it.
-   * If the indicated interface is up and running, kick it.
-   */
-  DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr)));
-  DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
-			in_ntoa(raddr), dev2->name, skb->len));
+	/*
+	 * Gosh.  Not only is the packet valid; we even know how to
+	 * forward it onto its final destination.  Can we say this
+	 * is being plain lucky?
+	 * If the router told us that there is no GW, use the dest.
+	 * IP address itself- we seem to be connected directly...
+	 */
 
-  if (dev2->flags & IFF_UP) {
-	skb2 = (struct sk_buff *) alloc_skb(sizeof(struct sk_buff) +
-		       dev2->hard_header_len + skb->len, GFP_ATOMIC);
-	if (skb2 == NULL) {
-		printk("\nIP: No memory available for IP forward\n");
+	raddr = rt->rt_gateway;
+
+	if (raddr != 0) 
+	{
+		/*
+		 *	There is a gateway so find the correct route for it.
+		 *	Gateways cannot in turn be gatewayed.
+		 */
+		rt = ip_rt_route(raddr, NULL, NULL);
+		if (rt == NULL) 
+		{
+			DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n"));
+
+			/* 
+			 *	Tell the sender its packet cannot be delivered... 
+			 */
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
+			return;
+		}
+		if (rt->rt_gateway != 0) 
+			raddr = rt->rt_gateway;
+  	} 
+  	else 
+  		raddr = iph->daddr;
+  		
+  	/*
+  	 *	Having picked a route we can now send the frame out.
+  	 */
+
+	dev2 = rt->rt_dev;
+
+	/*
+	 *	In IP you never forward a frame on the interface that it arrived
+	 *	upon. We should generate an ICMP HOST REDIRECT giving the route
+	 *	we calculated.
+	 *	For now just dropping the packet is an acceptable compromise.
+	 */
+
+	if (dev == dev2)
 		return;
-	}
-	ptr = skb2->data;
-	skb2->sk = NULL;
-	skb2->free = 1;
-	skb2->len = skb->len + dev2->hard_header_len;
-	skb2->mem_addr = skb2;
-	skb2->mem_len = sizeof(struct sk_buff) + skb2->len;
-	skb2->next = NULL;
-	skb2->h.raw = ptr;
 
-	/* Copy the packet data into the new buffer. */
-	memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
-		
-	/* Now build the MAC header. */
-	(void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
+	/*
+	 * We now allocate a new buffer, and copy the datagram into it.
+	 * If the indicated interface is up and running, kick it.
+	 */
 
-	if(skb2->len > dev2->mtu)
-	{
-		ip_fragment(NULL,skb2,dev2, is_frag);
-		kfree_skb(skb2,FREE_WRITE);
-	}
-	else
+	DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr)));
+	DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
+			in_ntoa(raddr), dev2->name, skb->len));
+
+	if (dev2->flags & IFF_UP) 
 	{
-		if(iph->tos & IPTOS_LOWDELAY)
-			dev2->queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
-		else if(iph->tos & IPTOS_THROUGHPUT)
-			dev2->queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
+	
+		/*
+		 *	Current design decrees we copy the packet. For identical header
+		 *	lengths we could avoid it. The new skb code will let us push
+		 *	data so the problem goes away then.
+		 */
+		 
+		skb2 = alloc_skb(dev2->hard_header_len + skb->len, GFP_ATOMIC);
+		/*
+		 *	This is rare and since IP is tolerant of network failures
+		 *	quite harmless.
+		 */
+		if (skb2 == NULL) 
+		{
+			printk("\nIP: No memory available for IP forward\n");
+			return;
+		}
+		ptr = skb2->data;
+		skb2->free = 1;
+		skb2->len = skb->len + dev2->hard_header_len;
+		skb2->h.raw = ptr;
+
+		/* 
+		 *	Copy the packet data into the new buffer. 
+		 */
+		memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
+		
+		/* Now build the MAC header. */
+		(void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
+
+		ip_statistics.IpForwDatagrams++;
+
+		/*
+		 *	See if it needs fragmenting. Note in ip_rcv we tagged
+		 *	the fragment type. This must be right so that
+		 *	the fragmenter does the right thing.
+		 */
+		 
+		if(skb2->len > dev2->mtu)
+		{
+			ip_fragment(NULL,skb2,dev2, is_frag);
+			kfree_skb(skb2,FREE_WRITE);
+		}
 		else
-			dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL);
+		{
+			/*
+			 *	Map service types to priority. We lie about
+			 *	throughput being low priority, but its a good
+			 *	choice to help improve general usage.
+			 */
+			if(iph->tos & IPTOS_LOWDELAY)
+				dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
+			else if(iph->tos & IPTOS_THROUGHPUT)
+				dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
+			else
+				dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
+		}
 	}
-  }
 }
 
 
 #endif
 
-/* This function receives all incoming IP datagrams. */
-int
-ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-  struct iphdr *iph = skb->h.iph;
-  unsigned char hash;
-  unsigned char flag = 0;
-  unsigned char opts_p = 0;	/* Set iff the packet has options. */
-  struct inet_protocol *ipprot;
-  static struct options opt; /* since we don't use these yet, and they
+/*
+ *	This function receives all incoming IP datagrams. 
+ */
+ 
+int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct iphdr *iph = skb->h.iph;
+	unsigned char hash;
+	unsigned char flag = 0;
+	unsigned char opts_p = 0;	/* Set iff the packet has options. */
+	struct inet_protocol *ipprot;
+	static struct options opt; /* since we don't use these yet, and they
 				take up stack space. */
-  int brd;
-  int is_frag=0;
+	int brd;
+	int is_frag=0;
 
-  DPRINTF((DBG_IP, "<<\n"));
 
-  skb->ip_hdr = iph;		/* Fragments can cause ICMP errors too! */
-  /* Is the datagram acceptable? */
-  if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) {
-	DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n"));
-	DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->saddr)));
-	DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->daddr)));
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-	return(0);
-  }
-  
-  if (iph->ihl != 5) {  	/* Fast path for the typical optionless IP packet. */
-      ip_print(iph);		/* Bogus, only for debugging. */
-      memset((char *) &opt, 0, sizeof(opt));
-      if (do_options(iph, &opt) != 0)
-	  return 0;
-      opts_p = 1;
-  }
+	ip_statistics.IpInReceives++;
+	
+	DPRINTF((DBG_IP, "<<\n"));
+
+	/*
+	 *	Tag the ip header of this packet so we can find it
+	 */
+	 
+	skb->ip_hdr = iph;
+
+	/*
+	 *	Is the datagram acceptable? 
+	 *
+	 *	1.	Length at least the size of an ip header
+	 *	2.	Version of 4
+	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+	 *	(4.	We ought to check for IP multicast addresses and undefined types.. does this matter ?)
+	 */
+
+  	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) 
+  	{
+  		ip_statistics.IpInHdrErrors++;
+		DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n"));
+		DPRINTF((DBG_IP, "    SRC = %s   ", in_ntoa(iph->saddr)));
+		DPRINTF((DBG_IP, "    DST = %s (ignored)\n", in_ntoa(iph->daddr)));
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+	}
+	
+	/*
+	 *	Our transport medium may have padded the buffer out. Now we know it
+	 *	is IP we can trim to the true length of the frame.
+	 */
+	 
+	skb->len=ntohs(iph->tot_len);
+
+	/*
+	 *	Next anaylse the packet for options. Studies show under one packet in
+	 *	a thousand have options....
+	 */
+	   
+	if (iph->ihl != 5) 
+	{  	/* Fast path for the typical optionless IP packet. */
+      		ip_print(iph);		/* Bogus, only for debugging. */
+      		memset((char *) &opt, 0, sizeof(opt));
+      		if (do_options(iph, &opt) != 0)
+	  		return 0;
+      		opts_p = 1;
+	}
+
+	/*
+	 *	Remember if the frame is fragmented.
+	 */
 
-  if (iph->frag_off & 0x0020)
-  	is_frag|=1;
-  if (ntohs(iph->frag_off) & 0x1fff)
-  	is_frag|=2;
+	if (iph->frag_off & 0x0020)
+  		is_frag|=1;
+  	
+  	/*
+  	 *	Last fragment ?
+  	 */
+  	 
+  	if (ntohs(iph->frag_off) & 0x1fff)
+  		is_frag|=2;
   	
-  /* Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday. */
-  if ((brd = chk_addr(iph->daddr)) == 0) {
+  	/* 
+  	 *	Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday. 
+  	 *
+  	 *	This is inefficient. While finding out if it is for us we could also compute
+  	 *	the routing table entry. This is where the great unified cache theory comes
+  	 *	in as and when someone impliments it
+  	 */
+
+	if ((brd = ip_chk_addr(iph->daddr)) == 0) 
+	{
+	
+		/*
+		 *	The packet is for another target. Forward the frame
+		 */
+		 
 #ifdef CONFIG_IP_FORWARD
-	ip_forward(skb, dev, is_frag);
+		ip_forward(skb, dev, is_frag);
 #else
-	printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n",
+		printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n",
 			iph->saddr,iph->daddr);
+		ip_statistics.IpInAddrErrors++;
 #endif			
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-	return(0);
-  }
+		/*
+		 *	The forwarder is inefficient and copies the packet. We 
+		 *	free the original now.
+		 */
+		 
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+  	}
 
-  /*
-   * Reassemble IP fragments. 
-   */
+  	/*
+  	 * Reassemble IP fragments. 
+  	 */
 
-  if(is_frag)
-  {
+  	if(is_frag)
+  	{
 #ifdef CONFIG_IP_DEFRAG
-        skb=ip_defrag(iph,skb,dev);
-        if(skb==NULL)
-        {
-        	return 0;
-        }
-        iph=skb->h.iph;
+		/* Defragment. Obtain the complete packet if there is one */
+		skb=ip_defrag(iph,skb,dev);
+        	if(skb==NULL)
+        		return 0;
+	        iph=skb->h.iph;
 #else
-	printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
-	printk("    SRC = %s   ", in_ntoa(iph->saddr));
-	printk("    DST = %s (ignored)\n", in_ntoa(iph->daddr));
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-	return(0);
+		printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
+		printk("    SRC = %s   ", in_ntoa(iph->saddr));
+		printk("    DST = %s (ignored)\n", in_ntoa(iph->daddr));
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
 #endif
-  }
-
+	}
 
+	/*
+	 *	Point into the IP datagram, just past the header. 
+	 */
 
-  if(brd==IS_INVBCAST)
-  {
-/*	printk("Invalid broadcast address from %x [target %x] (Probably they have a wrong netmask)\n",
-		iph->saddr,iph->daddr);*/
-  	skb->sk=NULL;
-  	kfree_skb(skb,FREE_WRITE);
-  	return(0);
-  }
-  
-  /* Point into the IP datagram, just past the header. */
+	skb->ip_hdr = iph;
+	skb->h.raw += iph->ihl*4;
+	
+	/*
+	 *	skb->h.raw now points at the protocol beyond the IP header.
+	 */
+	 
+	hash = iph->protocol & (MAX_INET_PROTOS -1);
+	for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
+	{
+      		struct sk_buff *skb2;
 
-  skb->ip_hdr = iph;
-  skb->h.raw += iph->ihl*4;
-  hash = iph->protocol & (MAX_INET_PROTOS -1);
-  for (ipprot = (struct inet_protocol *)inet_protos[hash];
-       ipprot != NULL;
-       ipprot=(struct inet_protocol *)ipprot->next)
-    {
-       struct sk_buff *skb2;
-
-       if (ipprot->protocol != iph->protocol) continue;
-       DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot));
-       print_ipprot(ipprot);
+       		if (ipprot->protocol != iph->protocol) 
+       			continue;
+		DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot));
+		print_ipprot(ipprot);
 
        /*
-	* See if we need to make a copy of it.  This will
-	* only be set if more than one protocol wants it. 
-	* and then not for the last one.
+	* 	See if we need to make a copy of it.  This will
+	* 	only be set if more than one protocol wants it. 
+	* 	and then not for the last one.
+	*
+	* 	This is an artifact of poor upper protocol design. 
+	*	Because the upper protocols damage the actual packet
+	*	we must do copying. In actual fact it's even worse
+	*	than this as TCP may hold on to the buffer.
 	*/
-       if (ipprot->copy) {
-		skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC);
-		if (skb2 == NULL) 
-			continue;
-		memcpy(skb2, skb, skb->mem_len);
-		skb2->mem_addr = skb2;
-		skb2->ip_hdr = (struct iphdr *)(
-				(unsigned long)skb2 +
-				(unsigned long) skb->ip_hdr -
-				(unsigned long)skb);
-		skb2->h.raw = (unsigned char *)(
-				(unsigned long)skb2 +
-				(unsigned long) skb->h.raw -
-				(unsigned long)skb);
-		skb2->free=1;
-	} else {
-		skb2 = skb;
-	}
-	flag = 1;
+		if (ipprot->copy) 
+		{
+#if 0		
+			skb2 = alloc_skb(skb->mem_len-sizeof(struct sk_buff), GFP_ATOMIC);
+			if (skb2 == NULL) 
+				continue;
+			memcpy(skb2, skb, skb2->mem_len);
+			skb2->ip_hdr = (struct iphdr *)(
+					(unsigned long)skb2 +
+					(unsigned long) skb->ip_hdr -
+					(unsigned long)skb);
+			skb2->h.raw = (unsigned char *)(
+					(unsigned long)skb2 +
+					(unsigned long) skb->h.raw -
+					(unsigned long)skb);
+			skb2->free=1;
+#else
+			skb2 = skb_clone(skb, GFP_ATOMIC);
+			if(skb2==NULL)
+				continue;
+#endif							
+		} 
+		else 
+		{
+			skb2 = skb;
+		}
+		flag = 1;
 
-       /*
-	* Pass on the datagram to each protocol that wants it,
-	* based on the datagram protocol.  We should really
-	* check the protocol handler's return values here...
-	*/
-	ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
-			(ntohs(iph->tot_len) - (iph->ihl * 4)),
-			iph->saddr, 0, ipprot);
+	       /*
+		* Pass on the datagram to each protocol that wants it,
+		* based on the datagram protocol.  We should really
+		* check the protocol handler's return values here...
+		*/
+		ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
+				(ntohs(iph->tot_len) - (iph->ihl * 4)),
+				iph->saddr, 0, ipprot);
 
-  }
+	}
 
-  /*
-   * All protocols checked.
-   * If this packet was a broadcast, we may *not* reply to it, since that
-   * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
-   * ICMP reply messages get queued up for transmission...)
-   */
-  if (!flag) {
-	if (brd != IS_BROADCAST)
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-  }
+	/*
+	 * All protocols checked.
+	 * If this packet was a broadcast, we may *not* reply to it, since that
+	 * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+	 * ICMP reply messages get queued up for transmission...)
+	 */
 
-  return(0);
+	if (!flag) 
+	{
+		if (brd != IS_BROADCAST)
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+		kfree_skb(skb, FREE_WRITE);
+	}
+
+	return(0);
 }
 
 
@@ -1371,178 +1749,287 @@
  * Queues a packet to be sent, and starts the transmitter
  * if necessary.  if free = 1 then we free the block after
  * transmit, otherwise we don't.
- * This routine also needs to put in the total length, and
- * compute the checksum.
+ * This routine also needs to put in the total length,
+ * and compute the checksum
  */
-void
-ip_queue_xmit(struct sock *sk, struct device *dev, 
+ 
+void ip_queue_xmit(struct sock *sk, struct device *dev, 
 	      struct sk_buff *skb, int free)
 {
-  struct iphdr *iph;
-  unsigned char *ptr;
+  	struct iphdr *iph;
+  	unsigned char *ptr;
 
-  if (sk == NULL) free = 1;
-  if (dev == NULL) {
-	printk("IP: ip_queue_xmit dev = NULL\n");
-	return;
-  }
-  IS_SKB(skb);
-  skb->free = free;
-  skb->dev = dev;
-  skb->when = jiffies;
+	/* All buffers without an owner socket get freed */
+  	if (sk == NULL) 
+  		free = 1;
+  	
+  	/* Sanity check */
+  	if (dev == NULL) 
+  	{
+		printk("IP: ip_queue_xmit dev = NULL\n");
+		return;
+  	}
   
-  DPRINTF((DBG_IP, ">>\n"));
-  ptr = skb->data;
-  ptr += dev->hard_header_len;
-  iph = (struct iphdr *)ptr;
-  skb->ip_hdr = iph;
-  iph->tot_len = ntohs(skb->len-dev->hard_header_len);
-
-  if(skb->len > dev->mtu)
-  {
-/*  	printk("Fragment!\n");*/
-  	ip_fragment(sk,skb,dev,0);
   	IS_SKB(skb);
-  	kfree_skb(skb,FREE_WRITE);
-  	return;
-  }
+  	
+  	/*
+  	 *	Do some book-keeping in the packet for later
+  	 */
+
+  	skb->free = free;
+  	skb->dev = dev;
+  	skb->when = jiffies;
   
-  ip_send_check(iph);
-  ip_print(iph);
-  skb->next = NULL;
-
-  /* See if this is the one trashing our queue. Ross? */
-  skb->magic = 1;
-  if (!free) {
-	skb->link3 = NULL;
-	sk->packets_out++;
-	cli();
-	if (sk->send_head == NULL) {
-		sk->send_tail = skb;
-		sk->send_head = skb;
-	} else {
-		/* See if we've got a problem. */
-		if (sk->send_tail == NULL) {
-			printk("IP: ***bug sk->send_tail == NULL != sk->send_head\n");
-			sort_send(sk);
-		} else {
+	DPRINTF((DBG_IP, ">>\n"));
+
+	/*
+	 *	Find the IP header and set the length. This is bad
+	 *	but once we get the skb data handling code in the
+	 *	hardware will push its header sensibly and we will
+	 *	set skb->ip_hdr to avoid this mess and the fixed
+	 *	header length problem
+	 */
+	 
+	ptr = skb->data;
+	ptr += dev->hard_header_len;
+	iph = (struct iphdr *)ptr;
+	skb->ip_hdr = iph;
+	iph->tot_len = ntohs(skb->len-dev->hard_header_len);
+
+	/*
+	 *	Do we need to fragment. Again this is inefficient. 
+	 *	We need to somehow lock the original buffer and use
+	 *	bits of it.
+	 */
+	 
+	if(skb->len > dev->mtu)
+  	{
+		ip_fragment(sk,skb,dev,0);
+		IS_SKB(skb);
+  		kfree_skb(skb,FREE_WRITE);
+  		return;
+	}
+  
+  	/*
+  	 *	Add an IP checksum
+  	 */
+  	 
+	ip_send_check(iph);
+	
+	/*
+	 *	Print the frame when debugging
+	 */
+	ip_print(iph);
+
+	/*
+	 *	More debugging. You cannot queue a packet already on a list
+	 *	Spot this and moan loudly.
+	 */
+	if (skb->next != NULL) 
+	{
+		printk("ip_queue_xmit: next != NULL\n");
+		skb_unlink(skb);
+	}
+
+	/*
+	 *	If a sender wishes the packet to remain unfreed
+	 *	we add it to his send queue. This arguably belongs
+	 *	in the TCP level since nobody elses uses it. BUT
+	 *	remember IPng might change all the rules.
+	 */
+	 
+	if (!free) 
+	{
+		unsigned long flags;
+		/* The socket now has more outstanding blocks */
+		
+		sk->packets_out++;
+		
+		/* Protect the list for a moment */
+		save_flags(flags);
+		cli();
+		
+		if (skb->link3 != NULL) 
+		{
+			printk("ip.c: link3 != NULL\n");
+			skb->link3 = NULL;
+		}
+		if (sk->send_head == NULL) 
+		{
+			sk->send_tail = skb;
+			sk->send_head = skb;
+		}
+		else 
+		{
 			sk->send_tail->link3 = skb;
 			sk->send_tail = skb;
 		}
-	}
-	sti();
-	reset_timer(sk, TIME_WRITE, sk->rto);
-  } else {
-	skb->sk = sk;
-  }
+		/* skb->link3 is NULL */
+		
+		/* Interrupt restore */
+		restore_flags(flags);
+		/* Set the IP write timeout to the round trip time for the packet.
+		   If an acknowledge has not arrived by then we may wish to act */	
+		reset_timer(sk, TIME_WRITE, sk->rto);
+	} 
+	else 
+		/* Remember who owns the buffer */
+		skb->sk = sk;
 
-  /* If the indicated interface is up and running, kick it. */
-  if (dev->flags & IFF_UP) {
-	if (sk != NULL) {
-		dev->queue_xmit(skb, dev, sk->priority);
+	/*
+	 *	If the indicated interface is up and running, send the packet. 
+	 */
+	ip_statistics.IpOutRequests++;
+	 
+	if (dev->flags & IFF_UP) 
+	{
+		/* 
+		 *	If we have an owner use its priority setting,
+		 *	otherwise use NORMAL
+		 */
+		
+		if (sk != NULL) 
+		{
+			dev_queue_xmit(skb, dev, sk->priority);
+		}
+		else 
+		{
+			dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+		}
 	} 
-	else {
-		dev->queue_xmit(skb, dev, SOPRI_NORMAL);
+	else 
+	{
+		ip_statistics.IpOutDiscards++;
+		if (free) 
+			kfree_skb(skb, FREE_WRITE);
 	}
-  } else {
-	if (free) kfree_skb(skb, FREE_WRITE);
-  }
 }
 
 
-void
-ip_do_retransmit(struct sock *sk, int all)
+/*
+ *	A socket has timed out on its send queue and wants to do a
+ *	little retransmitting. Currently this means TCP.
+ */
+
+void ip_do_retransmit(struct sock *sk, int all)
 {
-  struct sk_buff * skb;
-  struct proto *prot;
-  struct device *dev;
-  int retransmits;
-
-  prot = sk->prot;
-  skb = sk->send_head;
-  retransmits = sk->retransmits;
-  while (skb != NULL) {
-	dev = skb->dev;
-	/* I know this can't happen but as it does.. */
-	if(dev==NULL)
+  	struct sk_buff * skb;
+  	struct proto *prot;
+  	struct device *dev;
+  	int retransmits;
+
+	prot = sk->prot;
+	skb = sk->send_head;
+	retransmits = sk->retransmits;
+	
+	while (skb != NULL) 
 	{
-		printk("ip_retransmit: NULL device bug!\n");
-		goto oops;
-	}
+		dev = skb->dev;
+		IS_SKB(skb);
+#if 0
+	/********** THIS IS NOW DONE BY THE DEVICE LAYER **********/	
+		/*
+		 * 	The rebuild_header function sees if the ARP is done.
+		 * 	If not it sends a new ARP request, and if so it builds
+		 * 	the header. It isn't really needed here, and with the
+		 *	new ARP pretty much will not happen.
+		 */
+		 
+		if (!skb->arp) 
+		{
+			if (dev->rebuild_header(skb->data, dev, skb->raddr, NULL)) 
+			{
+				if (!all) 
+					break;
+				skb = skb->link3;
+				continue;
+			}
+		}
+#endif
+		skb->when = jiffies;
 
-	IS_SKB(skb);
-	
-	/*
-	 * The rebuild_header function sees if the ARP is done.
-	 * If not it sends a new ARP request, and if so it builds
-	 * the header.
-	 */
-        cli();	/* We might get interrupted by an arp reply here and fill
-		   the frame in twice. Because of the technique used this
-		   would be a little sad */
-	if (!skb->arp) {
-		if (dev->rebuild_header(skb->data, dev)) {
-			sti();	/* Failed to rebuild - next */
-			if (!all) break;
-			skb = (struct sk_buff *)skb->link3;
-			continue;
+		/* 
+		 *	If the interface is (still) up and running, kick it. 
+		 */
+		
+		if (dev->flags & IFF_UP) 
+		{
+			/*
+			 *	If the packet is still being sent by the device/protocol
+			 *	below then don't retransmit. This is both needed, and good -
+			 *	especially with connected mode AX.25 where it stops resends
+			 *	occuring of an as yet unsent anyway frame!
+			 *	We still add up the counts as the round trip time wants
+			 *	adjusting.
+			 */
+			if (sk && !skb_device_locked(skb))
+			{
+				/* Remove it from any existing driver queue first! */
+				skb_unlink(skb);
+				/* Now queue it */
+				ip_statistics.IpOutRequests++;
+				dev_queue_xmit(skb, dev, sk->priority);
+			}
 		}
-	}
-	skb->arp = 1;
-	sti();
-	skb->when = jiffies;
+		
+		/*
+		 *	Count retransmissions
+		 */
+		retransmits++;
+		sk->prot->retransmits ++;
+		
+		/*
+		 *	Only one retransmit requested.
+		 */
+		if (!all) 
+			break;
 
-	/* If the interface is (still) up and running, kick it. */
-	if (dev->flags & IFF_UP) {
-		if (sk && !skb_device_locked(skb))
-			dev->queue_xmit(skb, dev, sk->priority);
-	/*	  else dev->queue_xmit(skb, dev, SOPRI_NORMAL ); CANNOT HAVE SK=NULL HERE */
+		/*
+		 *	This should cut it off before we send too many packets. 
+		 */
+		if (sk->retransmits > sk->cong_window) 
+			break;
+		skb = skb->link3;
 	}
-
-oops:	retransmits++;
-	sk->prot->retransmits ++;
-	if (!all) break;
-
-	/* This should cut it off before we send too many packets. */
-	if (sk->retransmits > sk->cong_window) break;
-	skb = (struct sk_buff *)skb->link3;
-  }
 }
 
 /*
- * This is the normal code called for timeouts.  It does the retransmission
- * and then does backoff.  ip_do_retransmit is separated out because
- * tcp_ack needs to send stuff from the retransmit queue without
- * initiating a backoff.
+ * 	This is the normal code called for timeouts.  It does the retransmission
+ * 	and then does backoff.  ip_do_retransmit is separated out because
+ * 	tcp_ack needs to send stuff from the retransmit queue without
+ * 	initiating a backoff.
  */
 
-void
-ip_retransmit(struct sock *sk, int all)
+void ip_retransmit(struct sock *sk, int all)
 {
-  ip_do_retransmit(sk, all);
+  	ip_do_retransmit(sk, all);
+
+  	/*
+  	 * Increase the timeout each time we retransmit.  Note that
+  	 * we do not increase the rtt estimate.  rto is initialized
+  	 * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
+  	 * that doubling rto each time is the least we can get away with.
+  	 * In KA9Q, Karn uses this for the first few times, and then
+  	 * goes to quadratic.  netBSD doubles, but only goes up to *64,
+  	 * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
+  	 * defined in the protocol as the maximum possible RTT.  I guess
+  	 * we'll have to use something other than TCP to talk to the
+   	 * University of Mars.
+   	 */
 
-  /*
-   * Increase the timeout each time we retransmit.  Note that
-   * we do not increase the rtt estimate.  rto is initialized
-   * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
-   * that doubling rto each time is the least we can get away with.
-   * In KA9Q, Karns uses this for the first few times, and then
-   * goes to quadratic.  netBSD doubles, but only goes up to *64,
-   * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
-   * defined in the protocol as the maximum possible RTT.  I guess
-   * we'll have to use something other than TCP to talk to the
-   * University of Mars.
-   */
-
-  sk->retransmits++;
-  sk->backoff++;
-  sk->rto = min(sk->rto << 1, 120*HZ);
-  reset_timer(sk, TIME_WRITE, sk->rto);
+	sk->retransmits++;
+	sk->backoff++;
+	sk->rto = min(sk->rto << 1, 120*HZ);
+	reset_timer(sk, TIME_WRITE, sk->rto);
 }
 
 /*
  *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
  *	an IP socket.
+ *
+ *	We impliment IP_TOS (type of service), IP_TTL (time to live).
+ *
+ *	Next release we will sort out IP_OPTIONS since for some people are kind of important.
  */
  
 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
@@ -1579,6 +2066,11 @@
 	}
 }
 
+/*
+ *	Get the options. Note for future reference. The GET of IP options gets the
+ *	_received_ ones. The set sets the _sent_ ones.
+ */
+ 
 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 {
 	int val,err;
@@ -1608,4 +2100,32 @@
   	put_fs_long(val,(unsigned long *)optval);
 
   	return(0);
+}
+
+/*
+ *	IP protocol layer initialiser
+ */
+ 
+static struct packet_type ip_packet_type = 
+{
+	0,	/* MUTTER ntohs(ETH_P_IP),*/
+	0,		/* copy */
+	ip_rcv,
+	NULL,
+	NULL,
+};
+ 
+ 
+/*
+ *	IP registers the packet type and then calls the subprotocol initialisers
+ */
+ 
+void ip_init(void)
+{
+	ip_packet_type.type=htons(ETH_P_IP);
+	dev_add_pack(&ip_packet_type);
+/*	ip_raw_init();
+	ip_packet_init();
+	ip_tcp_init();
+	ip_udp_init();*/
 }
diff -u --recursive --new-file v1.1.3/linux/net/inet/ip.h linux/net/inet/ip.h
--- v1.1.3/linux/net/inet/ip.h	Sun Feb  6 12:17:40 1994
+++ linux/net/inet/ip.h	Wed Apr 13 12:52:24 1994
@@ -9,6 +9,7 @@
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -21,6 +22,9 @@
 
 #include <linux/ip.h>
 
+#ifndef _SNMP_H
+#include "snmp.h"
+#endif
 
 #include "sock.h"	/* struct sock */
 
@@ -61,7 +65,7 @@
 
 extern int		backoff(int n);
 
-extern void		ip_print(struct iphdr *ip);
+extern void		ip_print(const struct iphdr *ip);
 extern int		ip_ioctl(struct sock *sk, int cmd,
 				 unsigned long arg);
 extern void		ip_route_check(unsigned long daddr);
@@ -81,5 +85,7 @@
 extern void		ip_do_retransmit(struct sock *sk, int all);
 extern int 		ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
 extern int 		ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+extern void		ip_init(void);
 
+extern struct ip_mib	ip_statistics;
 #endif	/* _IP_H */
diff -u --recursive --new-file v1.1.3/linux/net/inet/ipx.c linux/net/inet/ipx.c
--- v1.1.3/linux/net/inet/ipx.c
+++ linux/net/inet/ipx.c	Wed Apr 13 12:52:25 1994
@@ -0,0 +1,1123 @@
+/*
+ *	Implements an IPX socket layer (badly - but I'm working on it).
+ *
+ *	This code is derived from work by
+ *		Ross Biro	: 	Writing the original IP stack
+ *		Fred Van Kempen :	Tidying up the TCP/IP
+ *
+ *	Many thanks go to Keith Baker, Institute For Industrial Information
+ *	Technology Ltd, Swansea University for allowing me to work on this
+ *	in my own time even though it was in some ways related to commercial
+ *	work I am currently employed to do there.
+ *
+ *	All the material in this file is subject to the Gnu license version 2.
+ *	Neither Alan Cox nor the Swansea University Computer Society admit liability
+ *	nor provide warranty for any of this software. This material is provided 
+ *	as is and at no charge.		
+ *
+ *	Revision 0.21:	Uses the new generic socket option code.
+ *	Revision 0.22:	Gcc clean ups and drop out device registration. Use the
+ *			new multi-protocol edition of hard_header 
+ *	Revision 0.23:  IPX /proc by Mark Evans.
+ *     			Adding a route will overwrite any existing route to the same
+ *			network.
+ *	Revision 0.24:	Supports new /proc with no 4K limit
+ *
+ */
+ 
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/ipx.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "sock.h"
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#ifdef CONFIG_IPX
+/***********************************************************************************************************************\
+*															*
+*						Handlers for the socket list.						*
+*															*
+\***********************************************************************************************************************/
+
+static ipx_socket *volatile ipx_socket_list=NULL;
+
+/*
+ *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
+ *	handler using this technique. They can be added although we do not
+ *	use this facility.
+ */
+ 
+static void ipx_remove_socket(ipx_socket *sk)
+{
+	ipx_socket *s;
+	
+	cli();
+	s=ipx_socket_list;
+	if(s==sk)
+	{
+		ipx_socket_list=s->next;
+		sti();
+		return;
+	}
+	while(s && s->next)
+	{
+		if(s->next==sk)
+		{
+			s->next=sk->next;
+			sti();
+			return;
+		}
+		s=s->next;
+	}
+	sti();
+}
+
+static void ipx_insert_socket(ipx_socket *sk)
+{
+	cli();
+	sk->next=ipx_socket_list;
+	ipx_socket_list=sk;
+	sti();
+}
+
+static ipx_socket *ipx_find_socket(int port)
+{
+	ipx_socket *s;
+	s=ipx_socket_list;
+	while(s)
+	{
+		if(s->ipx_source_addr.sock==port)
+		{
+			return(s);
+		}
+		s=s->next;
+	}
+	return(NULL);
+}
+
+/*
+ *	This is only called from user mode. Thus it protects itself against
+ *	interrupt users but doesn't worry about being called during work.
+ *	Once it is removed from the queue no interrupt or bottom half will
+ *	touch it and we are (fairly 8-) ) safe.
+ */
+ 
+static void ipx_destroy_socket(ipx_socket *sk)
+{
+	struct sk_buff *skb;
+	ipx_remove_socket(sk);
+	
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+	{
+		kfree_skb(skb,FREE_READ);
+	}
+	
+	kfree_s(sk,sizeof(*sk));
+}
+
+
+/* Called from proc fs */
+int ipx_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	ipx_socket *s;
+	int len=0;
+	off_t pos=0;
+	off_t begin=0;
+
+	/* Theory.. Keep printing in the same place until we pass offset */
+	
+	len += sprintf (buffer,"Type local_address             rem_address              tx_queue rx_queue st uid\n");
+	for (s = ipx_socket_list; s != NULL; s = s->next)
+	{
+		len += sprintf (buffer+len,"%02X   ", s->ipx_type);
+		len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%02X ", htonl(s->ipx_source_addr.net),
+			s->ipx_source_addr.node[0], s->ipx_source_addr.node[1], s->ipx_source_addr.node[2],
+			s->ipx_source_addr.node[3], s->ipx_source_addr.node[4], s->ipx_source_addr.node[5],
+			htons(s->ipx_source_addr.sock));
+		len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%02X ", htonl(s->ipx_dest_addr.net),
+			s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], s->ipx_dest_addr.node[2],
+			s->ipx_dest_addr.node[3], s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
+			htons(s->ipx_dest_addr.sock));
+		len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc);
+		len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
+		
+		/* Are we still dumping unwanted data then discard the record */
+		pos=begin+len;
+		
+		if(pos<offset)
+		{
+			len=0;			/* Keep dumping into the buffer start */
+			begin=pos;
+		}
+		if(pos>offset+length)		/* We have dumped enough */
+			break;
+	}
+	
+	/* The data in question runs from begin to begin+len */
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Remove unwanted header data from lenth */
+	if(len>length)
+		len=length;		/* Remove unwanted tail data from length */
+	
+	return len;
+}
+
+/*******************************************************************************************************************\
+*													            *
+*	            			Routing tables for the IPX socket layer				            *
+*														    *
+\*******************************************************************************************************************/
+
+
+static ipx_route *ipx_router_list=NULL;
+
+static ipx_route *ipxrtr_get_dev(long net)
+{
+	ipx_route *r;
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	r=ipx_router_list;
+	while(r!=NULL)
+	{
+		if(r->net==net)
+		{
+			restore_flags(flags);
+			return r;
+		}
+		r=r->next;
+	}
+	restore_flags(flags);
+	return NULL;
+}
+
+static int ipxrtr_create(struct ipx_route_def *r)
+{
+	ipx_route *rt=ipxrtr_get_dev(r->ipx_network);
+	struct device *dev;
+
+	if(r->ipx_router_network!=0)
+	{
+		/* Adding an indirect route */
+		ipx_route *rt1=ipxrtr_get_dev(r->ipx_router_network);
+		if(rt1==NULL)
+			return -ENETUNREACH;
+		if(rt1->flags&IPX_RT_ROUTED)
+			return -EMULTIHOP;
+		if (rt==NULL)
+		{
+			rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);	/* Because we are brave and don't lock the table! */
+			if(rt==NULL)
+				return -EAGAIN;
+			rt->next=ipx_router_list;
+			ipx_router_list=rt;
+		}
+		rt->net=r->ipx_network;
+		rt->router_net=r->ipx_router_network;
+		memcpy(rt->router_node,r->ipx_router_node,sizeof(rt->router_node));
+		rt->flags=(rt1->flags&IPX_RT_BLUEBOOK)|IPX_RT_ROUTED;
+		rt->dev=rt1->dev;
+		return 0;
+	}
+	/* Add a direct route */
+	dev=dev_get(r->ipx_device);
+	if(dev==NULL)
+		return -ENODEV;
+	/* Check addresses are suitable */
+	if(dev->addr_len>6)
+		return -EINVAL;
+	if(dev->addr_len<2)
+		return -EINVAL;
+	/* Ok now create */
+	if (rt==NULL)
+	{
+		rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);	/* Because we are brave and don't lock the table! */
+		if(rt==NULL)
+			return -EAGAIN;
+		rt->next=ipx_router_list;
+		ipx_router_list=rt;
+	}
+	rt->router_net=0;
+	memset(rt->router_node,0,sizeof(rt->router_node));
+	rt->dev=dev;
+	rt->net=r->ipx_network;
+	rt->flags=r->ipx_flags&IPX_RT_BLUEBOOK;
+	return 0;
+}
+
+static int ipxrtr_delete(long net)
+{
+	ipx_route *r=ipx_router_list;
+	if(r->net==net)
+	{
+		ipx_router_list=r->next;
+		return 0;
+	}
+	while(r->next!=NULL)
+	{
+		if(r->next->net==net)
+		{
+			ipx_route *d=r->next;
+			r->next=d->next;
+			kfree_s(d,sizeof(ipx_route));
+			return 0;
+		}
+		r=r->next;
+	}
+	return -ENOENT;
+}
+
+static int ipxrtr_ioctl(unsigned int cmd, void *arg)
+{
+	int err;
+	switch(cmd)
+	{
+		case SIOCDELRT:
+			err=verify_area(VERIFY_READ,arg,sizeof(long));
+			if(err)
+				return err;
+			return ipxrtr_delete(get_fs_long(arg));
+		case SIOCADDRT:
+		{
+			struct ipx_route_def f;
+			err=verify_area(VERIFY_READ,arg,sizeof(f));
+			if(err)
+				return err;
+			memcpy_fromfs(&f,arg,sizeof(f));
+			return ipxrtr_create(&f);
+		}
+		default:
+			return -EINVAL;
+	}
+}
+
+/* Called from proc fs */
+int ipx_rt_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	ipx_route *rt;
+	int len=0;
+	off_t pos=0;
+	off_t begin=0;
+
+	len += sprintf (buffer,"Net      Router                Flags Dev\n");
+	for (rt = ipx_router_list; rt != NULL; rt = rt->next)
+	{
+		len += sprintf (buffer+len,"%08lX %08lX:%02X%02X%02X%02X%02X%02X %02X    %s\n", ntohl(rt->net),
+			ntohl(rt->router_net), rt->router_node[0], rt->router_node[1], rt->router_node[2],
+			rt->router_node[3], rt->router_node[4], rt->router_node[5], rt->flags, rt->dev->name);
+		pos=begin+len;
+		if(pos<offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos>offset+length)
+			break;
+	}
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	return len;
+}
+
+/*******************************************************************************************************************\
+*													            *
+*	      Handling for system calls applied via the various interfaces to an IPX socket object		    *
+*														    *
+\*******************************************************************************************************************/
+ 
+static int ipx_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	ipx_socket *sk;
+	
+	sk=(ipx_socket *)sock->data;
+	
+	if(sk==NULL)
+	{
+		printk("IPX:fcntl:passed sock->data=NULL\n");
+		return(0);
+	}
+	
+	switch(cmd)
+	{
+		default:
+			return(-EINVAL);
+	}
+}
+
+static int ipx_setsockopt(struct socket *sock, int level, int optname,
+	char *optval, int optlen)
+{
+	ipx_socket *sk;
+	int err,opt;
+	
+	sk=(ipx_socket *)sock->data;
+	
+	if(sk==NULL)
+	{
+		printk("IPX:setsockopt:passed sock->data=NULL\n");
+		return 0;
+	}
+	
+	if(optval==NULL)
+		return(-EINVAL);
+	err=verify_area(VERIFY_READ,optval,sizeof(int));
+	if(err)
+		return err;
+	opt=get_fs_long((unsigned long *)optval);
+	
+	switch(level)
+	{
+		case SOL_IPX:
+			switch(optname)
+			{
+				case IPX_TYPE:
+					if(!suser())
+						return(-EPERM);
+					sk->ipx_type=opt;
+					return 0;
+				default:
+					return -EOPNOTSUPP;
+			}
+			break;
+			
+		case SOL_SOCKET:		
+			return sock_setsockopt(sk,level,optname,optval,optlen);
+
+		default:
+			return -EOPNOTSUPP;
+	}
+}
+
+static int ipx_getsockopt(struct socket *sock, int level, int optname,
+	char *optval, int *optlen)
+{
+	ipx_socket *sk;
+	int val=0;
+	int err;
+	
+	sk=(ipx_socket *)sock->data;
+	if(sk==NULL)
+	{
+		printk("IPX:getsockopt:passed NULL sock->data.\n");
+		return 0;
+	}
+
+	switch(level)
+	{
+
+		case SOL_IPX:
+			switch(optname)
+			{
+				case IPX_TYPE:
+					val=sk->ipx_type;
+					break;
+				default:
+					return -ENOPROTOOPT;
+			}
+			break;
+			
+		case SOL_SOCKET:
+			return sock_getsockopt(sk,level,optname,optval,optlen);
+			
+		default:
+			return -EOPNOTSUPP;
+	}
+	err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
+	if(err)
+		return err;
+	put_fs_long(sizeof(int),(unsigned long *)optlen);
+	err=verify_area(VERIFY_WRITE,optval,sizeof(int));
+	put_fs_long(val,(unsigned long *)optval);
+	return(0);
+}
+
+static int ipx_listen(struct socket *sock, int backlog)
+{
+	return -EOPNOTSUPP;
+}
+
+static void def_callback1(struct sock *sk)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk, int len)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static int ipx_create(struct socket *sock, int protocol)
+{
+	ipx_socket *sk;
+	sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+	if(sk==NULL)
+		return(-ENOMEM);
+	switch(sock->type)
+	{
+		case SOCK_DGRAM:
+			break;
+		default:
+			kfree_s((void *)sk,sizeof(*sk));
+			return(-ESOCKTNOSUPPORT);
+	}
+	sk->stamp.tv_sec=0;
+	sk->rmem_alloc=0;
+	sk->dead=0;
+	sk->next=NULL;
+	sk->broadcast=0;
+	sk->rcvbuf=SK_RMEM_MAX;
+	sk->sndbuf=SK_WMEM_MAX;
+	sk->wmem_alloc=0;
+	sk->rmem_alloc=0;
+	sk->inuse=0;
+	sk->dead=0;
+	sk->prot=NULL;	/* So we use default free mechanisms */
+	sk->broadcast=0;
+	sk->err=0;
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->write_queue);
+	sk->send_head=NULL;
+	skb_queue_head_init(&sk->back_log);
+	sk->mtu=512;
+	sk->state=TCP_CLOSE;
+	sk->socket=sock;
+	sk->type=sock->type;
+	sk->ipx_type=0;		/* General user level IPX */
+	sk->debug=0;
+	
+	memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
+	memset(&sk->ipx_source_addr,'\0',sizeof(sk->ipx_source_addr));
+	sk->mtu=IPX_MTU;
+	
+	if(sock!=NULL)
+	{
+		sock->data=(void *)sk;
+		sk->sleep=sock->wait;
+	}
+	
+	sk->state_change=def_callback1;
+	sk->data_ready=def_callback2;
+	sk->write_space=def_callback1;
+	sk->error_report=def_callback1;
+
+	sk->zapped=1;
+	return(0);
+}
+
+static int ipx_dup(struct socket *newsock,struct socket *oldsock)
+{
+	return(ipx_create(newsock,SOCK_DGRAM));
+}
+
+static int ipx_release(struct socket *sock, struct socket *peer)
+{
+	ipx_socket *sk=(ipx_socket *)sock->data;
+	if(sk==NULL)
+		return(0);
+	if(!sk->dead)
+		sk->state_change(sk);
+	sk->dead=1;
+	sock->data=NULL;
+	ipx_destroy_socket(sk);
+	return(0);
+}
+		
+static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
+{
+	ipx_socket *sk;
+	int err;
+	struct sockaddr_ipx addr;
+	struct ipx_route *rt;
+	
+	sk=(ipx_socket *)sock->data;
+	if(sk==NULL)
+	{
+		printk("IPX:bind:sock->data=NULL\n");
+		return 0;
+	}
+
+	if(sk->zapped==0)
+		return(-EIO);
+		
+	err=verify_area(VERIFY_READ,uaddr,addr_len);
+	if(err)
+		return err;
+	if(addr_len!=sizeof(addr))
+		return -EINVAL;
+	memcpy_fromfs(&addr,uaddr,addr_len);
+	
+	if(ntohs(addr.sipx_port)<0x4000 && !suser())
+		return(-EPERM);	/* protect IPX system stuff like routing/sap */
+	
+	/* Source addresses are easy. It must be our network:node pair for
+	   an interface routed to IPX with the ipx routing ioctl() */
+
+	if(ipx_find_socket(addr.sipx_port)!=NULL)
+	{
+		if(sk->debug)
+			printk("IPX: bind failed because port %X in use.\n",
+				(int)addr.sipx_port);
+		return(-EADDRINUSE);	   
+	}
+	sk->ipx_source_addr.sock=addr.sipx_port;
+	memcpy(sk->ipx_source_addr.node,addr.sipx_node,sizeof(sk->ipx_source_addr.node));
+	sk->ipx_source_addr.net=addr.sipx_network;
+	if((rt=ipxrtr_get_dev(sk->ipx_source_addr.net))==NULL)
+	{
+		if(sk->debug)
+			printk("IPX: bind failed (no device for net %lX)\n",
+				sk->ipx_source_addr.net);
+		return(-EADDRNOTAVAIL);
+	}
+	memset(sk->ipx_source_addr.node,'\0',6);
+	memcpy(sk->ipx_source_addr.node,rt->dev->dev_addr,rt->dev->addr_len);
+	ipx_insert_socket(sk);
+	sk->zapped=0;
+	if(sk->debug)
+		printk("IPX: socket is bound.\n");
+	return(0);
+}
+
+static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
+	int addr_len, int flags)
+{
+	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sockaddr_ipx addr;
+	int err;
+	
+	if(sk==NULL)
+	{
+		printk("IPX:connect:sock->data=NULL!\n");
+		return 0;
+	}
+
+	sk->state = TCP_CLOSE;	
+	sock->state = SS_UNCONNECTED;
+	
+	if(addr_len!=sizeof(addr))
+		return(-EINVAL);
+	err=verify_area(VERIFY_READ,uaddr,addr_len);
+	if(err)
+		return err;
+	memcpy_fromfs(&addr,uaddr,sizeof(addr));
+	
+	if(ntohs(addr.sipx_port)<0x4000 && !suser())
+		return -EPERM;
+	if(sk->ipx_source_addr.net==0)	/* Must bind first - no autobinding in this */
+		return -EINVAL;
+		
+	
+	sk->ipx_dest_addr.net=addr.sipx_network;
+	sk->ipx_dest_addr.sock=addr.sipx_port;
+	memcpy(sk->ipx_dest_addr.node,addr.sipx_node,sizeof(sk->ipx_source_addr.node));
+	if(ipxrtr_get_dev(sk->ipx_dest_addr.net)==NULL)
+		return -ENETUNREACH;
+	sock->state = SS_CONNECTED;
+	sk->state=TCP_ESTABLISHED;
+	return(0);
+}
+
+static int ipx_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return(-EOPNOTSUPP);
+}
+
+static int ipx_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	if(newsock->data)
+		kfree_s(newsock->data,sizeof(ipx_socket));
+	return -EOPNOTSUPP;
+}
+
+static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
+	int *uaddr_len, int peer)
+{
+	ipx_address *addr;
+	struct sockaddr_ipx sipx;
+	ipx_socket *sk;
+	int len;
+	int err;
+	
+	sk=(ipx_socket *)sock->data;
+	
+	err = verify_area(VERIFY_WRITE,uaddr_len,sizeof(long));
+	if(err)
+		return err;
+		
+	len = get_fs_long(uaddr_len);
+	
+	err = verify_area(VERIFY_WRITE, uaddr, len);
+	if(err)
+		return err;
+	
+	if(len<sizeof(struct sockaddr_ipx))
+		return -EINVAL;
+		
+	if(peer)
+	{
+		if(sk->state!=TCP_ESTABLISHED)
+			return -ENOTCONN;
+		addr=&sk->ipx_dest_addr;
+	}
+	else
+		addr=&sk->ipx_source_addr;
+		
+	sipx.sipx_family = AF_IPX;
+	sipx.sipx_port = addr->sock;
+	sipx.sipx_network = addr->net;
+	memcpy(sipx.sipx_node,addr->node,sizeof(sipx.sipx_node));
+	memcpy_tofs(uaddr,&sipx,sizeof(sipx));
+	put_fs_long(len,uaddr_len);
+	return(0);
+}
+
+
+int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	/* NULL here for pt means the packet was looped back */
+	ipx_socket *sock;
+	unsigned char *buff;
+	ipx_packet *ipx;
+	ipx_route *rt;
+	
+	buff=skb->data;
+	buff+=dev->hard_header_len;
+	ipx=(ipx_packet *)buff;
+	
+	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM)
+	{
+		/* We don't do checksum options. We can't really. Novell don't seem to have documented them.
+		   If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be
+		   the same... */
+		kfree_skb(skb,FREE_READ);
+		return(0);
+	}
+	
+	/* Too small */
+	if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet)+dev->hard_header_len)
+	{
+		kfree_skb(skb,FREE_READ);
+		return(0);
+	}
+	
+	/* Too many hops */
+	if(ipx->ipx_tctrl>16)
+	{
+		kfree_skb(skb,FREE_READ);
+		return(0);
+	}
+	
+	/* Not us/broadcast */
+	if(memcmp(dev->dev_addr,ipx->ipx_dest.node,dev->addr_len)!=0
+	     && memcmp(ipx_broadcast_node,ipx->ipx_dest.node,dev->addr_len)!=0)
+	{
+		/**********************************************************************************************
+		
+			IPX router. Roughly as per the Novell spec. This doesn't handle netbios flood fill
+			broadcast frames. See the Novell IPX router specification for more details
+			(for ftp from ftp.novell.com)
+			
+		***********************************************************************************************/
+		
+		struct sk_buff *skb2;
+		int free_it=0;
+		
+		/* Rule: Don't forward packets that have exceeded the hop limit. This is fixed at 16 in IPX */
+		if(ipx->ipx_tctrl==16)
+		{
+			kfree_skb(skb,FREE_READ);
+			return(0);
+		}
+
+		/* Don't forward if we don't have a route. We ought to go off and start hunting out routes but
+		   if someone needs this _THEY_ can add it */		
+		rt=ipxrtr_get_dev(ipx->ipx_dest.net);
+		if(rt==NULL)   /* Unlike IP we can send on the interface we received. Eg doing DIX/802.3 conversion */
+		{
+			kfree_skb(skb,FREE_READ);
+			return(0);
+		}
+		if(rt->dev->hard_header_len!=dev->hard_header_len)
+		{
+			/* A different header length causes a copy. Awkward to avoid with the current
+			   sk_buff stuff. */
+			skb2=alloc_skb(skb->len,GFP_ATOMIC);
+			if(skb2==NULL)
+			{
+				kfree_skb(skb,FREE_READ);
+				return 0;
+			}
+			free_it=1;
+			skb2->free=1;
+			skb2->len=skb->len;
+			memcpy((char *)(skb2+1),(char *)(skb+1),skb->len);
+			buff=(char *)(skb2+1);
+			ipx=(ipx_packet *)(buff+dev->hard_header_len);
+		}
+		else
+		{
+			skb2=skb;
+			buff=(char *)(skb+1);
+		}
+		/* Now operate on the buffer */
+		/* Increase hop count */
+		ipx->ipx_tctrl++;
+		/* If the route is a gateway then forward to it */
+		
+		dev->hard_header(buff, dev, 
+			(rt->flags&IPX_RT_BLUEBOOK)?ntohs(ETH_P_IPX):ntohs(skb2->len),
+			(rt->flags&IPX_RT_ROUTED)?rt->router_node:ipx->ipx_dest.node,
+			NULL,	/* Our source */
+			skb2->len,
+			skb2);
+			
+		dev_queue_xmit(skb2,dev,SOPRI_NORMAL);
+
+		if(free_it)
+			kfree_skb(skb,FREE_READ);
+		return(0);
+	}
+	/************ End of router: Now sanity check stuff for us ***************/
+	
+	/* Ok its for us ! */
+	
+	sock=ipx_find_socket(ipx->ipx_dest.sock);
+	if(sock==NULL)	/* But not one of our sockets */
+	{
+		kfree_skb(skb,FREE_READ);
+		return(0);
+	}
+	
+	if(sock->rmem_alloc>=sock->rcvbuf)
+	{
+		kfree_skb(skb,FREE_READ);	/* Socket is full */
+		return(0);
+	}
+	
+	sock->rmem_alloc+=skb->mem_len;
+	skb_queue_tail(&sock->receive_queue,skb);
+	if(!sock->dead)
+		sock->data_ready(sock,skb->len);
+	return(0);
+}
+
+
+
+
+static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
+	unsigned flags, struct sockaddr *usip, int addr_len)
+{
+	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip;
+	int err;
+	struct sockaddr_ipx sipx;
+	struct sk_buff *skb;
+	struct device *dev;
+	struct ipx_packet *ipx;
+	int size;
+	ipx_route *rt;
+
+	if(flags)
+		return -EINVAL;
+	if(len<0)
+		return -EINVAL;
+	if(len == 0)
+		return 0;
+		
+	if(usipx)
+	{
+		if(addr_len <sizeof(sipx))
+			return(-EINVAL);
+		err=verify_area(VERIFY_READ,usipx,sizeof(sipx));
+		if(err)
+			return(err);
+		memcpy_fromfs(&sipx,usipx,sizeof(sipx));
+		if(sipx.sipx_family != AF_IPX)
+			return -EINVAL;
+		if(htons(sipx.sipx_port)<0x4000 && !suser())
+			return -EPERM;
+	}
+	else
+	{
+		if(sk->state!=TCP_ESTABLISHED)
+			return -ENOTCONN;
+		sipx.sipx_family=AF_IPX;
+		sipx.sipx_port=sk->ipx_dest_addr.sock;
+		sipx.sipx_network=sk->ipx_dest_addr.net;
+		memcpy(sipx.sipx_node,sk->ipx_dest_addr.node,sizeof(sipx.sipx_node));
+	}
+	
+	if(sk->debug)
+		printk("IPX: sendto: Addresses built.\n");
+	if(!sk->broadcast && memcmp(&sipx.sipx_node,&ipx_broadcast_node,6)==0)
+		return -ENETUNREACH;
+	/* Build a packet */
+	
+	if(sk->debug)
+		printk("IPX: sendto: building packet.\n");
+	err=verify_area(VERIFY_READ,ubuf,len);
+	if(err)
+		return err;
+		
+	size=sizeof(ipx_packet)+len;	/* For mac headers */
+
+	/* Find out where this has to go */
+	rt=ipxrtr_get_dev(sipx.sipx_network);
+	if(rt==NULL)
+	{
+		return -ENETUNREACH;
+	}
+	
+	dev=rt->dev;
+	
+	size+=dev->hard_header_len;
+		
+	if(sk->debug)
+		printk("IPX: sendto: allocating buffer (%d)\n",size-sizeof(struct sk_buff));
+	
+	if(size+sk->wmem_alloc>sk->sndbuf)
+		return -EAGAIN;
+		
+	skb=alloc_skb(size,GFP_KERNEL);
+	if(skb==NULL)
+		return -ENOMEM;
+	if(skb->mem_len+sk->wmem_alloc>sk->sndbuf)
+	{
+		kfree_skb(skb,FREE_WRITE);
+		return -EAGAIN;
+	}
+
+	sk->wmem_alloc+=skb->mem_len;
+	skb->sk=sk;
+	skb->free=1;
+	skb->arp=1;
+	skb->len=size-sizeof(struct sk_buff);
+
+	if(sk->debug)
+		printk("Building MAC header.\n");		
+	skb->dev=rt->dev;
+	
+	dev->hard_header(skb->data,skb->dev,
+		(rt->flags&IPX_RT_BLUEBOOK)?ntohs(ETH_P_IPX):ntohs(len+sizeof(ipx_packet)),
+		(rt->flags&IPX_RT_ROUTED)?rt->router_node:sipx.sipx_node,
+		NULL,
+		len+sizeof(ipx_packet),
+		skb);
+
+	/* Now the IPX */
+	if(sk->debug)
+		printk("Building IPX Header.\n");
+	ipx=(ipx_packet *)skb->data+skb->dev->hard_header_len;
+	ipx->ipx_checksum=0xFFFF;
+	ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
+	ipx->ipx_tctrl=0;
+	ipx->ipx_type=sk->ipx_type;
+	memcpy(&ipx->ipx_source,&sk->ipx_source_addr,sizeof(ipx->ipx_source));
+	ipx->ipx_dest.net=sipx.sipx_network;
+	memcpy(ipx->ipx_dest.node,sipx.sipx_node,sizeof(ipx->ipx_dest.node));
+	ipx->ipx_dest.sock=sipx.sipx_port;
+	if(sk->debug)
+		printk("IPX: Appending user data.\n");
+	/* User data follows immediately after the IPX data */
+	memcpy_fromfs((char *)(ipx+1),ubuf,len);
+	if(sk->debug)
+		printk("IPX: Transmitting buffer\n");
+	if(dev->flags&IFF_LOOPBACK)
+		/* loop back */
+		ipx_rcv(skb,dev,NULL);
+	else
+		dev_queue_xmit(skb,dev,SOPRI_NORMAL);
+	return len;
+}
+
+static int ipx_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags)
+{
+	return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0);
+}
+
+static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
+{
+	return ipx_send(sock,ubuf,size,noblock,0);
+}
+
+static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
+		   unsigned flags, struct sockaddr *sip, int *addr_len)
+{
+	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)sip;
+	/* FILL ME IN */
+	int copied = 0;
+	struct sk_buff *skb;
+	int er;
+
+	if(sk->err)
+	{
+		er= -sk->err;
+		sk->err=0;
+		return er;
+	}
+	
+	if(size==0)
+		return 0;
+	if(size<0)
+		return -EINVAL;
+	if(addr_len)
+	{
+		er=verify_area(VERIFY_WRITE,addr_len,sizeof(*addr_len));
+		if(er)
+			return er;
+		put_fs_long(sizeof(*sipx),addr_len);
+	}
+	if(sipx)
+	{
+		er=verify_area(VERIFY_WRITE,sipx,sizeof(*sipx));
+		if(er)
+			return er;
+	}
+	er=verify_area(VERIFY_WRITE,ubuf,size);
+	if(er)
+		return er;
+	skb=skb_recv_datagram(sk,flags,noblock,&er);
+	if(skb==NULL)
+		return er;
+	copied=(size<skb->len)?size:skb->len;
+	skb_copy_datagram(skb,sizeof(struct ipx_packet),ubuf,copied);
+	sk->stamp=skb->stamp;
+	
+	if(sipx)
+	{
+		struct sockaddr_ipx addr;
+		
+		addr.sipx_family=AF_IPX;
+		addr.sipx_port=((ipx_packet*)skb->h.raw)->ipx_source.sock;
+		memcpy(addr.sipx_node,((ipx_packet*)skb->h.raw)->ipx_source.node,sizeof(addr.sipx_node));
+		addr.sipx_network=((ipx_packet*)skb->h.raw)->ipx_source.net;
+		memcpy_tofs(sipx,&addr,sizeof(*sipx));
+	}
+	skb_free_datagram(skb);
+	return(copied);
+}		
+
+static int ipx_recv(struct socket *sock, void *ubuf, int size , int noblock,
+	unsigned flags)
+{
+	ipx_socket *sk=(ipx_socket *)sock->data;
+	if(sk->zapped)
+		return -ENOTCONN;
+	return ipx_recvfrom(sock,ubuf,size,noblock,flags,NULL, NULL);
+}
+
+static int ipx_read(struct socket *sock, char *ubuf, int size, int noblock)
+{
+	return ipx_recv(sock,ubuf,size,noblock,0);
+}
+
+
+static int ipx_shutdown(struct socket *sk,int how)
+{
+	return -EOPNOTSUPP;
+}
+
+static int ipx_select(struct socket *sock , int sel_type, select_table *wait)
+{
+	ipx_socket *sk=(ipx_socket *)sock->data;
+	
+	return datagram_select(sk,sel_type,wait);
+}
+
+static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+	
+	switch(cmd)
+	{
+		case SIOCADDRT:
+		case SIOCDELRT:
+			if(!suser())
+				return -EPERM;
+			return(ipxrtr_ioctl(cmd,(void *)arg));
+		default:
+			return -EINVAL;
+	}
+	/*NOTREACHED*/
+	return(0);
+}
+
+static struct proto_ops ipx_proto_ops = {
+	AF_IPX,
+	
+	ipx_create,
+	ipx_dup,
+	ipx_release,
+	ipx_bind,
+	ipx_connect,
+	ipx_socketpair,
+	ipx_accept,
+	ipx_getname,
+	ipx_read,
+	ipx_write,
+	ipx_select,
+	ipx_ioctl,
+	ipx_listen,
+	ipx_send,
+	ipx_recv,
+	ipx_sendto,
+	ipx_recvfrom,
+	ipx_shutdown,
+	ipx_setsockopt,
+	ipx_getsockopt,
+	ipx_fcntl,
+};
+
+/* Called by ddi.c on kernel start up */
+
+static struct packet_type ipx_8023_packet_type = 
+{
+	0,	/* MUTTER ntohs(ETH_P_8023),*/
+	0,		/* copy */
+	ipx_rcv,
+	NULL,
+	NULL,
+};
+ 
+static struct packet_type ipx_dix_packet_type = 
+{
+	0,	/* MUTTER ntohs(ETH_P_IPX),*/
+	0,		/* copy */
+	ipx_rcv,
+	NULL,
+	NULL,
+};
+ 
+
+void ipx_proto_init(struct ddi_proto *pro)
+{
+	(void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
+	ipx_dix_packet_type.type=htons(ETH_P_IPX);
+	dev_add_pack(&ipx_dix_packet_type);
+	ipx_8023_packet_type.type=htons(ETH_P_802_3);
+	dev_add_pack(&ipx_8023_packet_type);
+	
+	printk("Swansea University Computer Society IPX 0.24 BETA for NET3 ALPHA.008\n");
+	
+}
+
+#endif
diff -u --recursive --new-file v1.1.3/linux/net/inet/ipx.h linux/net/inet/ipx.h
--- v1.1.3/linux/net/inet/ipx.h
+++ linux/net/inet/ipx.h	Wed Apr 13 12:52:25 1994
@@ -0,0 +1,64 @@
+
+/*
+ *	The following information is in its entirety obtained from:
+ *
+ *	Novell 'IPX Router Specification' Version 1.10 
+ *		Part No. 107-000029-001
+ *
+ *	Which is available from ftp.novell.com
+ */
+
+#ifndef _IPX_H
+#define _IPX_H
+
+#include <linux/ddi.h>
+
+typedef struct
+{
+	unsigned long net;
+	unsigned char  node[6]; 
+	unsigned short sock;
+} ipx_address;
+
+#define ipx_broadcast_node	"\377\377\377\377\377\377"
+
+typedef struct ipx_packet
+{
+	unsigned short	ipx_checksum;
+#define IPX_NO_CHECKSUM	0xFFFF
+	unsigned short  ipx_pktsize;
+	unsigned char   ipx_tctrl;
+	unsigned char   ipx_type;
+#define IPX_TYPE_UNKNOWN	0x00
+#define IPX_TYPE_RIP		0x01	/* may also be 0 */
+#define IPX_TYPE_SAP		0x04	/* may also be 0 */
+#define IPX_TYPE_SPX		0x05	/* Not yet implemented */
+#define IPX_TYPE_NCP		0x11	/* $lots for docs on this (SPIT) */
+#define IPX_TYPE_PPROP		0x14	/* complicated flood fill brdcast [Not supported] */
+	ipx_address	ipx_dest __attribute__ ((packed));
+	ipx_address	ipx_source __attribute__ ((packed));
+} ipx_packet;
+
+
+typedef struct ipx_route
+{
+	unsigned long net;
+	unsigned char router_node[6];
+	unsigned long router_net;
+	unsigned short flags;
+	struct device *dev;
+#define IPX_RT_ROUTED	1		/* This isn't a direct route. Send via this if to node router_node */
+#define IPX_RT_BLUEBOOK	2		/* Use DIX 8137 frames not IEE802.3 */
+	struct ipx_route *next;
+} ipx_route;
+
+
+typedef struct sock ipx_socket;
+
+
+#include "ipxcall.h"
+extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
+
+
+
+#endif
diff -u --recursive --new-file v1.1.3/linux/net/inet/ipxcall.h linux/net/inet/ipxcall.h
--- v1.1.3/linux/net/inet/ipxcall.h
+++ linux/net/inet/ipxcall.h	Wed Apr 13 12:52:25 1994
@@ -0,0 +1,2 @@
+/* Seperate to keep compilation of Space.c simpler */
+extern void ipx_proto_init(struct ddi_proto *pro);
diff -u --recursive --new-file v1.1.3/linux/net/inet/loopback.c linux/net/inet/loopback.c
--- v1.1.3/linux/net/inet/loopback.c	Wed Jan 19 13:27:09 1994
+++ linux/net/inet/loopback.c
@@ -1,137 +0,0 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Pseudo-driver for the loopback interface.
- *
- * Version:	@(#)loopback.c	1.0.4b	08/16/93
- *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *		Donald Becker, <becker@super.org>
- *
- *		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/config.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/errno.h>
-#include <linux/fcntl.h>
-#include <linux/in.h>
-#include <linux/if_ether.h>	/* For the statistics structure. */
-
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/io.h>
-
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
-#include "ip.h"
-#include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
-#include "sock.h"
-#include "arp.h"
-
-
-static int
-loopback_xmit(struct sk_buff *skb, struct device *dev)
-{
-  struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
-  int done;
-
-  DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb));
-  if (skb == NULL || dev == NULL) return(0);
-
-  cli();
-  if (dev->tbusy != 0) {
-	sti();
-	stats->tx_errors++;
-	return(1);
-  }
-  dev->tbusy = 1;
-  sti();
-
-  done = dev_rint(skb->data, skb->len, 0, dev);
-  if (skb->free) kfree_skb(skb, FREE_WRITE);
-
-  while (done != 1) {
-	done = dev_rint(NULL, 0, 0, dev);
-  }
-  stats->tx_packets++;
-
-  dev->tbusy = 0;
-
-#if 1
-	__asm__("cmpl $0,_intr_count\n\t"
-		"jne 1f\n\t"
-		"movl _bh_active,%%eax\n\t"
-		"testl _bh_mask,%%eax\n\t"
-		"je 1f\n\t"
-		"incl _intr_count\n\t"
-		"call _do_bottom_half\n\t"
-		"decl _intr_count\n"
-		"1:"
-		:
-		:
-		: "ax", "dx", "cx");
-#endif
-
-  return(0);
-}
-
-static struct enet_statistics *
-get_stats(struct device *dev)
-{
-    return (struct enet_statistics *)dev->priv;
-}
-
-/* Initialize the rest of the LOOPBACK device. */
-int
-loopback_init(struct device *dev)
-{
-  dev->mtu		= 2000;			/* MTU			*/
-  dev->tbusy		= 0;
-  dev->hard_start_xmit	= loopback_xmit;
-  dev->open		= NULL;
-#if 1
-  dev->hard_header	= eth_header;
-  dev->add_arp		= NULL;
-  dev->hard_header_len	= ETH_HLEN;		/* 14			*/
-  dev->addr_len		= ETH_ALEN;		/* 6			*/
-  dev->type		= ARPHRD_ETHER;		/* 0x0001		*/
-  dev->type_trans	= eth_type_trans;
-  dev->rebuild_header	= eth_rebuild_header;
-#else
-  dev->hard_header_length = 0;
-  dev->add_arp		= NULL;
-  dev->addr_len		= 0;
-  dev->type		= 0;			/* loopback_type (0)	*/
-  dev->hard_header	= NULL;
-  dev->type_trans	= NULL;
-  dev->rebuild_header	= NULL;
-#endif
-  dev->queue_xmit	= dev_queue_xmit;
-
-  /* New-style flags. */
-  dev->flags		= IFF_LOOPBACK;
-  dev->family		= AF_INET;
-  dev->pa_addr		= in_aton("127.0.0.1");
-  dev->pa_brdaddr	= in_aton("127.255.255.255");
-  dev->pa_mask		= in_aton("255.0.0.0");
-  dev->pa_alen		= sizeof(unsigned long);
-  dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
-  memset(dev->priv, 0, sizeof(struct enet_statistics));
-  dev->get_stats = get_stats;
-  
-  return(0);
-};
diff -u --recursive --new-file v1.1.3/linux/net/inet/packet.c linux/net/inet/packet.c
--- v1.1.3/linux/net/inet/packet.c	Mon Jan 31 16:35:36 1994
+++ linux/net/inet/packet.c	Wed Apr 13 12:52:26 1994
@@ -9,6 +9,7 @@
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  *
  * Fixes:	
  *		Alan Cox	:	verify_area() now used correctly
@@ -19,257 +20,416 @@
  *					from all old Linux datagram code.
  *		Alan Cox	:	Uses the improved datagram code.
  *		Alan Cox	:	Added NULL's for socket options.
+ *		Alan Cox	:	Re-commented the code.
  *
  *
  *		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/types.h>
 #include <linux/sched.h>
 #include <linux/fcntl.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
-#include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include <linux/errno.h>
 #include <linux/timer.h>
 #include <asm/system.h>
 #include <asm/segment.h>
-#include "udp.h"
-#include "raw.h"
 
+/*
+ *	We really ought to have a single public _inline_ min function!
+ */
 
-static unsigned long
-min(unsigned long a, unsigned long b)
+static unsigned long min(unsigned long a, unsigned long b)
 {
-  if (a < b) return(a);
-  return(b);
+	if (a < b) 
+		return(a);
+	return(b);
 }
 
 
-/* This should be the easiest of all, all we do is copy it into a buffer. */
-int
-packet_rcv(struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
+/*
+ *	This should be the easiest of all, all we do is copy it into a buffer. 
+ */
+ 
+int packet_rcv(struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
 {
-  struct sock *sk;
-
-  sk = (struct sock *) pt->data;
-  skb->dev = dev;
-  skb->len += dev->hard_header_len;
-
-  skb->sk = sk;
-
-  /* Charge it too the socket. */
-  if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_READ);
+	struct sock *sk;
+	
+	/*
+	 *	When we registered the protcol we saved the socket in the data
+	 *	field for just this event.
+	 */
+
+	sk = (struct sock *) pt->data;	
+
+	/*
+	 *	The SOCK_PACKET socket receives _all_ frames, and as such 
+	 *	therefore needs to put the header back onto the buffer.
+	 *	(it was removed by inet_bh()).
+	 */
+	 
+	skb->dev = dev;
+	skb->len += dev->hard_header_len;
+
+	skb->sk = sk;
+
+	/*
+	 *	Charge the memory to the socket. This is done specificially
+	 *	to prevent sockets using all the memory up.
+	 */
+	 
+	if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) 
+	{
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+		return(0);
+	}
+	sk->rmem_alloc += skb->mem_len;	
+
+	/*
+	 *	Queue the packet up, and wake anyone waiting for it.
+	 */
+
+	skb_queue_tail(&sk->receive_queue,skb);
+	wake_up_interruptible(sk->sleep);
+
+	/*
+	 *	Processing complete.
+	 */
+	 
+	release_sock(sk);	/* This is now effectively surplus in this layer */
 	return(0);
-  }
-  sk->rmem_alloc += skb->mem_len;
-  skb_queue_tail(&sk->rqueue,skb);
-  wake_up_interruptible(sk->sleep);
-  release_sock(sk);
-  return(0);
 }
 
 
-/* This will do terrible things if len + ipheader + devheader > dev->mtu */
-static int
-packet_sendto(struct sock *sk, unsigned char *from, int len,
+/*
+ *	Output a raw packet to a device layer. This bypasses all the other
+ *	protocol layers and you must therefore supply it with a complete frame
+ */
+ 
+static int packet_sendto(struct sock *sk, unsigned char *from, int len,
 	      int noblock, unsigned flags, struct sockaddr_in *usin,
 	      int addr_len)
 {
-  struct sk_buff *skb;
-  struct device *dev;
-  struct sockaddr saddr;
-  int err;
-
-  /* Check the flags. */
-  if (flags) return(-EINVAL);
-  if (len < 0) return(-EINVAL);
-
-  /* Get and verify the address. */
-  if (usin) {
-	if (addr_len < sizeof(saddr)) return(-EINVAL);
-	err=verify_area(VERIFY_READ, usin, sizeof(saddr));
+	struct sk_buff *skb;
+	struct device *dev;
+	struct sockaddr saddr;
+	int err;
+
+	/*
+	 *	Check the flags. 
+	 */
+
+	if (flags) 
+		return(-EINVAL);
+	if (len < 0) 
+		return(-EINVAL);
+
+	/*
+	 *	Get and verify the address. 
+	 */
+	 
+	if (usin) 
+	{
+		if (addr_len < sizeof(saddr)) 
+			return(-EINVAL);
+		err=verify_area(VERIFY_READ, usin, sizeof(saddr));
+		if(err)
+			return err;
+		memcpy_fromfs(&saddr, usin, sizeof(saddr));
+	} 
+	else
+		return(-EINVAL);	/* SOCK_PACKET must be sent giving an address */
+	
+
+	/*
+	 *	Check the buffer is readable.
+	 */
+
+	err=verify_area(VERIFY_READ,from,len);
 	if(err)
-		return err;
-	memcpy_fromfs(&saddr, usin, sizeof(saddr));
-  } else
-	return(-EINVAL);
+		return(err);
+		
+	/*
+	 *	Find the device first to size check it 
+	 */
+
+	saddr.sa_data[13] = 0;
+	dev = dev_get(saddr.sa_data);
+	if (dev == NULL) 
+	{
+		return(-ENXIO);
+  	}
 	
-  err=verify_area(VERIFY_READ,from,len);
-  if(err)
-  	return(err);
-/* Find the device first to size check it */
-
-  saddr.sa_data[13] = 0;
-  dev = dev_get(saddr.sa_data);
-  if (dev == NULL) {
-	return(-ENXIO);
-  }
-  if(len>dev->mtu)
-  	return -EMSGSIZE;
-
-/* Now allocate the buffer, knowing 4K pagelimits wont break this line */  
-  skb = sk->prot->wmalloc(sk, len+sizeof(*skb), 0, GFP_KERNEL);
-
-  /* This shouldn't happen, but it could. */
-  if (skb == NULL) {
-	DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n"));
-	return(-ENOMEM);
-  }
-  /* Fill it in */
-  skb->mem_addr = skb;
-  skb->mem_len = len + sizeof(*skb);
-  skb->sk = sk;
-  skb->free = 1;
-  memcpy_fromfs(skb->data, from, len);
-  skb->len = len;
-  skb->next = NULL;
-  skb->arp = 1;
-  if (dev->flags & IFF_UP) dev->queue_xmit(skb, dev, sk->priority);
-    else kfree_skb(skb, FREE_WRITE);
-  return(len);
+	/*
+	 *	You may not queue a frame bigger than the mtu. This is the lowest level
+	 *	raw protocol and you must do your own fragmentation at this level.
+	 */
+	 
+	if(len>dev->mtu)
+  		return -EMSGSIZE;
+
+	/*
+	 *	Now allocate the buffer, knowing 4K pagelimits wont break this line.
+	 */  
+	 
+	skb = sk->prot->wmalloc(sk, len, 0, GFP_KERNEL);
+
+	/*
+	 *	If the write buffer is full, then tough. At this level the user gets to
+	 *	deal with the problem.
+	 */
+	 
+	if (skb == NULL) 
+	{
+		DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n"));
+		return(-ENOMEM);
+	}
+	
+	/*
+	 *	Fill it in 
+	 */
+	 
+	skb->sk = sk;
+	skb->free = 1;
+	memcpy_fromfs(skb->data, from, len);
+	skb->len = len;
+	skb->arp = 1;		/* No ARP needs doing on this (complete) frame */
+
+	/*
+	 *	Now send it
+	 */
+
+	if (dev->flags & IFF_UP) 
+		dev_queue_xmit(skb, dev, sk->priority);
+	else
+		kfree_skb(skb, FREE_WRITE);
+	return(len);
 }
 
+/*
+ *	A write to a SOCK_PACKET can't actually do anything useful and will
+ *	always fail but we include it for completeness and future expansion.
+ */
 
-static int
-packet_write(struct sock *sk, unsigned char *buff, 
+static int packet_write(struct sock *sk, unsigned char *buff, 
 	     int len, int noblock,  unsigned flags)
 {
-  return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0));
+	return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0));
 }
 
+/*
+ *	Close a SOCK_PACKET socket. This is fairly simple. We immediately go
+ *	to 'closed' state and remove our protocol entry in the device list.
+ *	The release_sock() will destroy the socket if a user has closed the
+ *	file side of the object.
+ */
 
-static void
-packet_close(struct sock *sk, int timeout)
+static void packet_close(struct sock *sk, int timeout)
 {
-  sk->inuse = 1;
-  sk->state = TCP_CLOSE;
-  dev_remove_pack((struct packet_type *)sk->pair);
-  kfree_s((void *)sk->pair, sizeof(struct packet_type));
-  sk->pair = NULL;
-  release_sock(sk);
+	sk->inuse = 1;
+	sk->state = TCP_CLOSE;
+	dev_remove_pack((struct packet_type *)sk->pair);
+	kfree_s((void *)sk->pair, sizeof(struct packet_type));
+	sk->pair = NULL;
+	release_sock(sk);
 }
 
+/*
+ *	Create a packet of type SOCK_PACKET. We do one slightly irregular
+ *	thing here that wants tidying up. We borrow the 'pair' pointer in
+ *	the socket object so we can find the packet_type entry in the
+ *	device list. The reverse is easy as we use the data field of the
+ *	packet type to point to our socket.
+ */
 
-static int
-packet_init(struct sock *sk)
+static int packet_init(struct sock *sk)
 {
-  struct packet_type *p;
-
-  p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);
-  if (p == NULL) return(-ENOMEM);
+	struct packet_type *p;
 
-  p->func = packet_rcv;
-  p->type = sk->num;
-  p->data = (void *)sk;
-  dev_add_pack(p);
+	p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p == NULL) 
+		return(-ENOMEM);
+
+	p->func = packet_rcv;
+	p->type = sk->num;
+	p->data = (void *)sk;
+	dev_add_pack(p);
+   
+	/*
+	 *	We need to remember this somewhere. 
+	 */
    
-  /* We need to remember this somewhere. */
-  sk->pair = (struct sock *)p;
+	sk->pair = (struct sock *)p;
 
-  return(0);
+	return(0);
 }
 
 
 /*
- * This should be easy, if there is something there
- * we return it, otherwise we block.
+ *	Pull a packet from our receive queue and hand it to the user.
+ *	If neccessary we block.
  */
-int
-packet_recvfrom(struct sock *sk, unsigned char *to, int len,
+ 
+int packet_recvfrom(struct sock *sk, unsigned char *to, int len,
 	        int noblock, unsigned flags, struct sockaddr_in *sin,
 	        int *addr_len)
 {
-  int copied=0;
-  struct sk_buff *skb;
-  struct sockaddr *saddr;
-  int err;
-
-  saddr = (struct sockaddr *)sin;
-  if (len == 0) return(0);
-  if (len < 0) return(-EINVAL);
-
-  if (sk->shutdown & RCV_SHUTDOWN) return(0);
-  if (addr_len) {
-	  err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
-	  if(err)
-	  	return err;
-	  put_fs_long(sizeof(*saddr), addr_len);
-  }
-  
-  err=verify_area(VERIFY_WRITE,to,len);
-  if(err)
-  	return err;
-  skb=skb_recv_datagram(sk,flags,noblock,&err);
-  if(skb==NULL)
-  	return err;
-  copied = min(len, skb->len);
-
-  memcpy_tofs(to, skb->data, copied);	/* Don't use skb_copy_datagram here: We can't get frag chains */
-
-  /* Copy the address. */
-  if (saddr) {
-	struct sockaddr addr;
-
-	addr.sa_family = skb->dev->type;
-	memcpy(addr.sa_data,skb->dev->name, 14);
-	verify_area(VERIFY_WRITE, saddr, sizeof(*saddr));
-	memcpy_tofs(saddr, &addr, sizeof(*saddr));
-  }
-
-  skb_free_datagram(skb);		/* Its either been used up, or its a peek_copy anyway */
-
-  release_sock(sk);
-  return(copied);
+	int copied=0;
+	struct sk_buff *skb;
+	struct sockaddr *saddr;
+	int err;
+
+	saddr = (struct sockaddr *)sin;
+	if (len == 0) 
+		return(0);
+	if (len < 0)
+		return(-EINVAL);
+
+	if (sk->shutdown & RCV_SHUTDOWN) 
+		return(0);
+		
+	/*
+	 *	If the address length field is there to be filled in, we fill
+	 *	it in now.
+	 */
+
+	if (addr_len) 
+	{
+		err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+		if(err)
+			return err;
+		put_fs_long(sizeof(*saddr), addr_len);
+	}
+	
+	if(saddr)
+	{
+		err=verify_area(VERIFY_WRITE, saddr, sizeof(*saddr));		
+		if(err)
+			return err;
+  	}
+	
+	/*
+	 *	Check the user given area can be written to.
+	 */
+	 
+	err=verify_area(VERIFY_WRITE,to,len);
+	if(err)
+		return err;
+		
+	/*
+	 *	Call the generic datagram receiver. This handles all sorts
+	 *	of horrible races and re-entrancy so we can forget about it
+	 *	in the protocol layers.
+	 */
+	 
+	skb=skb_recv_datagram(sk,flags,noblock,&err);
+	
+	/*
+	 *	An error occured so return it. Because skb_recv_datagram() 
+	 *	handles the blocking we don't see and worry about blocking
+	 *	retries.
+	 */
+	 
+	if(skb==NULL)
+		return err;
+		
+	/*
+	 *	You lose any data beyond the buffer you gave. If it worries a
+	 *	user program they can ask the device for its MTU anyway.
+	 */
+	 
+	copied = min(len, skb->len);
+
+	memcpy_tofs(to, skb->data, copied);	/* We can't use skb_copy_datagram here */
+
+	/*
+	 *	Copy the address. 
+	 */
+	 
+	if (saddr) 
+	{
+		struct sockaddr addr;
+
+		addr.sa_family = skb->dev->type;
+		memcpy(addr.sa_data,skb->dev->name, 14);
+		memcpy_tofs(saddr, &addr, sizeof(*saddr));
+	}
+	
+	/*
+	 *	Free or return the buffer as appropriate. Again this hides all the
+	 *	races and re-entrancy issues from us.
+	 */
+
+	skb_free_datagram(skb);
+
+	/*
+	 *	We are done.
+	 */
+	 
+	release_sock(sk);
+	return(copied);
 }
 
 
-int
-packet_read(struct sock *sk, unsigned char *buff,
+/*
+ *	A packet read can succeed and is just the same as a recvfrom but without the
+ *	addresses being recorded.
+ */
+
+int packet_read(struct sock *sk, unsigned char *buff,
 	    int len, int noblock, unsigned flags)
 {
-  return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+	return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
 }
 
 
-struct proto packet_prot = {
-  sock_wmalloc,
-  sock_rmalloc,
-  sock_wfree,
-  sock_rfree,
-  sock_rspace,
-  sock_wspace,
-  packet_close,
-  packet_read,
-  packet_write,
-  packet_sendto,
-  packet_recvfrom,
-  ip_build_header,
-  udp_connect,
-  NULL,
-  ip_queue_xmit,
-  ip_retransmit,
-  NULL,
-  NULL,
-  NULL, 
-  datagram_select,
-  NULL,
-  packet_init,
-  NULL,
-  NULL,	/* No set/get socket options */
-  NULL,
-  128,
-  0,
-  {NULL,},
-  "PACKET"
+/*
+ *	This structure declares to the lower layer socket subsystem currently
+ *	incorrectly embedded in the IP code how to behave. This interface needs
+ *	a lot of work and will change.
+ */
+ 
+struct proto packet_prot = 
+{
+	sock_wmalloc,
+	sock_rmalloc,
+	sock_wfree,
+	sock_rfree,
+	sock_rspace,
+	sock_wspace,
+	packet_close,
+	packet_read,
+	packet_write,
+	packet_sendto,
+	packet_recvfrom,
+	ip_build_header,	/* Not actually used */
+	NULL,
+	NULL,
+	ip_queue_xmit,		/* These two are not actually used */
+	ip_retransmit,
+	NULL,
+	NULL,
+	NULL, 
+	datagram_select,
+	NULL,
+	packet_init,
+	NULL,
+	NULL,			/* No set/get socket options */
+	NULL,
+	128,
+	0,
+	{NULL,},
+	"PACKET"
 };
diff -u --recursive --new-file v1.1.3/linux/net/inet/proc.c linux/net/inet/proc.c
--- v1.1.3/linux/net/inet/proc.c	Mon Apr  4 09:23:07 1994
+++ linux/net/inet/proc.c	Wed Apr 13 12:52:26 1994
@@ -36,13 +36,13 @@
 #include <linux/un.h>
 #include <linux/in.h>
 #include <linux/param.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "tcp.h"
 #include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "raw.h"
 
@@ -54,18 +54,21 @@
  *  happens, get__netinfo returns only part of the available infos.
  */
 static int
-get__netinfo(struct proto *pro, char *buffer, int format)
+get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
 {
   struct sock **s_array;
   struct sock *sp;
-  char *pos=buffer;
   int i;
   int timer_active;
   unsigned long  dest, src;
   unsigned short destp, srcp;
+  int len=0;
+  off_t pos=0;
+  off_t begin=0;
+  
 
   s_array = pro->sock_array;
-  pos+=sprintf(pos, "sl  local_address rem_address   st tx_queue rx_queue tr tm->when uid\n");
+  len+=sprintf(buffer, "sl  local_address rem_address   st tx_queue rx_queue tr tm->when uid\n");
 /*
  *	This was very pretty but didn't work when a socket is destroyed at the wrong moment
  *	(eg a syn recv socket getting a reset), or a memory timer destroy. Instead of playing
@@ -86,7 +89,7 @@
 		timer_active = del_timer(&sp->timer);
 		if (!timer_active)
 			sp->timer.expires = 0;
-		pos+=sprintf(pos, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n",
+		len+=sprintf(buffer+len, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n",
 			i, src, srcp, dest, destp, sp->state, 
 			format==0?sp->write_seq-sp->rcv_ack_seq:sp->rmem_alloc, 
 			format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc,
@@ -94,40 +97,47 @@
 			SOCK_INODE(sp->socket)->i_uid);
 		if (timer_active)
 			add_timer(&sp->timer);
-		/* Is place in buffer too rare? then abort. */
-		if (pos > buffer+PAGE_SIZE-90) {
-			printk("oops, too many %s sockets for netinfo.\n",
-					pro->name);
-			return(strlen(buffer));
-		}
-
 		/*
 		 * All sockets with (port mod SOCK_ARRAY_SIZE) = i
 		 * are kept in sock_array[i], so we must follow the
 		 * 'next' link to get them all.
 		 */
 		sp = sp->next;
+		pos=begin+len;
+		if(pos<offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos>offset+length)
+			break;
 	}
 	sti();	/* We only turn interrupts back on for a moment, but because the interrupt queues anything built up
 		   before this will clear before we jump back and cli, so its not as bad as it looks */
+	if(pos>offset+length)
+		break;
   }
-  return(strlen(buffer));
+  *start=buffer+(offset-begin);
+  len-=(offset-begin);
+  if(len>length)
+  	len=length;
+  return len;
 } 
 
 
-int tcp_get_info(char *buffer)
+int tcp_get_info(char *buffer, char **start, off_t offset, int length)
 {
-  return get__netinfo(&tcp_prot, buffer,0);
+  return get__netinfo(&tcp_prot, buffer,0, start, offset, length);
 }
 
 
-int udp_get_info(char *buffer)
+int udp_get_info(char *buffer, char **start, off_t offset, int length)
 {
-  return get__netinfo(&udp_prot, buffer,1);
+  return get__netinfo(&udp_prot, buffer,1, start, offset, length);
 }
 
 
-int raw_get_info(char *buffer)
+int raw_get_info(char *buffer, char **start, off_t offset, int length)
 {
-  return get__netinfo(&raw_prot, buffer,1);
+  return get__netinfo(&raw_prot, buffer,1, start, offset, length);
 }
diff -u --recursive --new-file v1.1.3/linux/net/inet/protocol.c linux/net/inet/protocol.c
--- v1.1.3/linux/net/inet/protocol.c	Wed Dec  1 14:44:15 1993
+++ linux/net/inet/protocol.c	Wed Apr 13 12:52:26 1994
@@ -29,12 +29,12 @@
 #include <linux/string.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "icmp.h"
 #include "udp.h"
diff -u --recursive --new-file v1.1.3/linux/net/inet/raw.c linux/net/inet/raw.c
--- v1.1.3/linux/net/inet/raw.c	Tue Apr  5 10:17:16 1994
+++ linux/net/inet/raw.c	Wed Apr 13 12:52:26 1994
@@ -40,12 +40,14 @@
 #include <linux/fcntl.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
+#if 0
 #include "tcp.h"
-#include "skbuff.h"
+#endif
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "icmp.h"
 #include "udp.h"
@@ -101,13 +103,18 @@
 	   "         len=%d, saddr=%X, redo=%d, protocol=%X)\n",
 	   skb, dev, opt, daddr, len, saddr, redo, protocol));
 
-  if (skb == NULL) return(0);
-  if (protocol == NULL) {
+  if (skb == NULL)
+  	return(0);
+  	
+  if (protocol == NULL) 
+  {
 	kfree_skb(skb, FREE_READ);
 	return(0);
   }
+  
   sk = (struct sock *) protocol->data;
-  if (sk == NULL) {
+  if (sk == NULL) 
+  {
 	kfree_skb(skb, FREE_READ);
 	return(0);
   }
@@ -122,12 +129,14 @@
 
   /* Charge it too the socket. */
   if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
-	skb->sk = NULL;
+	ip_statistics.IpInDiscards++;
+	skb->sk=NULL;
 	kfree_skb(skb, FREE_READ);
 	return(0);
   }
   sk->rmem_alloc += skb->mem_len;
-  skb_queue_tail(&sk->rqueue,skb);
+  ip_statistics.IpInDelivers++;
+  skb_queue_tail(&sk->receive_queue,skb);
   sk->data_ready(sk,skb->len);
   release_sock(sk);
   return(0);
@@ -173,7 +182,7 @@
   }
   if (sin.sin_port == 0) sin.sin_port = sk->protocol;
   
-  if (sk->broadcast == 0 && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+  if (sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
   	return -EACCES;
 
   sk->inuse = 1;
@@ -188,7 +197,7 @@
   	}
   	
 	skb = sk->prot->wmalloc(sk,
-			len+sizeof(*skb) + sk->prot->max_header,
+			len + sk->prot->max_header,
 			0, GFP_KERNEL);
 	if (skb == NULL) {
 		int tmp;
@@ -210,12 +219,9 @@
 		sti();
 	}
   }
-  skb->mem_addr = skb;
-  skb->mem_len = len + sizeof(*skb) +sk->prot->max_header;
   skb->sk = sk;
 
-  skb->free = 1; /* these two should be unecessary. */
-  skb->arp = 0;
+  skb->free = 1;
 
   tmp = sk->prot->build_header(skb, sk->saddr, 
 			       sin.sin_addr.s_addr, &dev,
@@ -244,6 +250,7 @@
   }
 
   skb->len = tmp + len;
+  
   sk->prot->queue_xmit(sk, dev, skb, 1);
   release_sock(sk);
   return(len);
@@ -345,6 +352,7 @@
   copied = min(len, skb->len);
   
   skb_copy_datagram(skb, 0, to, copied);
+  sk->stamp=skb->stamp;
 
   /* Copy the address. */
   if (sin) {
diff -u --recursive --new-file v1.1.3/linux/net/inet/route.c linux/net/inet/route.c
--- v1.1.3/linux/net/inet/route.c	Sun Mar 13 16:20:59 1994
+++ linux/net/inet/route.c	Wed Apr 13 12:52:27 1994
@@ -9,6 +9,8 @@
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Linus Torvalds, <Linus.Torvalds@helsinki.fi>
  *
  * Fixes:
  *		Alan Cox	:	Verify area fixes.
@@ -16,12 +18,15 @@
  *		Rui Oliveira	:	ICMP routing table updates
  *		(rco@di.uminho.pt)	Routing table insertion and update
  *		Linus Torvalds	:	Rewrote bits to be sensible
+ *		Alan Cox	:	Added BSD route gw semantics
+ *		Alan Cox	:	Super /proc >4K 
  *
  *		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 <asm/segment.h>
 #include <asm/system.h>
 #include <linux/types.h>
@@ -32,40 +37,52 @@
 #include <linux/sockios.h>
 #include <linux/errno.h>
 #include <linux/in.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "route.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
-#include "arp.h"
 #include "icmp.h"
 
+/*
+ *	The routing table list
+ */
 
 static struct rtable *rt_base = NULL;
+
+/*
+ *	Pointer to the loopback route
+ */
+ 
 static struct rtable *rt_loopback = NULL;
 
-/* Dump the contents of a routing table entry. */
-static void
-rt_print(struct rtable *rt)
+/*
+ *	Dump the contents of a routing table entry. 
+ */
+ 
+static void rt_print(struct rtable *rt)
 {
-  if (rt == NULL || inet_debug != DBG_RT) return;
+
+	if (rt == NULL || inet_debug != DBG_RT) 
+	  	return;
 
-  printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
+	printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n",
 		(long) rt, (long) rt->rt_next, rt->rt_flags);
-  printk("    TARGET=%s ", in_ntoa(rt->rt_dst));
-  printk("GW=%s ", in_ntoa(rt->rt_gateway));
-  printk("    DEV=%s USE=%ld REF=%d\n",
-	(rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
-	rt->rt_use, rt->rt_refcnt);
+	printk("    TARGET=%s ", in_ntoa(rt->rt_dst));
+	printk("GW=%s ", in_ntoa(rt->rt_gateway));
+	printk("    DEV=%s USE=%ld REF=%d\n",
+		(rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
+		rt->rt_use, rt->rt_refcnt);
 }
 
 
 /*
- * Remove a routing table entry.
+ *	Remove a routing table entry.
  */
+
 static void rt_del(unsigned long dst)
 {
 	struct rtable *r, **rp;
@@ -73,14 +90,27 @@
 
 	DPRINTF((DBG_RT, "RT: flushing for dst %s\n", in_ntoa(dst)));
 	rp = &rt_base;
+	
+	/*
+	 *	This must be done with interrupts off because we could take
+	 *	an ICMP_REDIRECT.
+	 */
+	 
 	save_flags(flags);
 	cli();
-	while((r = *rp) != NULL) {
-		if (r->rt_dst != dst) {
+	while((r = *rp) != NULL) 
+	{
+		if (r->rt_dst != dst) 
+		{
 			rp = &r->rt_next;
 			continue;
 		}
 		*rp = r->rt_next;
+		
+		/*
+		 *	If we delete the loopback route update its pointer.
+		 */
+		 
 		if (rt_loopback == r)
 			rt_loopback = NULL;
 		kfree_s(r, sizeof(struct rtable));
@@ -90,9 +120,11 @@
 
 
 /*
- * Remove all routing table entries for a device.
+ *	Remove all routing table entries for a device. This is called when
+ *	a device is downed.
  */
-void rt_flush(struct device *dev)
+ 
+void ip_rt_flush(struct device *dev)
 {
 	struct rtable *r;
 	struct rtable **rp;
@@ -116,12 +148,13 @@
 }
 
 /*
- * Used by 'rt_add()' when we can't get the netmask any other way..
+ *	Used by 'rt_add()' when we can't get the netmask any other way..
  *
- * If the lower byte or two are zero, we guess the mask based on the
- * number of zero 8-bit net numbers, otherwise we use the "default"
- * masks judging by the destination address and our device netmask.
+ *	If the lower byte or two are zero, we guess the mask based on the
+ *	number of zero 8-bit net numbers, otherwise we use the "default"
+ *	masks judging by the destination address and our device netmask.
  */
+ 
 static inline unsigned long default_mask(unsigned long dst)
 {
 	dst = ntohl(dst);
@@ -132,6 +165,11 @@
 	return htonl(IN_CLASSC_NET);
 }
 
+
+/*
+ *	If no mask is specified then generate a default entry.
+ */
+
 static unsigned long guess_mask(unsigned long dst, struct device * dev)
 {
 	unsigned long mask;
@@ -144,16 +182,25 @@
 	return dev->pa_mask;
 }
 
+
+/*
+ *	Find the route entry through which our gateway will be reached
+ */
+ 
 static inline struct device * get_gw_dev(unsigned long gw)
 {
 	struct rtable * rt;
 
-	for (rt = rt_base ; ; rt = rt->rt_next) {
+	for (rt = rt_base ; ; rt = rt->rt_next) 
+	{
 		if (!rt)
 			return NULL;
 		if ((gw ^ rt->rt_dst) & rt->rt_mask)
 			continue;
-		/* gateways behind gateways are a no-no */
+		/* 
+		 *	Gateways behind gateways are a no-no 
+		 */
+		 
 		if (rt->rt_flags & RTF_GATEWAY)
 			return NULL;
 		return rt->rt_dev;
@@ -161,41 +208,78 @@
 }
 
 /*
- * rewrote rt_add(), as the old one was weird. Linus
+ *	Rewrote rt_add(), as the old one was weird - Linus
+ *
+ *	This routine is used to update the IP routing table, either
+ *	from the kernel (ICMP_REDIRECT) or via an ioctl call issued
+ *	by the superuser.
  */
-void rt_add(short flags, unsigned long dst, unsigned long mask,
+ 
+void ip_rt_add(short flags, unsigned long dst, unsigned long mask,
 	unsigned long gw, struct device *dev)
 {
 	struct rtable *r, *rt;
 	struct rtable **rp;
 	unsigned long cpuflags;
 
-	if (flags & RTF_HOST) {
+	/*
+	 *	A host is a unique machine and has no network bits.
+	 */
+	 
+	if (flags & RTF_HOST) 
+	{
 		mask = 0xffffffff;
-	} else if (!mask) {
-		if (!((dst ^ dev->pa_addr) & dev->pa_mask)) {
+	} 
+	
+	/*
+	 *	Calculate the network mask
+	 */
+	 
+	else if (!mask) 
+	{
+		if (!((dst ^ dev->pa_addr) & dev->pa_mask)) 
+		{
 			mask = dev->pa_mask;
 			flags &= ~RTF_GATEWAY;
-			if (flags & RTF_DYNAMIC) {
+			if (flags & RTF_DYNAMIC) 
+			{
 				/*printk("Dynamic route to my own net rejected\n");*/
 				return;
 			}
-		} else
+		} 
+		else
 			mask = guess_mask(dst, dev);
 		dst &= mask;
 	}
+	
+	/*
+	 *	A gateway must be reachable and not a local address
+	 */
+	 
 	if (gw == dev->pa_addr)
 		flags &= ~RTF_GATEWAY;
-	if (flags & RTF_GATEWAY) {
-		/* don't try to add a gateway we can't reach.. */
+		
+	if (flags & RTF_GATEWAY) 
+	{
+		/*
+		 *	Don't try to add a gateway we can't reach.. 
+		 */
+		 
 		if (dev != get_gw_dev(gw))
 			return;
+			
 		flags |= RTF_GATEWAY;
-	} else
+	} 
+	else
 		gw = 0;
-	/* Allocate an entry. */
+		
+	/*
+	 *	Allocate an entry and fill it in.
+	 */
+	 
 	rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
-	if (rt == NULL) {
+	if (rt == NULL) 
+	{
 		DPRINTF((DBG_RT, "RT: no memory for new route!\n"));
 		return;
 	}
@@ -207,17 +291,26 @@
 	rt->rt_mask = mask;
 	rt->rt_mtu = dev->mtu;
 	rt_print(rt);
+
 	/*
-	 * What we have to do is loop though this until we have
-	 * found the first address which has a higher generality than
-	 * the one in rt.  Then we can put rt in right before it.
+	 *	What we have to do is loop though this until we have
+	 *	found the first address which has a higher generality than
+	 *	the one in rt.  Then we can put rt in right before it.
+	 *	The interrupts must be off for this process.
 	 */
+
 	save_flags(cpuflags);
 	cli();
-	/* remove old route if we are getting a duplicate. */
+
+	/*
+	 *	Remove old route if we are getting a duplicate. 
+	 */
+	 
 	rp = &rt_base;
-	while ((r = *rp) != NULL) {
-		if (r->rt_dst != dst) {
+	while ((r = *rp) != NULL) 
+	{
+		if (r->rt_dst != dst) 
+		{
 			rp = &r->rt_next;
 			continue;
 		}
@@ -226,7 +319,11 @@
 			rt_loopback = NULL;
 		kfree_s(r, sizeof(struct rtable));
 	}
-	/* add the new route */
+	
+	/*
+	 *	Add the new route 
+	 */
+	 
 	rp = &rt_base;
 	while ((r = *rp) != NULL) {
 		if ((r->rt_mask & mask) != mask)
@@ -235,12 +332,27 @@
 	}
 	rt->rt_next = r;
 	*rp = rt;
+	
+	/*
+	 *	Update the loopback route
+	 */
+	 
 	if (rt->rt_dev->flags & IFF_LOOPBACK)
 		rt_loopback = rt;
+		
+	/*
+	 *	Restore the interrupts and return
+	 */
+	 
 	restore_flags(cpuflags);
 	return;
 }
 
+
+/*
+ *	Check if a mask is acceptable.
+ */
+ 
 static inline int bad_mask(unsigned long mask, unsigned long addr)
 {
 	if (addr & (mask = ~mask))
@@ -251,6 +363,10 @@
 	return 0;
 }
 
+/*
+ *	Process a route add request from the user
+ */
+ 
 static int rt_new(struct rtentry *r)
 {
 	int err;
@@ -258,7 +374,12 @@
 	struct device * dev = NULL;
 	unsigned long flags, daddr, mask, gw;
 
-	if ((devname = r->rt_dev) != NULL) {
+	/*
+	 *	If a device is specified find it.
+	 */
+	 
+	if ((devname = r->rt_dev) != NULL) 
+	{
 		err = getname(devname, &devname);
 		if (err)
 			return err;
@@ -267,22 +388,37 @@
 		if (!dev)
 			return -EINVAL;
 	}
+	
+	/*
+	 *	If the device isn't INET, don't allow it
+	 */
 
 	if (r->rt_dst.sa_family != AF_INET)
 		return -EAFNOSUPPORT;
 
+	/*
+	 *	Make local copies of the important bits
+	 */
+	 
 	flags = r->rt_flags;
 	daddr = ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr;
 	mask = ((struct sockaddr_in *) &r->rt_genmask)->sin_addr.s_addr;
 	gw = ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr;
 
-/* BSD emulation: Permits route add someroute gw one-of-my-addresses
-   to indicate which iface. Not as clean as the nice Linux dev technique
-   but people keep using it... */
-	if (!dev && (flags & RTF_GATEWAY)) {
+
+	/*
+	 *	BSD emulation: Permits route add someroute gw one-of-my-addresses
+	 *	to indicate which iface. Not as clean as the nice Linux dev technique
+	 *	but people keep using it... 
+	 */
+	 
+	if (!dev && (flags & RTF_GATEWAY)) 
+	{
 		struct device *dev2;
-		for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next) {
-			if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw) {
+		for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next) 
+		{
+			if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw) 
+			{
 				flags &= ~RTF_GATEWAY;
 				dev = dev2;
 				break;
@@ -290,30 +426,56 @@
 		}
 	}
 
+	/*
+	 *	Ignore faulty masks
+	 */
+	 
 	if (bad_mask(mask, daddr))
 		mask = 0;
 
+	/*
+	 *	Set the mask to nothing for host routes.
+	 */
+	 
 	if (flags & RTF_HOST)
 		mask = 0xffffffff;
 	else if (mask && r->rt_genmask.sa_family != AF_INET)
 		return -EAFNOSUPPORT;
 
-	if (flags & RTF_GATEWAY) {
+	/*
+	 *	You can only gateway IP via IP..
+	 */
+	 
+	if (flags & RTF_GATEWAY) 
+	{
 		if (r->rt_gateway.sa_family != AF_INET)
 			return -EAFNOSUPPORT;
 		if (!dev)
 			dev = get_gw_dev(gw);
-	} else if (!dev)
-		dev = dev_check(daddr);
+	} 
+	else if (!dev)
+		dev = ip_dev_check(daddr);
 
+	/*
+	 *	Unknown device.
+	 */
+	 
 	if (dev == NULL)
 		return -ENETUNREACH;
 
-	rt_add(flags, daddr, mask, gw, dev);
+	/*
+	 *	Add the route
+	 */
+	 
+	ip_rt_add(flags, daddr, mask, gw, dev);
 	return 0;
 }
 
 
+/*
+ *	Remove a route, as requested by the user.
+ */
+
 static int rt_kill(struct rtentry *r)
 {
 	struct sockaddr_in *trg;
@@ -324,45 +486,83 @@
 }
 
 
-/* Called from the PROCfs module. */
-int
-rt_get_info(char *buffer)
+/* 
+ *	Called from the PROCfs module. This outputs /proc/net/route.
+ */
+ 
+int rt_get_info(char *buffer, char **start, off_t offset, int length)
 {
-  struct rtable *r;
-  char *pos;
-
-  pos = buffer;
+	struct rtable *r;
+	int len=0;
+	off_t pos=0;
+	off_t begin=0;
+	int size;
 
-  pos += sprintf(pos,
+	len += sprintf(buffer,
 		 "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\n");
+	pos=len;
   
-  /* This isn't quite right -- r->rt_dst is a struct! */
-  for (r = rt_base; r != NULL; r = r->rt_next) {
-        pos += sprintf(pos, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n",
-		r->rt_dev->name, r->rt_dst, r->rt_gateway,
-		r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
-		r->rt_mask);
-  }
-  return(pos - buffer);
+	/*
+	 *	This isn't quite right -- r->rt_dst is a struct! 
+	 */
+	 
+	for (r = rt_base; r != NULL; r = r->rt_next) 
+	{
+        	size = sprintf(buffer+len, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n",
+			r->rt_dev->name, r->rt_dst, r->rt_gateway,
+			r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
+			r->rt_mask);
+		len+=size;
+		pos+=size;
+		if(pos<offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos>offset+length)
+			break;
+  	}
+  	
+  	*start=buffer+(offset-begin);
+  	len-=(offset-begin);
+  	if(len>length)
+  		len=length;
+  	return len;
 }
 
 /*
- * This is hackish, but results in better code. Use "-S" to see why.
+ *	This is hackish, but results in better code. Use "-S" to see why.
  */
+ 
 #define early_out ({ goto no_route; 1; })
 
-struct rtable * rt_route(unsigned long daddr, struct options *opt)
+/*
+ *	Route a packet. This needs to be fairly quick. Florian & Co. 
+ *	suggested a unified ARP and IP routing cache. Done right its
+ *	probably a brilliant idea. I'd actually suggest a unified
+ *	ARP/IP routing/Socket pointer cache. Volunteers welcome
+ */
+ 
+struct rtable * ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr)
 {
 	struct rtable *rt;
 
-	for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) {
+	for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) 
+	{
 		if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
 			break;
-		/* broadcast addresses can be special cases.. */
+		/*
+		 *	broadcast addresses can be special cases.. 
+		 */
+		 
 		if ((rt->rt_dev->flags & IFF_BROADCAST) &&
 		     rt->rt_dev->pa_brdaddr == daddr)
 			break;
 	}
+	
+	if(src_addr!=NULL)
+		*src_addr= rt->rt_dev->pa_addr;
+		
 	if (daddr == rt->rt_dev->pa_addr) {
 		if ((rt = rt_loopback) == NULL)
 			goto no_route;
@@ -373,7 +573,11 @@
 	return NULL;
 }
 
-static int get_old_rtent(struct old_rtentry * src, struct rtentry * rt)
+/*
+ *	Backwards compatibility
+ */
+ 
+static int ip_get_old_rtent(struct old_rtentry * src, struct rtentry * rt)
 {
 	int err;
 	struct old_rtentry tmp;
@@ -389,34 +593,42 @@
 	((struct sockaddr_in *) &rt->rt_genmask)->sin_addr.s_addr = tmp.rt_genmask;
 	rt->rt_flags = tmp.rt_flags;
 	rt->rt_dev = tmp.rt_dev;
+	printk("Warning: obsolete routing request made.\n");
 	return 0;
 }
 
-int rt_ioctl(unsigned int cmd, void *arg)
+/*
+ *	Handle IP routing ioctl calls. These are used to manipulate the routing tables
+ */
+ 
+int ip_rt_ioctl(unsigned int cmd, void *arg)
 {
 	int err;
 	struct rtentry rt;
 
-	switch(cmd) {
-	case DDIOCSDBG:
-		return dbg_ioctl(arg, DBG_RT);
-	case SIOCADDRTOLD:
-	case SIOCDELRTOLD:
-		if (!suser())
-			return -EPERM;
-		err = get_old_rtent((struct old_rtentry *) arg, &rt);
-		if (err)
-			return err;
-		return (cmd == SIOCDELRTOLD) ? rt_kill(&rt) : rt_new(&rt);
-	case SIOCADDRT:
-	case SIOCDELRT:
-		if (!suser())
-			return -EPERM;
-		err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry));
-		if (err)
-			return err;
-		memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
-		return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
+	switch(cmd) 
+	{
+		case DDIOCSDBG:		/* Control debugging */
+			return dbg_ioctl(arg, DBG_RT);
+	
+		case SIOCADDRTOLD:	/* Old style add route */
+		case SIOCDELRTOLD:	/* Old style delete route */
+			if (!suser())
+				return -EPERM;
+			err = ip_get_old_rtent((struct old_rtentry *) arg, &rt);
+			if (err)
+				return err;
+			return (cmd == SIOCDELRTOLD) ? rt_kill(&rt) : rt_new(&rt);
+
+		case SIOCADDRT:		/* Add a route */
+		case SIOCDELRT:		/* Delete a route */
+			if (!suser())
+				return -EPERM;
+			err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry));
+			if (err)
+				return err;
+			memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
+			return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
 	}
 
 	return -EINVAL;
diff -u --recursive --new-file v1.1.3/linux/net/inet/route.h linux/net/inet/route.h
--- v1.1.3/linux/net/inet/route.h	Tue Jan 25 08:30:29 1994
+++ linux/net/inet/route.h	Wed Apr 13 12:52:27 1994
@@ -37,11 +37,11 @@
 };
 
 
-extern void		rt_flush(struct device *dev);
-extern void		rt_add(short flags, unsigned long addr, unsigned long mask,
+extern void		ip_rt_flush(struct device *dev);
+extern void		ip_rt_add(short flags, unsigned long addr, unsigned long mask,
 			       unsigned long gw, struct device *dev);
-extern struct rtable	*rt_route(unsigned long daddr, struct options *opt);
-extern int		rt_get_info(char * buffer);
-extern int		rt_ioctl(unsigned int cmd, void *arg);
+extern struct rtable	*ip_rt_route(unsigned long daddr, struct options *opt, unsigned long *src_addr);
+extern int		rt_get_info(char * buffer, char **start, off_t offset, int length);
+extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
 
 #endif	/* _ROUTE_H */
diff -u --recursive --new-file v1.1.3/linux/net/inet/skbuff.c linux/net/inet/skbuff.c
--- v1.1.3/linux/net/inet/skbuff.c	Wed Apr 13 11:33:59 1994
+++ linux/net/inet/skbuff.c	Wed Apr 13 16:48:10 1994
@@ -1,19 +1,15 @@
 /*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
+ *	Routines having to do with the 'struct sk_buff' memory handlers.
  *
- *		A saner implementation of the skbuff stuff scattered everywhere
- *		in the old NET2D code.
- *
  *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
+ *			Florian La Roche <rzsfl@rz.uni-sb.de>
  *
- *	Fixes:
- *		Alan Cox	:	Tracks memory and number of buffers for kernel memory report
- *					and memory leak hunting.
- *		Alan Cox	:	More generic kfree handler
+ *	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/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -23,124 +19,178 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
-#include <linux/string.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
-#include "arp.h"
+#include <linux/string.h>
 #include "route.h"
 #include "tcp.h"
 #include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 
 
-/* Socket buffer operations. Ideally much of this list swap stuff ought to be using
-   exch instructions on the 386, and CAS/CAS2 on a 68K. This is the boring generic
-   slow C version. No doubt when Linus sees this comment he'll do horrible things
-   to this code 8-)
-*/
-
 /*
  *	Resource tracking variables
  */
+ 
+volatile unsigned long net_memory = 0;
+volatile unsigned long net_skbcount = 0;
 
-volatile unsigned long net_memory=0;
-volatile unsigned long net_skbcount=0;
 
+#if CONFIG_SKB_CHECK
+
 /*
  *	Debugging paranoia. Can go later when this crud stack works
- */
+ */ 
 
+int skb_check(struct sk_buff *skb, int head, int line, char *file)
+{
+	if (head) {
+		if (skb->magic_debug_cookie != SK_HEAD_SKB) {
+			printk("File: %s Line %d, found a bad skb-head\n",
+				file,line);
+			return -1;
+		}
+		if (!skb->next || !skb->prev) {
+			printk("skb_check: head without next or prev\n");
+			return -1;
+		}
+		if (skb->next->magic_debug_cookie != SK_HEAD_SKB
+			&& skb->next->magic_debug_cookie != SK_GOOD_SKB) {
+			printk("File: %s Line %d, bad next head-skb member\n",
+				file,line);
+			return -1;
+		}
+		if (skb->prev->magic_debug_cookie != SK_HEAD_SKB
+			&& skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
+			printk("File: %s Line %d, bad prev head-skb member\n",
+				file,line);
+			return -1;
+		}
+#if 0
+		{
+		struct sk_buff *skb2 = skb->next;
+		int i = 0;
+		while (skb2 != skb && i < 5) {
+			if (skb_check(skb2, 0, line, file) < 0) {
+				printk("bad queue element in whole queue\n");
+				return -1;
+			}
+			i++;
+			skb2 = skb2->next;
+		}
+		}
+#endif
+		return 0;
+	}
+	if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
+		&& skb->next->magic_debug_cookie != SK_GOOD_SKB) {
+		printk("File: %s Line %d, bad next skb member\n",
+			file,line);
+		return -1;
+	}
+	if (skb->prev != NULL && skb->prev->magic_debug_cookie != SK_HEAD_SKB
+		&& skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
+		printk("File: %s Line %d, bad prev skb member\n",
+			file,line);
+		return -1;
+	}
 
 
-void skb_check(struct sk_buff *skb, int line, char *file)
-{
 	if(skb->magic_debug_cookie==SK_FREED_SKB)
 	{
 		printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
 			file,line);
-		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n",
-			skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free);
+		printk("skb=%p, real size=%ld, claimed size=%ld, free=%d\n",
+			skb,skb->truesize,skb->mem_len,skb->free);
+		return -1;
 	}
 	if(skb->magic_debug_cookie!=SK_GOOD_SKB)
 	{
 		printk("File: %s Line %d, passed a non skb!\n", file,line);
-		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n",
-			skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free);
+		printk("skb=%p, real size=%ld, claimed size=%ld, free=%d\n",
+			skb,skb->truesize,skb->mem_len,skb->free);
+		return -1;
 	}
 	if(skb->mem_len!=skb->truesize)
 	{
 		printk("File: %s Line %d, Dubious size setting!\n",file,line);
-		printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p\n",
-			skb,skb->truesize,skb->mem_len,skb->magic,skb->list);
+		printk("skb=%p, real size=%ld, claimed size=%ld\n",
+			skb,skb->truesize,skb->mem_len);
+		return -1;
 	}
 	/* Guess it might be acceptable then */
+	return 0;
+}
+#endif
+
+
+void skb_queue_head_init(struct sk_buff_head *list)
+{
+	list->prev = (struct sk_buff *)list;
+	list->next = (struct sk_buff *)list;
+#if CONFIG_SKB_CHECK
+	list->magic_debug_cookie = SK_HEAD_SKB;
+#endif
 }
 
+
 /*
  *	Insert an sk_buff at the start of a list.
  */
-
-void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk)
+    
+void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
 {
 	unsigned long flags;
+	struct sk_buff *list = (struct sk_buff *)list_;
 
-	IS_SKB(newsk);
-	if(newsk->list)
-		printk("Suspicious queue head: sk_buff on list!\n");
 	save_flags(flags);
 	cli();
-	newsk->list=list;
+
+#if CONFIG_SKB_CHECK
+	IS_SKB(newsk);
+	IS_SKB_HEAD(list);
+	if (newsk->next || newsk->prev)
+		printk("Suspicious queue head: sk_buff on list!\n");
+#endif
 
-	newsk->next=*list;
+	newsk->next = list->next;
+	newsk->prev = list;
 
-	if(*list)
-		newsk->prev=(*list)->prev;
-	else
-		newsk->prev=newsk;
-	newsk->prev->next=newsk;
-	newsk->next->prev=newsk;
-	IS_SKB(newsk->prev);
-	IS_SKB(newsk->next);
-	*list=newsk;
+	newsk->next->prev = newsk;
+	newsk->prev->next = newsk;
+	
 	restore_flags(flags);
 }
 
 /*
  *	Insert an sk_buff at the end of a list.
  */
-
-void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk)
+ 
+void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
 {
 	unsigned long flags;
-
-	if(newsk->list)
-		printk("Suspicious queue tail: sk_buff on list!\n");
+	struct sk_buff *list = (struct sk_buff *)list_;
 
-	IS_SKB(newsk);
 	save_flags(flags);
 	cli();
 
-	newsk->list=list;
-	if(*list)
-	{
-		(*list)->prev->next=newsk;
-		newsk->prev=(*list)->prev;
-		newsk->next=*list;
-		(*list)->prev=newsk;
-	}
-	else
-	{
-		newsk->next=newsk;
-		newsk->prev=newsk;
-		*list=newsk;
-	}
-	IS_SKB(newsk->prev);
-	IS_SKB(newsk->next);
-	restore_flags(flags);
+#if CONFIG_SKB_CHECK
+	if (newsk->next || newsk->prev)
+		printk("Suspicious queue tail: sk_buff on list!\n");
+	IS_SKB(newsk);
+	IS_SKB_HEAD(list);
+#endif
 
+	newsk->next = list;
+	newsk->prev = list->prev;
+
+	newsk->next->prev = newsk;
+	newsk->prev->next = newsk;
+
+	restore_flags(flags);
 }
 
 /*
@@ -148,93 +198,88 @@
  *	so you can grab read and free buffers as another process adds them.
  */
 
-struct sk_buff *skb_dequeue(struct sk_buff *volatile* list)
+struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
 {
 	long flags;
 	struct sk_buff *result;
+	struct sk_buff *list = (struct sk_buff *)list_;
 
 	save_flags(flags);
 	cli();
 
-	if(*list==NULL)
-	{
+	IS_SKB_HEAD(list);
+	
+	result = list->next;
+	if (result == list) {
 		restore_flags(flags);
-		return(NULL);
+		return NULL;
 	}
+	
+	result->next->prev = list;
+	list->next = result->next;
 
-	result=*list;
-	if(result->next==result)
-		*list=NULL;
-	else
-	{
-		result->next->prev=result->prev;
-		result->prev->next=result->next;
-		*list=result->next;
-	}
+	result->next = NULL;
+	result->prev = NULL;
 
-	IS_SKB(result);
 	restore_flags(flags);
-
-	if(result->list!=list)
-		printk("Dequeued packet has invalid list pointer\n");
-
-	result->list=0;
-	result->next=0;
-	result->prev=0;
-	return(result);
+	
+	return result;
 }
 
 /*
  *	Insert a packet before another one in a list.
  */
-
+ 
 void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
 {
 	unsigned long flags;
 
+#if CONFIG_SKB_CHECK
 	IS_SKB(old);
 	IS_SKB(newsk);
 
-	if(!old->list)
+	if(!old->next || !old->prev)
 		printk("insert before unlisted item!\n");
-	if(newsk->list)
+	if(newsk->next || newsk->prev)
 		printk("inserted item is already on a list.\n");
+#endif
 
 	save_flags(flags);
 	cli();
-	newsk->list=old->list;
-	newsk->next=old;
-	newsk->prev=old->prev;
-	newsk->next->prev=newsk;
-	newsk->prev->next=newsk;
-
+	newsk->next = old;
+	newsk->prev = old->prev;
+	old->prev = newsk;
+	newsk->prev->next = newsk;
+	
 	restore_flags(flags);
 }
 
 /*
  *	Place a packet after a given packet in a list.
  */
-
+ 
 void skb_append(struct sk_buff *old, struct sk_buff *newsk)
 {
 	unsigned long flags;
 
+#if CONFIG_SKB_CHECK
 	IS_SKB(old);
 	IS_SKB(newsk);
 
-	if(!old->list)
+	if(!old->next || !old->prev)
 		printk("append before unlisted item!\n");
-	if(newsk->list)
+	if(newsk->next || newsk->prev)
 		printk("append item is already on a list.\n");
+#endif
 
 	save_flags(flags);
 	cli();
-	newsk->list=old->list;
-	newsk->prev=old;
-	newsk->next=old->next;
-	newsk->next->prev=newsk;
-	newsk->prev->next=newsk;
 
+	newsk->prev = old;
+	newsk->next = old->next;
+	newsk->next->prev = newsk;
+	old->next = newsk;
+
 	restore_flags(flags);
 }
 
@@ -244,131 +289,94 @@
  *	MUST EXIST when you unlink. Thus a list must have its contents unlinked
  *	_FIRST_.
  */
-
+ 
 void skb_unlink(struct sk_buff *skb)
 {
 	unsigned long flags;
+
 	save_flags(flags);
 	cli();
 
 	IS_SKB(skb);
 
-	if(skb->list)
+	if(skb->prev && skb->next)
 	{
-		skb->next->prev=skb->prev;
-		skb->prev->next=skb->next;
-		if(*skb->list==skb)
-		{
-			if(skb->next==skb)
-				*skb->list=NULL;
-			else
-				*skb->list=skb->next;
-		}
-		skb->next=0;
-		skb->prev=0;
-		skb->list=0;
+		skb->next->prev = skb->prev;
+		skb->prev->next = skb->next;
+		skb->next = NULL;
+		skb->prev = NULL;
 	}
+#ifdef PARANOID_BUGHUNT_MODE	/* This is legal but we sometimes want to watch it */
+	else
+		printk("skb_unlink: not a linked element\n");
+#endif		
 	restore_flags(flags);
 }
 
 /*
- *	An skbuff list has had its head reassigned. Move all the list
- *	pointers. Must be called with ints off during the whole head
- *	shifting
- */
-
-void skb_new_list_head(struct sk_buff *volatile* list)
-{
-	struct sk_buff *skb=skb_peek(list);
-	if(skb!=NULL)
-	{
-		do
-		{
-			IS_SKB(skb);
-			skb->list=list;
-			skb=skb->next;
-		}
-		while(skb!=*list);
-	}
-}
-
-/*
- *	Peek an sk_buff. Unlike most other operations you _MUST_
- *	be careful with this one. A peek leaves the buffer on the
- *	list and someone else may run off with it. For an interrupt
- *	type system cli() peek the buffer copy the data and sti();
- */
-
-struct sk_buff *skb_peek(struct sk_buff *volatile* list)
-{
-	return *list;
-}
-
-/*
  *	Get a clone of an sk_buff. This is the safe way to peek at
  *	a socket queue without accidents. Its a bit long but most
  *	of it acutally ends up as tiny bits of inline assembler
  *	anyway. Only the memcpy of upto 4K with ints off is not
  *	as nice as I'd like.
  */
-
-struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
+ 
+struct sk_buff *skb_peek_copy(struct sk_buff_head *list_)
 {
+	struct sk_buff *list = (struct sk_buff *)list_;
 	struct sk_buff *orig,*newsk;
 	unsigned long flags;
 	unsigned int len;
 	/* Now for some games to avoid races */
 
+	IS_SKB_HEAD(list);
 	do
 	{
 		save_flags(flags);
 		cli();
-		orig=skb_peek(list);
-		if(orig==NULL)
-		{
+		orig = list->next;
+		if (orig == list) {
 			restore_flags(flags);
 			return NULL;
 		}
 		IS_SKB(orig);
-		len=orig->truesize;
+		len = orig->truesize;
 		restore_flags(flags);
 
-		newsk=alloc_skb(len,GFP_KERNEL);	/* May sleep */
+		newsk = alloc_skb(len,GFP_KERNEL);	/* May sleep */
 
-		if(newsk==NULL)		/* Oh dear... not to worry */
+		if (newsk == NULL)		/* Oh dear... not to worry */
 			return NULL;
-
+	
 		save_flags(flags);
 		cli();
-		if(skb_peek(list)!=orig)	/* List changed go around another time */
+		if (list->next != orig)	/* List changed go around another time */
 		{
 			restore_flags(flags);
-			newsk->sk=NULL;
-			newsk->free=1;
-			newsk->mem_addr=newsk;
-			newsk->mem_len=len;
+			newsk->sk = NULL;
+			newsk->free = 1;
+			newsk->mem_addr = newsk;
+			newsk->mem_len = len;
 			kfree_skb(newsk, FREE_WRITE);
 			continue;
 		}
-
+		
 		IS_SKB(orig);
 		IS_SKB(newsk);
 		memcpy(newsk,orig,len);
-		newsk->list=NULL;
-		newsk->magic=0;
-		newsk->next=NULL;
-		newsk->prev=NULL;
-		newsk->mem_addr=newsk;
-		newsk->h.raw+=((char *)newsk-(char *)orig);
-		newsk->link3=NULL;
-		newsk->sk=NULL;
-		newsk->free=1;
+		newsk->next = NULL;
+		newsk->prev = NULL;
+		newsk->mem_addr = newsk;
+		newsk->h.raw += ((char *)newsk - (char *)orig);
+		newsk->link3 = NULL;
+		newsk->sk = NULL;
+		newsk->free = 1;
 	}
 	while(0);
-
+	
 	restore_flags(flags);
-	return(newsk);
-}
+	return newsk;
+}	
 
 /*
  *	Free an sk_buff. This still knows about things it should
@@ -377,30 +385,29 @@
 
 void kfree_skb(struct sk_buff *skb, int rw)
 {
-	if (skb == NULL) {
+	if (skb == NULL) 
+	{
 		printk("kfree_skb: skb = NULL\n");
 		return;
-	}
+  	}
 	IS_SKB(skb);
-	if(skb->lock)
+	if (skb->lock) 
 	{
-		skb->free=1;	/* Free when unlocked */
+		skb->free = 1;    /* Free when unlocked */
 		return;
-	}
-	
-	if(skb->free == 2)
+  	}
+  	if (skb->free == 2)
 		printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n");
-	if(skb->list)
-		printk("Warning: kfree_skb passed an skb still on a list.\n");
-	skb->magic = 0;
-	if (skb->sk)
+	if (skb->next)
+	 	printk("Warning: kfree_skb passed an skb still on a list.\n");
+	if (skb->sk) 
 	{
-		if(skb->sk->prot!=NULL)
+	        if(skb->sk->prot!=NULL)
 		{
 			if (rw)
-				skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len);
-			else
-				skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len);
+		     		skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len);
+		     	else
+		     		skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len);
 
 		}
 		else
@@ -411,11 +418,11 @@
 			else
 				skb->sk->wmem_alloc-=skb->mem_len;
 			if(!skb->sk->dead)
-			wake_up_interruptible(skb->sk->sleep);
+				wake_up_interruptible(skb->sk->sleep);
 			kfree_skbmem(skb->mem_addr,skb->mem_len);
 		}
-	}
-	else
+	} 
+	else 
 		kfree_skbmem(skb->mem_addr, skb->mem_len);
 }
 
@@ -423,57 +430,111 @@
  *	Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
  *	fields and also do memory statistics to find all the [BEEP] leaks.
  */
-
-struct sk_buff *alloc_skb(unsigned int size,int priority)
-{
-	struct sk_buff *skb;
-
-	if (intr_count && priority != GFP_ATOMIC) {
+ 
+ struct sk_buff *alloc_skb(unsigned int size,int priority)
+ {
+ 	struct sk_buff *skb;
+ 		
+ 	if (intr_count && priority!=GFP_ATOMIC) {
 		static int count = 0;
 		if (++count < 5) {
 			printk("alloc_skb called nonatomically from interrupt %08lx\n",
 				((unsigned long *)&size)[-1]);
 			priority = GFP_ATOMIC;
 		}
-	}
-	skb=(struct sk_buff *)kmalloc(size,priority);
-	if(skb==NULL)
-		return NULL;
-	skb->free= 2;	/* Invalid so we pick up forgetful users */
-	skb->list= 0;	/* Not on a list */
-	skb->lock= 0;
-	skb->truesize=size;
-	skb->mem_len=size;
-	skb->mem_addr=skb;
-	skb->fraglist=NULL;
-	net_memory+=size;
-	net_skbcount++;
-	skb->magic_debug_cookie=SK_GOOD_SKB;
-	skb->users=0;
-	return skb;
+ 	}
+ 	
+ 	size+=sizeof(struct sk_buff);
+ 	skb=(struct sk_buff *)kmalloc(size,priority);
+ 	if (skb == NULL)
+ 		return NULL;
+
+ 	skb->free = 2;	/* Invalid so we pick up forgetful users */
+	skb->lock = 0;
+ 	skb->truesize = size;
+ 	skb->mem_len = size;
+ 	skb->mem_addr = skb;
+ 	skb->fraglist = NULL;
+	skb->prev = skb->next = NULL;
+	skb->link3 = NULL;
+	skb->sk = NULL;
+	skb->stamp.tv_sec=0;	/* No idea about time */
+ 	net_memory += size;
+ 	net_skbcount++;
+#if CONFIG_SKB_CHECK
+	skb->magic_debug_cookie = SK_GOOD_SKB;
+#endif
+ 	skb->users = 0;
+ 	return skb;
 }
 
 /*
  *	Free an skbuff by memory
- */
+ */ 	
 
 void kfree_skbmem(void *mem,unsigned size)
 {
-	struct sk_buff *x=(struct sk_buff *) mem;
+#if CONFIG_SKB_CHECK
+	struct sk_buff *x = mem;
 	IS_SKB(x);
-	if(x->magic_debug_cookie==SK_GOOD_SKB)
+	if(x->magic_debug_cookie == SK_GOOD_SKB)
 	{
-		x->magic_debug_cookie=SK_FREED_SKB;
+		x->magic_debug_cookie = SK_FREED_SKB;
 		kfree_s(mem,size);
 		net_skbcount--;
-		net_memory-=size;
+		net_memory -= size;
 	}
+	else
+		printk("kfree_skbmem: bad magic cookie\n");
+#else
+	kfree_s(mem, size);
+#endif
 }
 
 /*
- *	Skbuff device locking
+ *	Duplicate an sk_buff. The new one is not owned by a socket or locked
+ *	and will be freed on deletion.
  */
- 
+
+struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
+{
+	struct sk_buff *n;
+	unsigned long offset;
+	
+	n=alloc_skb(skb->mem_len-sizeof(struct sk_buff),priority);
+	if(n==NULL)
+		return NULL;
+		
+	offset=((char *)n)-((char *)skb);
+		
+	memcpy(n->data,skb->data,skb->mem_len-sizeof(struct sk_buff));
+	n->len=skb->len;
+	n->link3=NULL;
+	n->sk=NULL;
+	n->when=skb->when;
+	n->dev=skb->dev;
+	n->h.raw=skb->h.raw+offset;
+	n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
+	n->fraglen=skb->fraglen;
+	n->fraglist=skb->fraglist;
+	n->saddr=skb->saddr;
+	n->daddr=skb->daddr;
+	n->raddr=skb->raddr;
+	n->acked=skb->acked;
+	n->used=skb->used;
+	n->free=1;
+	n->arp=skb->arp;
+	n->tries=0;
+	n->lock=0;
+	n->users=0;
+	return n;
+}
+	
+	
+/*
+ *     Skbuff device locking
+ */
+
 void skb_kept_by_device(struct sk_buff *skb)
 {
 	skb->lock++;
@@ -485,16 +546,13 @@
 
 	save_flags(flags);
 	cli();
-	if (!--skb->lock) {
-		if (skb->free==1)
-			kfree_skb(skb,mode);
-	}
+	if (!--skb->lock && skb->free == 1)
+		kfree_skb(skb,mode);
 	restore_flags(flags);
 }
 
 int skb_device_locked(struct sk_buff *skb)
 {
-	if(skb->lock)
-		return 1;
-	return 0;
+	return skb->lock? 1 : 0;
 }
+
diff -u --recursive --new-file v1.1.3/linux/net/inet/skbuff.h linux/net/inet/skbuff.h
--- v1.1.3/linux/net/inet/skbuff.h	Tue Apr  5 07:30:48 1994
+++ linux/net/inet/skbuff.h
@@ -1,111 +0,0 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		Definitions for the 'struct sk_buff' memory handlers.
- *
- * Version:	@(#)skbuff.h	1.0.4	05/20/93
- *
- * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
- *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- *		Corey Minyard <wf-rch!minyard@relay.EU.net>
- *
- * Fixes:
- *		Alan Cox		: 	Volatiles (this makes me unhappy - we want proper asm linked list stuff)
- *		Alan Cox		:	Declaration for new primitives
- *		Alan Cox		:	Fraglist support (idea by Donald Becker)
- *		Alan Cox		:	'users' counter. Combines with datagram changes to avoid skb_peek_copy
- *						being used.
- *
- *		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.
- */
-#ifndef _SKBUFF_H
-#define _SKBUFF_H
-#include <linux/malloc.h>
-
-#ifdef CONFIG_IPX
-#include "ipx.h"
-#endif
-
-#define HAVE_ALLOC_SKB		/* For the drivers to know */
-
-
-#define FREE_READ	1
-#define FREE_WRITE	0
-
-
-struct sk_buff {
-  unsigned long			magic_debug_cookie;
-  struct sk_buff		*volatile next;
-  struct sk_buff		*volatile prev;
-  struct sk_buff		*volatile link3;
-  struct sk_buff		*volatile* list;
-  struct sock			*sk;
-  volatile unsigned long	when;	/* used to compute rtt's	*/
-  struct device			*dev;
-  void				*mem_addr;
-  union {
-	struct tcphdr	*th;
-	struct ethhdr	*eth;
-	struct iphdr	*iph;
-	struct udphdr	*uh;
-	struct arphdr	*arp;
-	unsigned char	*raw;
-	unsigned long	seq;
-#ifdef CONFIG_IPX	
-	ipx_packet	*ipx;
-#endif	
-  } h;
-  struct iphdr		*ip_hdr;		/* For IPPROTO_RAW */
-  unsigned long			mem_len;
-  unsigned long 		len;
-  unsigned long			fraglen;
-  struct sk_buff		*fraglist;	/* Fragment list */
-  unsigned long			truesize;
-  unsigned long 		saddr;
-  unsigned long 		daddr;
-  int				magic;
-  volatile char 		acked,
-				used,
-				free,
-				arp;
-  unsigned char			tries,lock;	/* Lock is now unused */
-  unsigned short		users;		/* User count - see datagram.c (and soon seqpacket.c/stream.c) */
-  unsigned long			padding[0];
-  unsigned char			data[0];
-};
-
-#define SK_WMEM_MAX	32767
-#define SK_RMEM_MAX	32767
-
-#define SK_FREED_SKB	0x0DE2C0DE
-#define SK_GOOD_SKB	0xDEC0DED1
-
-extern void			print_skb(struct sk_buff *);
-extern void			kfree_skb(struct sk_buff *skb, int rw);
-extern void			skb_queue_head(struct sk_buff * volatile *list,struct sk_buff *buf);
-extern void			skb_queue_tail(struct sk_buff * volatile *list,struct sk_buff *buf);
-extern struct sk_buff *		skb_dequeue(struct sk_buff * volatile *list);
-extern void 			skb_insert(struct sk_buff *old,struct sk_buff *newsk);
-extern void			skb_append(struct sk_buff *old,struct sk_buff *newsk);
-extern void			skb_unlink(struct sk_buff *buf);
-extern void 			skb_new_list_head(struct sk_buff *volatile* list);
-extern struct sk_buff *		skb_peek(struct sk_buff * volatile *list);
-extern struct sk_buff *		skb_peek_copy(struct sk_buff * volatile *list);
-extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
-extern void			kfree_skbmem(void *mem, unsigned size);
-extern void			skb_kept_by_device(struct sk_buff *skb);
-extern void			skb_device_release(struct sk_buff *skb, int mode);
-extern int			skb_device_locked(struct sk_buff *skb);
-extern void 			skb_check(struct sk_buff *skb,int, char *);
-#define IS_SKB(skb)	skb_check((skb),__LINE__,__FILE__)
-
-extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
-extern int			datagram_select(struct sock *sk, int sel_type, select_table *wait);
-extern void			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
-extern void			skb_free_datagram(struct sk_buff *skb);
-#endif	/* _SKBUFF_H */
diff -u --recursive --new-file v1.1.3/linux/net/inet/snmp.h linux/net/inet/snmp.h
--- v1.1.3/linux/net/inet/snmp.h
+++ linux/net/inet/snmp.h	Wed Apr 13 12:52:27 1994
@@ -0,0 +1,107 @@
+/*
+ *
+ *		SNMP MIB entries for the IP subsystem.
+ *		
+ *		Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ *		We don't chose to implement SNMP in the kernel (this would
+ *		be silly as SNMP is a pain in the backside in places). We do
+ *		however need to collect the MIB statistics and export them
+ *		out of /proc (eventually)
+ *
+ *		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.
+ *
+ */
+ 
+#ifndef _SNMP_H
+#define _SNMP_H
+ 
+/*
+ *	We use all unsigned longs. Linux will soon be so reliable that even these
+ *	will rapidly get too small 8-). Seriously consider the IpInReceives count
+ *	on the 20Gb/s + networks people expect in a few years time!
+ */
+  
+struct ip_mib
+{
+ 	unsigned long	IpForwarding;
+ 	unsigned long	IpDefaultTTL;
+ 	unsigned long	IpInReceives;
+ 	unsigned long	IpInHdrErrors;
+ 	unsigned long	IpInAddrErrors;
+ 	unsigned long	IpForwDatagrams;
+ 	unsigned long	IpInUnknownProtos;
+ 	unsigned long	IpInDiscards;
+ 	unsigned long	IpInDelivers;
+ 	unsigned long	IpOutRequests;
+ 	unsigned long	IpOutDiscards;
+ 	unsigned long	IpOutNoRoutes;
+ 	unsigned long	IpReasmTimeout;
+ 	unsigned long	IpReasmReqds;
+ 	unsigned long	IpReasmOKs;
+ 	unsigned long	IpReasmFails;
+ 	unsigned long	IpFragOKs;
+ 	unsigned long	IpFragFails;
+ 	unsigned long	IpFragCreates;
+};
+ 
+ 
+struct icmp_mib
+{
+ 	unsigned long	IcmpInMsgs;
+ 	unsigned long	IcmpInErrors;
+  	unsigned long	IcmpInDestUnreachs;
+ 	unsigned long	IcmpInTimeExcds;
+ 	unsigned long	IcmpInParmProbs;
+ 	unsigned long	IcmpInSrcQuenchs;
+ 	unsigned long	IcmpInRedirects;
+ 	unsigned long	IcmpInEchos;
+ 	unsigned long	IcmpInEchoReps;
+ 	unsigned long	IcmpInTimestamps;
+ 	unsigned long	IcmpInTimestampReps;
+ 	unsigned long	IcmpInAddrMasks;
+ 	unsigned long	IcmpInAddrMaskReps;
+ 	unsigned long	IcmpOutMsgs;
+ 	unsigned long	IcmpOutErrors;
+ 	unsigned long	IcmpOutDestUnreachs;
+ 	unsigned long	IcmpOutTimeExcds;
+ 	unsigned long	IcmpOutParmProbs;
+ 	unsigned long	IcmpOutSrcQuenchs;
+ 	unsigned long	IcmpOutRedirects;
+ 	unsigned long	IcmpOutEchos;
+ 	unsigned long	IcmpOutEchoReps;
+ 	unsigned long	IcmpOutTimestamps;
+ 	unsigned long	IcmpOutTimestampReps;
+ 	unsigned long	IcmpOutAddrMasks;
+ 	unsigned long	IcmpOutAddrMaskReps;
+};
+ 
+struct tcp_mib
+{
+ 	unsigned long	TcpRtoAlgorithm;
+ 	unsigned long	TcpRtoMin;
+ 	unsigned long	TcpRtoMax;
+ 	unsigned long	TcpMaxConn;
+ 	unsigned long	TcpActiveOpens;
+ 	unsigned long	TcpPassiveOpens;
+ 	unsigned long	TcpAttemptFails;
+ 	unsigned long	TcpEstabResets;
+ 	unsigned long	TcpCurrEstab;
+ 	unsigned long	TcpInSegs;
+ 	unsigned long	TcpOutSegs;
+ 	unsigned long	TcpRetransSegs;
+};
+ 
+struct udp_mib
+{
+ 	unsigned long	UdpInDatagrams;
+ 	unsigned long	UdpNoPorts;
+ 	unsigned long	UdpInErrors;
+ 	unsigned long	UdpOutDatagrams;
+};
+ 
+ 	
+#endif
diff -u --recursive --new-file v1.1.3/linux/net/inet/sock.c linux/net/inet/sock.c
--- v1.1.3/linux/net/inet/sock.c	Tue Apr  5 07:37:25 1994
+++ linux/net/inet/sock.c	Wed Apr 13 12:52:28 1994
@@ -81,15 +81,15 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "arp.h"
 #include "route.h"
 #include "tcp.h"
 #include "udp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "raw.h"
 #include "icmp.h"
@@ -112,10 +112,7 @@
   }
   printk("  wmem_alloc = %lu\n", sk->wmem_alloc);
   printk("  rmem_alloc = %lu\n", sk->rmem_alloc);
-  printk("  send_head = %p\n", sk->send_head);
   printk("  state = %d\n",sk->state);
-  printk("  wback = %p, rqueue = %p\n", sk->wback, sk->rqueue);
-  printk("  wfront = %p\n", sk->wfront);
   printk("  daddr = %lX, saddr = %lX\n", sk->daddr,sk->saddr);
   printk("  num = %d", sk->num);
   printk(" next = %p\n", sk->next);
@@ -124,7 +121,7 @@
   printk("  rcv_ack_seq = %ld, window_seq = %ld, fin_seq = %ld\n",
 	  sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
   printk("  prot = %p\n", sk->prot);
-  printk("  pair = %p, back_log = %p\n", sk->pair,sk->back_log);
+  printk("  pair = %p\n", sk->pair);
   printk("  inuse = %d , blog = %d\n", sk->inuse, sk->blog);
   printk("  dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
   printk("  retransmits = %ld, timeout = %d\n", sk->retransmits, sk->timeout);
@@ -134,6 +131,7 @@
 }
 
 
+#if 0
 void
 print_skb(struct sk_buff *skb)
 {
@@ -146,6 +144,7 @@
   printk("  mem_addr = %p, mem_len = %lu\n", skb->mem_addr, skb->mem_len);
   printk("  used = %d free = %d\n", skb->used,skb->free);
 }
+#endif
 
 
 
@@ -272,10 +271,6 @@
   struct sock *sk2;
 
   DPRINTF((DBG_INET, "remove_sock(sk1=%X)\n", sk1));
-  if (!sk1) {
-	printk("sock.c: remove_sock: sk1 == NULL\n");
-	return;
-  }
 
   if (!sk1->prot) {
 	printk("sock.c: remove_sock: sk1->prot == NULL\n");
@@ -323,97 +318,61 @@
   	/* Now we can no longer get new packets. */
   	delete_timer(sk);
 
-
-	while ((skb = tcp_dequeue_partial(sk)) != NULL) 
-  	{
-  		IS_SKB(skb);
-  		kfree_skb(skb, FREE_WRITE);
-  	}
-
-  /* Cleanup up the write buffer. */
-  	for(skb = sk->wfront; skb != NULL; ) 
-  	{
-		struct sk_buff *skb2;
+	while ((skb = tcp_dequeue_partial(sk)) != NULL) {
+		IS_SKB(skb);
+		kfree_skb(skb, FREE_WRITE);
+	}
 
-		skb2=(struct sk_buff *)skb->next;
-		if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
-			printk("sock.c:destroy_sock write queue with bad magic(%X)\n",
-								skb->magic);
-			break;
-		}
+	/* Cleanup up the write buffer. */
+  	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		IS_SKB(skb);
 		kfree_skb(skb, FREE_WRITE);
-		skb = skb2;
   	}
 
-  	sk->wfront = NULL;
-  	sk->wback = NULL;
-
-  	if (sk->rqueue != NULL) 
-  	{
-	  	while((skb=skb_dequeue(&sk->rqueue))!=NULL)
-	  	{
-		/*
-		 * This will take care of closing sockets that were
-		 * listening and didn't accept everything.
-		 */
-			if (skb->sk != NULL && skb->sk != sk) 
-			{
-				IS_SKB(skb);
-				skb->sk->dead = 1;
-				skb->sk->prot->close(skb->sk, 0);
-			}
+  	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
+	/*
+	 * This will take care of closing sockets that were
+	 * listening and didn't accept everything.
+	 */
+		if (skb->sk != NULL && skb->sk != sk) 
+		{
 			IS_SKB(skb);
-			kfree_skb(skb, FREE_READ);
+			skb->sk->dead = 1;
+			skb->sk->prot->close(skb->sk, 0);
 		}
-  	}
-  	sk->rqueue = NULL;
+		IS_SKB(skb);
+		kfree_skb(skb, FREE_READ);
+	}
 
   /* Now we need to clean up the send head. */
-  	for(skb = sk->send_head; skb != NULL; ) 
-  	{
+	cli();
+	for(skb = sk->send_head; skb != NULL; )
+	{
 		struct sk_buff *skb2;
 
 		/*
 		 * We need to remove skb from the transmit queue,
 		 * or maybe the arp queue.
 		 */
-		cli();
-		/* see if it's in a transmit queue. */
-		/* this can be simplified quite a bit.  Look */
-		/* at tcp.c:tcp_ack to see how. */
-		if (skb->next != NULL) 
-		{
+		if (skb->next  && skb->prev) {
+			printk("destroy_sock: unlinked skb\n");
 			IS_SKB(skb);
 			skb_unlink(skb);
 		}
 		skb->dev = NULL;
-		sti();
-		skb2 = (struct sk_buff *)skb->link3;
+		skb2 = skb->link3;
 		kfree_skb(skb, FREE_WRITE);
 		skb = skb2;
-  	}	
-  	sk->send_head = NULL;
+	}
+	sk->send_head = NULL;
+	sti();
 
   	/* And now the backlog. */
-  	if (sk->back_log != NULL) 
-  	{
+  	while((skb=skb_dequeue(&sk->back_log))!=NULL) {
 		/* this should never happen. */
-		printk("cleaning back_log. \n");
-		cli();
-		skb = (struct sk_buff *)sk->back_log;
-		do 
-		{
-			struct sk_buff *skb2;
-	
-			skb2 = (struct sk_buff *)skb->next;
-			kfree_skb(skb, FREE_READ);
-			skb = skb2;
-		}
-		while(skb != sk->back_log);
-		sti();
+		printk("cleaning back_log\n");
+		kfree_skb(skb, FREE_READ);
 	}
-	sk->back_log = NULL;
 
   /* Now if it has a half accepted/ closed socket. */
 	if (sk->pair) 
@@ -452,10 +411,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   switch(cmd) {
 	case F_SETOWN:
@@ -831,6 +786,7 @@
   sk->nonagle = 0;
 #endif  
   sk->type = sock->type;
+  sk->stamp.tv_sec=0;
   sk->protocol = protocol;
   sk->wmem_alloc = 0;
   sk->rmem_alloc = 0;
@@ -882,14 +838,13 @@
   sk->max_ack_backlog = 0;
   sk->inuse = 0;
   sk->delay_acks = 0;
-  sk->wback = NULL;
-  sk->wfront = NULL;
-  sk->rqueue = NULL;
+  skb_queue_head_init(&sk->write_queue);
+  skb_queue_head_init(&sk->receive_queue);
   sk->mtu = 576;
   sk->prot = prot;
   sk->sleep = sock->wait;
   sk->daddr = 0;
-  sk->saddr = my_addr();
+  sk->saddr = ip_my_addr();
   sk->err = 0;
   sk->next = NULL;
   sk->pair = NULL;
@@ -899,7 +854,7 @@
   sk->broadcast = 0;
   sk->timer.data = (unsigned long)sk;
   sk->timer.function = &net_timer;
-  sk->back_log = NULL;
+  skb_queue_head_init(&sk->back_log);
   sk->blog = 0;
   sock->data =(void *) sk;
   sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
@@ -1017,13 +972,9 @@
   struct sock *sk, *sk2;
   unsigned short snum;
   int err;
+  int chk_addr_ret;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
-
   /* check this error. */
   if (sk->state != TCP_CLOSE) return(-EIO);
   if (sk->num != 0) return(-EINVAL);
@@ -1047,10 +998,11 @@
   }
   if (snum < PROT_SOCK && !suser()) return(-EACCES);
 
-  if (addr.sin_addr.s_addr!=0 && chk_addr(addr.sin_addr.s_addr)!=IS_MYADDR)
+  chk_addr_ret = ip_chk_addr(addr.sin_addr.s_addr);
+  if (addr.sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR)
   	return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
   	
-  if (chk_addr(addr.sin_addr.s_addr) || addr.sin_addr.s_addr == 0)
+  if (chk_addr_ret || addr.sin_addr.s_addr == 0)
 					sk->saddr = addr.sin_addr.s_addr;
 
   DPRINTF((DBG_INET, "sock_array[%d] = %X:\n", snum &(SOCK_ARRAY_SIZE -1),
@@ -1100,10 +1052,6 @@
 
   sock->conn = NULL;
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
   {
@@ -1184,23 +1132,19 @@
   int err;
 
   sk1 = (struct sock *) sock->data;
-  if (sk1 == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   /*
    * We've been passed an extra socket.
    * We need to free it up because the tcp module creates
    * it's own when it accepts one.
    */
-  if (newsock->data) {
-  	struct sock * sk = (struct sock *) newsock->data;
-  	newsock->data = NULL;
-  	sk->dead = 1;
+  if (newsock->data)
+  {
+  	struct sock *sk=(struct sock *)newsock->data;
+  	newsock->data=NULL;
   	destroy_sock(sk);
   }
-
+  
   if (sk1->prot->accept == NULL) return(-EOPNOTSUPP);
 
   /* Restore the state if we have been interrupted, and then returned. */
@@ -1283,7 +1227,7 @@
 	sin.sin_addr.s_addr = sk->daddr;
   } else {
 	sin.sin_port = sk->dummy_th.source;
-	if (sk->saddr == 0) sin.sin_addr.s_addr = my_addr();
+	if (sk->saddr == 0) sin.sin_addr.s_addr = ip_my_addr();
 	  else sin.sin_addr.s_addr = sk->saddr;
   }
   len = sizeof(sin);
@@ -1301,10 +1245,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   /* We may need to bind the socket. */
   if (sk->num == 0) {
@@ -1324,10 +1264,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   /* We may need to bind the socket. */
   if (sk->num == 0) {
@@ -1346,10 +1282,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
   if (sk->shutdown & SEND_SHUTDOWN) {
 	send_sig(SIGPIPE, current, 1);
 	return(-EPIPE);
@@ -1374,10 +1306,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
   if (sk->shutdown & SEND_SHUTDOWN) {
 	send_sig(SIGPIPE, current, 1);
 	return(-EPIPE);
@@ -1402,10 +1330,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
   if (sk->shutdown & SEND_SHUTDOWN) {
 	send_sig(SIGPIPE, current, 1);
 	return(-EPIPE);
@@ -1433,10 +1357,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   if (sk->prot->recvfrom == NULL) return(-EOPNOTSUPP);
 
@@ -1487,10 +1407,6 @@
   struct sock *sk;
 
   sk = (struct sock *) sock->data;
-  if (sk == NULL) {
-	printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
-	return(0);
-  }
 
   if (sk->prot->select == NULL) {
 	DPRINTF((DBG_INET, "select on non-selectable socket.\n"));
@@ -1531,6 +1447,20 @@
 			put_fs_long(sk->proc,(int *)arg);
 		}
 		return(0);
+		
+	case SIOCGSTAMP:
+		if (sk)
+		{
+			if(sk->stamp.tv_sec==0)
+				return -ENOENT;
+			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval));
+			if(err)
+				return err;
+			memcpy_tofs((void *)arg,&sk->stamp,sizeof(struct timeval));
+			return 0;
+		}
+		return -EINVAL;
+		
 #if 0	/* FIXME: */
 	case SIOCATMARK:
 		printk("AF_INET: ioctl(SIOCATMARK, 0x%08X)\n",(void *) arg);
@@ -1542,14 +1472,13 @@
 
 	case SIOCADDRT: case SIOCADDRTOLD:
 	case SIOCDELRT: case SIOCDELRTOLD:
-		return(rt_ioctl(cmd,(void *) arg));
+		return(ip_rt_ioctl(cmd,(void *) arg));
 
 	case SIOCDARP:
 	case SIOCGARP:
 	case SIOCSARP:
 		return(arp_ioctl(cmd,(void *) arg));
 
-	case IP_SET_DEV:
 	case SIOCGIFCONF:
 	case SIOCGIFFLAGS:
 	case SIOCSIFFLAGS:
@@ -1569,6 +1498,8 @@
 	case SIOCSIFMTU:
 	case SIOCSIFLINK:
 	case SIOCGIFHWADDR:
+	case SIOCSIFHWADDR:
+	case OLD_SIOCGIFHWADDR:
 		return(dev_ioctl(cmd,(void *) arg));
 
 	default:
@@ -1589,7 +1520,7 @@
 		struct sk_buff * c = alloc_skb(size, priority);
 		if (c) {
 			cli();
-			sk->wmem_alloc+= size;
+			sk->wmem_alloc+= c->mem_len;
 			sti();
 		}
 		return c;
@@ -1610,7 +1541,7 @@
 		struct sk_buff *c = alloc_skb(size, priority);
 		if (c) {
 			cli();
-			sk->rmem_alloc += size;
+			sk->rmem_alloc += c->mem_len;
 			sti();
 		}
 		return(c);
@@ -1655,7 +1586,7 @@
 {
   DPRINTF((DBG_INET, "sock_wfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
 
-  IS_SKB((struct sk_buff *) mem);
+  IS_SKB(mem);
   kfree_skbmem(mem, size);
   if (sk) {
 	sk->wmem_alloc -= size;
@@ -1675,7 +1606,7 @@
 sock_rfree(struct sock *sk, void *mem, unsigned long size)
 {
   DPRINTF((DBG_INET, "sock_rfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
-  IS_SKB((struct sk_buff *) mem);
+  IS_SKB(mem);
   kfree_skbmem(mem, size);
   if (sk) {
 	sk->rmem_alloc -= size;
@@ -1733,45 +1664,25 @@
 
 void release_sock(struct sock *sk)
 {
-  if (!sk) {
-	printk("sock.c: release_sock sk == NULL\n");
-	return;
-  }
-  if (!sk->prot) {
-/*	printk("sock.c: release_sock sk->prot == NULL\n"); */
+  struct sk_buff *skb;
+
+  if (!sk->prot)
 	return;
-  }
 
   if (sk->blog) return;
 
   /* See if we have any packets built up. */
-  cli();
   sk->inuse = 1;
-  while(sk->back_log != NULL) {
-	struct sk_buff *skb;
-
+  while((skb = skb_dequeue(&sk->back_log)) != NULL) {
 	sk->blog = 1;
-	skb =(struct sk_buff *)sk->back_log;
 	DPRINTF((DBG_INET, "release_sock: skb = %X:\n", skb));
-	if (skb->next != skb) {
-		sk->back_log = skb->next;
-		skb->prev->next = skb->next;
-		skb->next->prev = skb->prev;
-	} else {
-		sk->back_log = NULL;
-	}
-	sti();
-	DPRINTF((DBG_INET, "sk->back_log = %X\n", sk->back_log));
 	if (sk->prot->rcv) sk->prot->rcv(skb, skb->dev, sk->opt,
 					 skb->saddr, skb->len, skb->daddr, 1,
-
-	/* Only used for/by raw sockets. */
-	(struct inet_protocol *)sk->pair); 
-	cli();
+					/* Only used for/by raw sockets. */
+					(struct inet_protocol *)sk->pair); 
   }
   sk->blog = 0;
   sk->inuse = 0;
-  sti();
   if (sk->dead && sk->state == TCP_CLOSE) {
 	/* Should be about 2 rtt's */
 	reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
@@ -1857,44 +1768,73 @@
 
 extern unsigned long seq_offset;
 
-/* Called by ddi.c on kernel startup.  */
+/*
+ *	Called by ddi.c on kernel startup.  
+ */
+ 
 void inet_proto_init(struct ddi_proto *pro)
 {
-  struct inet_protocol *p;
-  int i;
+	struct inet_protocol *p;
+	int i;
 
-  printk("Swansea University Computer Society Net2Debugged [1.30]\n");
-  /* Set up our UNIX VFS major device. */
-  if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) {
-	printk("%s: cannot register major device %d!\n",
-					pro->name, AF_INET_MAJOR);
-	return;
-  }
+	printk("Swansea University Computer Society NET3.010\n");
+	/*
+	 *	Set up our UNIX VFS major device. (compatibility)
+	 */
 
-  /* Tell SOCKET that we are alive... */
-  (void) sock_register(inet_proto_ops.family, &inet_proto_ops);
+	if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) 
+	{
+		printk("%s: cannot register major device %d!\n",
+					pro->name, AF_INET_MAJOR);
+		return;
+	}
 
-  seq_offset = CURRENT_TIME*250;
+  /*
+   *	Tell SOCKET that we are alive... 
+   */
+   
+  	(void) sock_register(inet_proto_ops.family, &inet_proto_ops);
 
-  /* Add all the protocols. */
-  for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
-	tcp_prot.sock_array[i] = NULL;
-	udp_prot.sock_array[i] = NULL;
-	raw_prot.sock_array[i] = NULL;
-  }
-  printk("IP Protocols: ");
-  for(p = inet_protocol_base; p != NULL;) {
-	struct inet_protocol *tmp;
+  	seq_offset = CURRENT_TIME*250;
 
-	tmp = (struct inet_protocol *) p->next;
-	inet_add_protocol(p);
-	printk("%s%s",p->name,tmp?", ":"\n");
-	p = tmp;
-  }
+	/*
+	 *	Add all the protocols. 
+	 */
+	 
+	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
+	{
+		tcp_prot.sock_array[i] = NULL;
+		udp_prot.sock_array[i] = NULL;
+		raw_prot.sock_array[i] = NULL;
+  	}
 
-  /* Initialize the DEV module. */
-  dev_init();
+	printk("IP Protocols: ");
+	for(p = inet_protocol_base; p != NULL;) 
+	{
+		struct inet_protocol *tmp;
+	
+		tmp = (struct inet_protocol *) p->next;
+		inet_add_protocol(p);
+		printk("%s%s",p->name,tmp?", ":"\n");
+		p = tmp;
+	}
 
-  /* Initialize the "Buffer Head" pointers. */
-  bh_base[INET_BH].routine = inet_bh;
+	/*
+	 *	Initialize the DEV module. 
+	 */	 
+	dev_init();
+	/*
+	 *	Set the ARP module up
+	 */
+	arp_init();
+  	/*
+  	 *	Set the IP module up
+  	 */
+	ip_init();
+  
+	/*
+	 *	Initialize the "Buffer Head" pointers. 
+	 */
+	 
+	bh_base[INET_BH].routine = inet_bh;
 }
diff -u --recursive --new-file v1.1.3/linux/net/inet/sock.h linux/net/inet/sock.h
--- v1.1.3/linux/net/inet/sock.h	Wed Mar  2 11:29:06 1994
+++ linux/net/inet/sock.h	Wed Apr 13 12:52:28 1994
@@ -34,7 +34,7 @@
 #include <linux/ip.h>		/* struct options */
 #include <linux/tcp.h>		/* struct tcphdr */
 
-#include "skbuff.h"		/* struct sk_buff */
+#include <linux/skbuff.h>	/* struct sk_buff */
 #include "protocol.h"		/* struct inet_protocol */
 #ifdef CONFIG_AX25
 #include "ax25.h"
@@ -89,15 +89,14 @@
   int				proc;
   struct sock			*next;
   struct sock			*pair;
-  struct sk_buff		*volatile send_tail;
-  struct sk_buff		*volatile send_head;
-  struct sk_buff		*volatile back_log;
+  struct sk_buff		* volatile send_head;
+  struct sk_buff		* volatile send_tail;
+  struct sk_buff_head		back_log;
   struct sk_buff		*partial;
   struct timer_list		partial_timer;
   long				retransmits;
-  struct sk_buff		*volatile wback,
-				*volatile wfront,
-				*volatile rqueue;
+  struct sk_buff_head		write_queue,
+				receive_queue;
   struct proto			*prot;
   struct wait_queue		**sleep;
   unsigned long			daddr;
@@ -158,6 +157,7 @@
   /* This part is used for the timeout functions (timer.c). */
   int				timeout;	/* What are we waiting for? */
   struct timer_list		timer;
+  struct timeval		stamp;
 
   /* identd */
   struct socket			*socket;
diff -u --recursive --new-file v1.1.3/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.3/linux/net/inet/tcp.c	Thu Apr  7 11:57:33 1994
+++ linux/net/inet/tcp.c	Wed Apr 13 13:13:13 1994
@@ -63,7 +63,7 @@
  *		Charles Hedrick :	TCP fixes
  *		Toomas Tamm	:	TCP window fixes
  *		Alan Cox	:	Small URG fix to rlogin ^C ack fight
- *		Charles Hedrick	:	Window fix
+ *		Charles Hedrick	:	Rewrote most of it to actually work
  *		Linus		:	Rewrote tcp_read() and URG handling
  *					completely
  *		Gerhard Koerting:	Fixed some missing timer handling
@@ -74,11 +74,7 @@
  *		it causes a select. Linux can - given the official select semantics I
  *		feel that _really_ its the BSD network programs that are bust (notably
  *		inetd, which hangs occasionally because of this).
- *			Add VJ Fastrecovery algorithm ?
  *			Protocol closedown badly messed up.
- *			Incompatiblity with spider ports (tcp hangs on that 
- *			socket occasionally).
- *		MSG_PEEK and read on same socket at once can cause crashes.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -94,15 +90,15 @@
 #include <linux/termios.h>
 #include <linux/in.h>
 #include <linux/fcntl.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "snmp.h"
 #include "ip.h"
 #include "protocol.h"
 #include "icmp.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
-#include "arp.h"
 #include <linux/errno.h>
 #include <linux/timer.h>
 #include <asm/system.h>
@@ -111,6 +107,8 @@
 
 #define SEQ_TICK 3
 unsigned long seq_offset;
+struct tcp_mib	tcp_statistics;
+
 #define SUBNETSARELOCAL
 
 static __inline__ int 
@@ -145,12 +143,6 @@
 		__print_th(th);
 }
 
-/* This routine grabs the first thing off of a rcv queue. */
-static struct sk_buff *
-get_firstr(struct sock *sk)
-{
-  return skb_dequeue(&sk->rqueue);
-}
 
 /* This routine picks a TCP windows for a socket based on
    the following constraints
@@ -276,6 +268,7 @@
    */
   if (icmp_err_convert[err & 0xff].fatal) {
 	if (sk->state == TCP_SYN_SENT) {
+		tcp_statistics.TcpAttemptFails++;
 		sk->state = TCP_CLOSE;
 		sk->error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */
 	}
@@ -295,7 +288,6 @@
   unsigned long counted;
   unsigned long amount;
   struct sk_buff *skb;
-  int count=0;
   int sum;
   unsigned long flags;
 
@@ -303,8 +295,11 @@
   if(sk && sk->debug)
   	printk("tcp_readable: %p - ",sk);
 
-  if (sk == NULL || skb_peek(&sk->rqueue) == NULL) 	/* Empty sockets are easy! */
+  save_flags(flags);
+  cli();
+  if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL)
   {
+	restore_flags(flags);
   	if(sk && sk->debug) 
   		printk("empty\n");
   	return(0);
@@ -313,22 +308,8 @@
   counted = sk->copied_seq+1;	/* Where we are at the moment */
   amount = 0;
   
-  save_flags(flags);		/* So nobody adds things at the wrong moment */
-  cli();
-  skb =(struct sk_buff *)sk->rqueue;
-
   /* Do until a push or until we are out of data. */
   do {
-	count++;
-#ifdef OLD	
-	/* This is wrong: It breaks Chameleon amongst other stacks */
-	if (count > 20) {
-		restore_flags(flags);
-		DPRINTF((DBG_TCP, "tcp_readable, more than 20 packets without a psh\n"));
-		printk("tcp_read: possible read_queue corruption.\n");
-		return(amount);
-	}
-#endif	
 	if (before(counted, skb->h.th->seq)) 	/* Found a hole so stops here */
 		break;
 	sum = skb->len -(counted - skb->h.th->seq);	/* Length - header but start from where we are up to (avoid overlaps) */
@@ -340,8 +321,8 @@
 		counted += sum;
 	}
 	if (amount && skb->h.th->psh) break;
-	skb =(struct sk_buff *)skb->next;		/* Move along */
-  } while(skb != sk->rqueue);
+	skb = skb->next;
+  } while(skb != (struct sk_buff *)&sk->receive_queue);
   if (amount && !sk->urginline && sk->urg_data &&
       (sk->urg_seq - sk->copied_seq) <= (counted - sk->copied_seq))
 	amount--;		/* don't count urg data */
@@ -372,7 +353,7 @@
 		select_wait(sk->sleep, wait);
 		if(sk->debug)
 			printk("-select out");
-		if (skb_peek(&sk->rqueue) != NULL) {
+		if (skb_peek(&sk->receive_queue) != NULL) {
 			if (sk->state == TCP_LISTEN || tcp_readable(sk)) {
 				release_sock(sk);
 				if(sk->debug)
@@ -424,10 +405,8 @@
 		DPRINTF((DBG_TCP,
 			"tcp_select: sleeping on write sk->wmem_alloc = %d, "
 			"sk->packets_out = %d\n"
-			"sk->wback = %X, sk->wfront = %X\n"
 			"sk->write_seq = %u, sk->window_seq=%u\n", 
 				sk->wmem_alloc, sk->packets_out,
-				sk->wback, sk->wfront,
 				sk->write_seq, sk->window_seq));
 
 		release_sock(sk);
@@ -513,7 +492,7 @@
 {     
   unsigned long sum;
    
-  if (saddr == 0) saddr = my_addr();
+  if (saddr == 0) saddr = ip_my_addr();
   print_th(th);
   __asm__("\t addl %%ecx,%%ebx\n"
 	  "\t adcl %%edx,%%ebx\n"
@@ -602,7 +581,8 @@
 			return;
 		}
 	}
-  
+
+	tcp_statistics.TcpOutSegs++;  
 	/* We need to complete and send the packet. */
 	tcp_send_check(th, sk->saddr, sk->daddr, size, sk);
 
@@ -614,15 +594,12 @@
 					sk->cong_window, sk->packets_out));
 		DPRINTF((DBG_TCP, "sk->write_seq = %d, sk->window_seq = %d\n",
 					sk->write_seq, sk->window_seq));
-		skb->next = NULL;
-		skb->magic = TCP_WRITE_QUEUE_MAGIC;
-		if (sk->wback == NULL) {
-			sk->wfront = skb;
-		} else {
-			sk->wback->next = skb;
+		if (skb->next != NULL) {
+			printk("tcp_send_partial: next != NULL\n");
+			skb_unlink(skb);
 		}
-		sk->wback = skb;
-		if (before(sk->window_seq, sk->wfront->h.seq) &&
+		skb_queue_tail(&sk->write_queue, skb);
+		if (before(sk->window_seq, sk->write_queue.next->h.seq) &&
 		    sk->send_head == NULL &&
 		    sk->ack_backlog == 0)
 		  reset_timer(sk, TIME_PROBE0, sk->rto);
@@ -703,12 +680,10 @@
 	if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) {
 		reset_timer(sk, TIME_WRITE, 10);
 	}
-if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
+  if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
 	return;
   }
 
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_ACK_SIZE;
   buff->len = sizeof(struct tcphdr);
   buff->sk = sk;
   t1 =(struct tcphdr *) buff->data;
@@ -719,7 +694,7 @@
   if (tmp < 0) {
   	buff->free=1;
 	sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
-if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n");
+  if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n");
 	return;
   }
   buff->len += tmp;
@@ -746,7 +721,8 @@
 	sk->ack_backlog = 0;
 	sk->bytes_rcv = 0;
 	sk->ack_timed = 0;
-	if (sk->send_head == NULL && sk->wfront == NULL && sk->timeout == TIME_WRITE) 
+	if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL
+				  && sk->timeout == TIME_WRITE) 
 	{
 		if(sk->keepopen)
 			reset_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
@@ -759,6 +735,7 @@
   tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
   if (sk->debug)
   	 printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);
+  tcp_statistics.TcpOutSegs++;
   sk->prot->queue_xmit(sk, dev, buff, 1);
 }
 
@@ -947,13 +924,13 @@
 	  release_sock(sk);
 	  /* NB: following must be mtu, because mss can be increased.
 	   * mss is always <= mtu */
-	  skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header + sizeof(*skb), 0, GFP_KERNEL);
+	  skb = prot->wmalloc(sk, sk->mtu + 128 + prot->max_header, 0, GFP_KERNEL);
 	  sk->inuse = 1;
 	  send_tmp = skb;
 	} else {
 		/* We will release the socket incase we sleep here. */
 	  release_sock(sk);
-	  skb = prot->wmalloc(sk, copy + prot->max_header + sizeof(*skb), 0, GFP_KERNEL);
+	  skb = prot->wmalloc(sk, copy + prot->max_header , 0, GFP_KERNEL);
 	  sk->inuse = 1;
 	}
 
@@ -1106,8 +1083,6 @@
 	return;
   }
 
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_ACK_SIZE;
   buff->len = sizeof(struct tcphdr);
   buff->sk = sk;
 
@@ -1140,6 +1115,7 @@
   t1->doff = sizeof(*t1)/4;
   tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
   sk->prot->queue_xmit(sk, dev, buff, 1);
+  tcp_statistics.TcpOutSegs++;
 }
 
 
@@ -1168,7 +1144,7 @@
    * We have to loop through all the buffer headers,
    * and try to free up all the space we can.
    */
-  while((skb=skb_peek(&sk->rqueue)) != NULL ) 
+  while((skb=skb_peek(&sk->receive_queue)) != NULL) 
   {
 	if (!skb->used) 
 		break;
@@ -1330,7 +1306,7 @@
 
 		current->state = TASK_INTERRUPTIBLE;
 
-		skb = sk->rqueue;
+		skb = skb_peek(&sk->receive_queue);
 		do {
 			if (!skb)
 				break;
@@ -1343,8 +1319,8 @@
 				goto found_ok_skb;
 			if (!(flags & MSG_PEEK))
 				skb->used = 1;
-			skb = (struct sk_buff *)skb->next;
-		} while (skb != sk->rqueue);
+			skb = skb->next;
+		} while (skb != (struct sk_buff *)&sk->receive_queue);
 
 		if (copied)
 			break;
@@ -1464,8 +1440,6 @@
   sk->inuse = 1;
 
   DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff));
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_RESET_SIZE;
   buff->sk = sk;
   buff->len = sizeof(*t1);
   t1 =(struct tcphdr *) buff->data;
@@ -1506,12 +1480,13 @@
    * Can't just queue this up.
    * It should go at the end of the write queue.
    */
-  if (sk->wback != NULL) {
-  	buff->free=0;	
-	buff->next = NULL;
-	sk->wback->next = buff;
-	sk->wback = buff;
-	buff->magic = TCP_WRITE_QUEUE_MAGIC;
+  if (skb_peek(&sk->write_queue) != NULL) {
+  	buff->free=0;
+	if (buff->next != NULL) {
+		printk("tcp_shutdown: next != NULL\n");
+		skb_unlink(buff);
+	}
+	skb_queue_tail(&sk->write_queue, buff);
   } else {
         sk->sent_seq = sk->write_seq;
 	sk->prot->queue_xmit(sk, dev, buff, 0);
@@ -1569,7 +1544,8 @@
   struct sk_buff *buff;
   struct tcphdr *t1;
   int tmp;
-
+  struct device *ndev=NULL;
+  
   /*
    * We need to grab some memory, and put together an RST,
    * and then put it into the queue to be sent.
@@ -1579,8 +1555,6 @@
   	return;
 
   DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff));
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_RESET_SIZE;
   buff->len = sizeof(*t1);
   buff->sk = NULL;
   buff->dev = dev;
@@ -1588,7 +1562,7 @@
   t1 =(struct tcphdr *) buff->data;
 
   /* Put in the IP header and routing stuff. */
-  tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt,
+  tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
 			   sizeof(struct tcphdr),tos,ttl);
   if (tmp < 0) {
   	buff->free = 1;
@@ -1628,6 +1602,7 @@
   t1->doff = sizeof(*t1)/4;
   tcp_send_check(t1, saddr, daddr, sizeof(*t1), NULL);
   prot->queue_xmit(NULL, dev, buff, 1);
+  tcp_statistics.TcpOutSegs++;
 }
 
 
@@ -1712,6 +1687,7 @@
   unsigned char *ptr;
   struct sock *newsk;
   struct tcphdr *th;
+  struct device *ndev=NULL;
   int tmp;
 
   DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n"
@@ -1726,6 +1702,7 @@
   } else {
 	DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n"));
 	tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl);
+	tcp_statistics.TcpAttemptFails++;
 	kfree_skb(skb, FREE_READ);
 	return;
   }
@@ -1735,6 +1712,7 @@
    * flurry of syns from eating up all our memory.
    */
   if (sk->ack_backlog >= sk->max_ack_backlog) {
+	tcp_statistics.TcpAttemptFails++;
 	kfree_skb(skb, FREE_READ);
 	return;
   }
@@ -1749,18 +1727,18 @@
   newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
   if (newsk == NULL) {
 	/* just ignore the syn.  It will get retransmitted. */
+	tcp_statistics.TcpAttemptFails++;
 	kfree_skb(skb, FREE_READ);
 	return;
   }
 
   DPRINTF((DBG_TCP, "newsk = %X\n", newsk));
-  memcpy((void *)newsk,(void *)sk, sizeof(*newsk));
-  newsk->wback = NULL;
-  newsk->wfront = NULL;
-  newsk->rqueue = NULL;
+  memcpy(newsk, sk, sizeof(*newsk));
+  skb_queue_head_init(&newsk->write_queue);
+  skb_queue_head_init(&newsk->receive_queue);
   newsk->send_head = NULL;
   newsk->send_tail = NULL;
-  newsk->back_log = NULL;
+  skb_queue_head_init(&newsk->back_log);
   newsk->rtt = TCP_CONNECT_TIME << 3;
   newsk->rto = TCP_CONNECT_TIME;
   newsk->mdev = 0;
@@ -1846,18 +1824,17 @@
 	newsk->dead = 1;
 	release_sock(newsk);
 	kfree_skb(skb, FREE_READ);
+	tcp_statistics.TcpAttemptFails++;
 	return;
   }
   
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_SYN_SIZE;
   buff->len = sizeof(struct tcphdr)+4;
   buff->sk = newsk;
   
   t1 =(struct tcphdr *) buff->data;
 
   /* Put in the IP header and routing stuff. */
-  tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev,
+  tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev,
 			       IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
 
   /* Something went wrong. */
@@ -1869,6 +1846,7 @@
 	release_sock(newsk);
 	skb->sk = sk;
 	kfree_skb(skb, FREE_READ);
+	tcp_statistics.TcpAttemptFails++;
 	return;
   }
 
@@ -1911,9 +1889,10 @@
   sk->rmem_alloc -= skb->mem_len;
   newsk->rmem_alloc += skb->mem_len;
 
-  skb_queue_tail(&sk->rqueue,skb);
+  skb_queue_tail(&sk->receive_queue,skb);
   sk->ack_backlog++;
   release_sock(newsk);
+  tcp_statistics.TcpOutSegs++;
 }
 
 
@@ -1940,12 +1919,12 @@
   	sk->state_change(sk);
 
   /* We need to flush the recv. buffs. */
-  if (skb_peek(&sk->rqueue) != NULL) 
+  if (skb_peek(&sk->receive_queue) != NULL) 
   {
 	struct sk_buff *skb;
 	if(sk->debug)
 		printk("Clean rcv queue\n");
-	while((skb=skb_dequeue(&sk->rqueue))!=NULL)
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
 	{
 		if(skb->len > 0 && after(skb->h.th->seq + skb->len + 1 , sk->copied_seq))
 				need_reset = 1;
@@ -1954,7 +1933,6 @@
 	if(sk->debug)
 		printk("Cleaned.\n");
   }
-  sk->rqueue = NULL;
 
   /* Get rid off any half-completed packets. */
   if (sk->partial) {
@@ -2004,8 +1982,6 @@
 			reset_timer(sk, TIME_CLOSE, 100);
 			return;
 		}
-		buff->mem_addr = buff;
-		buff->mem_len = MAX_FIN_SIZE;
 		buff->sk = sk;
 		buff->free = 1;
 		buff->len = sizeof(*t1);
@@ -2024,6 +2000,7 @@
 			reset_timer(sk, TIME_CLOSE,4*sk->rto);
 			if(timeout)
 				tcp_time_wait(sk);
+
 			DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
 			release_sock(sk);
 			return;
@@ -2047,19 +2024,18 @@
 		t1->doff = sizeof(*t1)/4;
 		tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
 
-		if (sk->wfront == NULL) {
+		tcp_statistics.TcpOutSegs++;
+		
+		if (skb_peek(&sk->write_queue) == NULL) {
 			sk->sent_seq = sk->write_seq;
 			prot->queue_xmit(sk, dev, buff, 0);
 		} else {
 			reset_timer(sk, TIME_WRITE, sk->rto);
-			buff->next = NULL;
-			if (sk->wback == NULL) {
-				sk->wfront = buff;
-			} else {
-				sk->wback->next = buff;
+			if (buff->next != NULL) {
+				printk("tcp_close: next != NULL\n");
+				skb_unlink(buff);
 			}
-			sk->wback = buff;
-			buff->magic = TCP_WRITE_QUEUE_MAGIC;
+			skb_queue_tail(&sk->write_queue, buff);
 		}
 
 		if (sk->state == TCP_CLOSE_WAIT) {
@@ -2088,25 +2064,14 @@
   if(sk->zapped)
 	return;
 
-  while(sk->wfront != NULL &&
-        before(sk->wfront->h.seq, sk->window_seq +1) &&
+  while((skb = skb_peek(&sk->write_queue)) != NULL &&
+	before(skb->h.seq, sk->window_seq + 1) &&
 	(sk->retransmits == 0 ||
 	 sk->timeout != TIME_WRITE ||
-	 before(sk->wfront->h.seq, sk->rcv_ack_seq +1))
-        && sk->packets_out < sk->cong_window) {
-		skb = sk->wfront;
+	 before(skb->h.seq, sk->rcv_ack_seq + 1))
+	&& sk->packets_out < sk->cong_window) {
 		IS_SKB(skb);
-		sk->wfront = skb->next;
-		if (sk->wfront == NULL) sk->wback = NULL;
-		skb->next = NULL;
-		if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
-			printk("tcp.c skb with bad magic(%X) on write queue. Squashing "
-				"queue\n", skb->magic);
-			sk->wfront = NULL;
-			sk->wback = NULL;
-			return;
-		}
-		skb->magic = 0;
+		skb_unlink(skb);
 		DPRINTF((DBG_TCP, "Sending a packet.\n"));
 
 		/* See if we really need to send the packet. */
@@ -2133,13 +2098,13 @@
   struct sk_buff *skb,*skb2,*skb3;
 
   for (skb = sk->send_head; skb != NULL; skb = skb2) {
-	skb2 = (struct sk_buff *)skb->link3;
+	skb2 = skb->link3;
 	if (list == NULL || before (skb2->h.seq, list->h.seq)) {
 		skb->link3 = list;
 		sk->send_tail = skb;
 		list = skb;
 	} else {
-		for (skb3 = list; ; skb3 = (struct sk_buff *)skb3->link3) {
+		for (skb3 = list; ; skb3 = skb3->link3) {
 			if (skb3->link3 == NULL ||
 			    before(skb->h.seq, skb3->link3->h.seq)) {
 				skb->link3 = skb3->link3;
@@ -2220,7 +2185,7 @@
 	cli();
 	while (skb2 != NULL) {
 		skb = skb2;
-		skb2 = (struct sk_buff *)skb->link3;
+		skb2 = skb->link3;
 		skb->link3 = NULL;
 		if (after(skb->h.seq, sk->window_seq)) {
 			if (sk->packets_out > 0) sk->packets_out--;
@@ -2229,15 +2194,10 @@
 				skb_unlink(skb);				
 			}
 			/* Now add it to the write_queue. */
-			skb->magic = TCP_WRITE_QUEUE_MAGIC;
-			if (wskb == NULL) {
-				skb->next = sk->wfront;
-				sk->wfront = skb;
-			} else {
-				skb->next = wskb->next;
-				wskb->next = skb;
-			}
-			if (sk->wback == wskb) sk->wback = skb;
+			if (wskb == NULL)
+				skb_queue_head(&sk->write_queue,skb);
+			else
+				skb_append(wskb,skb);
 			wskb = skb;
 		} else {
 			if (sk->send_head == NULL) {
@@ -2297,8 +2257,8 @@
    * it needs to be for normal retransmission
    */
   if (sk->timeout == TIME_PROBE0) {
-  	if (sk->wfront != NULL &&   /* should always be non-null */
-	    ! before (sk->window_seq, sk->wfront->h.seq)) {
+  	if (skb_peek(&sk->write_queue) != NULL &&   /* should always be non-null */
+	    ! before (sk->window_seq, sk->write_queue.next->h.seq)) {
 	  sk->retransmits = 0;
 	  sk->backoff = 0;
 	  /* recompute rto from rtt.  this eliminates any backoff */
@@ -2397,15 +2357,15 @@
 
 		oskb = sk->send_head;
 		IS_SKB(oskb);
-		sk->send_head =(struct sk_buff *)oskb->link3;
+		sk->send_head = oskb->link3;
 		if (sk->send_head == NULL) {
 			sk->send_tail = NULL;
 		}
 
-		/* We may need to remove this from the dev send list. */		
-		skb_unlink(oskb);	/* Much easier! */
+		/* We may need to remove this from the dev send list. */
+		if (oskb->next)
+			skb_unlink(oskb);
 		sti();
-		oskb->magic = 0;
 		kfree_skb(oskb, FREE_WRITE); /* write. */
 		if (!sk->dead) sk->write_space(sk);
 	} else {
@@ -2417,15 +2377,15 @@
    * Maybe we can take some stuff off of the write queue,
    * and put it onto the xmit queue.
    */
-  if (sk->wfront != NULL) {
-	if (after (sk->window_seq+1, sk->wfront->h.seq) &&
+  if (skb_peek(&sk->write_queue) != NULL) {
+	if (after (sk->window_seq+1, sk->write_queue.next->h.seq) &&
 	        (sk->retransmits == 0 || 
 		 sk->timeout != TIME_WRITE ||
-		 before(sk->wfront->h.seq, sk->rcv_ack_seq +1))
+		 before(sk->write_queue.next->h.seq, sk->rcv_ack_seq + 1))
 		&& sk->packets_out < sk->cong_window) {
 		flag |= 1;
 		tcp_write_xmit(sk);
- 	} else if (before(sk->window_seq, sk->wfront->h.seq) &&
+ 	} else if (before(sk->window_seq, sk->write_queue.next->h.seq) &&
  		   sk->send_head == NULL &&
  		   sk->ack_backlog == 0 &&
  		   sk->state != TCP_TIME_WAIT) {
@@ -2452,7 +2412,7 @@
   }
 
   if (sk->packets_out == 0 && sk->partial != NULL &&
-      sk->wfront == NULL && sk->send_head == NULL) {
+      skb_peek(&sk->write_queue) == NULL && sk->send_head == NULL) {
 	flag |= 1;
 	tcp_send_partial(sk);
   }
@@ -2555,7 +2515,8 @@
   if (sk->shutdown & RCV_SHUTDOWN) {
 	sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
 	tcp_reset(sk->saddr, sk->daddr, skb->h.th,
-	sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
+		sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
+	tcp_statistics.TcpEstabResets++;
 	sk->state = TCP_CLOSE;
 	sk->err = EPIPE;
 	sk->shutdown = SHUTDOWN_MASK;
@@ -2574,20 +2535,13 @@
    */
 
   /* This should start at the last one, and then go around forwards. */
-  if (sk->rqueue == NULL) {
+  if (skb_peek(&sk->receive_queue) == NULL) {
 	DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb));
-#ifdef OLDWAY
-	sk->rqueue = skb;
-	skb->next = skb;
-	skb->prev = skb;
-	skb->list = &sk->rqueue;
-#else
-	skb_queue_head(&sk->rqueue,skb);
-#endif		
+	skb_queue_head(&sk->receive_queue,skb);
 	skb1= NULL;
   } else {
 	DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk));
-	for(skb1=sk->rqueue->prev; ; skb1 =(struct sk_buff *)skb1->prev) {
+	for(skb1=sk->receive_queue.prev; ; skb1 = skb1->prev) {
 		if(sk->debug)
 		{
 			printk("skb1=%p :", skb1);
@@ -2596,25 +2550,6 @@
 			printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq,
 					sk->acked_seq);
 		}
-#ifdef OLD		
-		if (after(th->seq+1, skb1->h.th->seq)) {
-			skb->prev = skb1;
-			skb->next = skb1->next;
-			skb->next->prev = skb;
-			skb1->next = skb;
-			if (skb1 == sk->rqueue) sk->rqueue = skb;
-			break;
-		}
-		if (skb1->prev == sk->rqueue) {
-			skb->next= skb1;
-			skb->prev = skb1->prev;
-			skb->prev->next = skb;
-			skb1->prev = skb;
-			skb1 = NULL; /* so we know we might be able
-					to ack stuff. */
-			break;
-		}
-#else
 		if (th->seq==skb1->h.th->seq && skb->len>= skb1->len)
 		{
 			skb_append(skb1,skb);
@@ -2629,12 +2564,11 @@
 			skb_append(skb1,skb);
 			break;
 		}
-		if (skb1 == sk->rqueue)
+		if (skb1 == skb_peek(&sk->receive_queue))
 		{
-			skb_queue_head(&sk->rqueue, skb);		
+			skb_queue_head(&sk->receive_queue, skb);
 			break;
 		}
-#endif		
 	}
 	DPRINTF((DBG_TCP, "skb = %X:\n", skb));
   }
@@ -2669,9 +2603,9 @@
 			sk->shutdown |= RCV_SHUTDOWN;
 		}
 	  
-		for(skb2 = (struct sk_buff *)skb->next;
-		    skb2 !=(struct sk_buff *) sk->rqueue;
-		    skb2 = (struct sk_buff *)skb2->next) {
+		for(skb2 = skb->next;
+		    skb2 != (struct sk_buff *)&sk->receive_queue;
+		    skb2 = skb2->next) {
 			if (before(skb2->h.th->seq, sk->acked_seq+1)) {
 				if (after(skb2->h.th->ack_seq, sk->acked_seq))
 				{
@@ -2729,7 +2663,7 @@
 	 * for the send side.  He could be sending us stuff as large as mtu.
 	 */
 	while (sk->prot->rspace(sk) < sk->mtu) {
-		skb1 = skb_peek(&sk->rqueue);
+		skb1 = skb_peek(&sk->receive_queue);
 		if (skb1 == NULL) {
 			printk("INET: tcp.c:tcp_data memory leak detected.\n");
 			break;
@@ -2741,15 +2675,6 @@
 		}
 		
 		skb_unlink(skb1);
-#ifdef OLDWAY		
-		if (skb1->prev == skb1) {
-			sk->rqueue = NULL;
-		} else {
-			sk->rqueue = (struct sk_buff *)skb1->prev;
-			skb1->next->prev = skb1->prev;
-			skb1->prev->next = skb1->next;
-		}
-#endif		
 		kfree_skb(skb1, FREE_READ);
 	}
 	tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
@@ -2832,7 +2757,7 @@
 	/* ok, got the correct packet, update info */
 	sk->urg_data = URG_VALID | *(ptr + (unsigned char *) th);
 	if (!sk->dead)
-		wake_up_interruptible(sk->sleep);
+		sk->data_ready(sk,0);
 	return 0;
 }
 
@@ -2856,6 +2781,7 @@
 		/* Contains the one that needs to be acked */
 		reset_timer(sk, TIME_CLOSE, TCP_TIMEOUT_LEN);
 		sk->fin_seq = th->seq+1;
+		tcp_statistics.TcpCurrEstab--;
 		sk->state = TCP_CLOSE_WAIT;
 		if (th->rst) sk->shutdown = SHUTDOWN_MASK;
 		break;
@@ -2906,7 +2832,7 @@
   /* avoid the race. */
   cli();
   sk->inuse = 1;
-  while((skb = get_firstr(sk)) == NULL) {
+  while((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
 	if (flags & O_NONBLOCK) {
 		sti();
 		release_sock(sk);
@@ -2961,7 +2887,7 @@
   DPRINTF((DBG_TCP, "TCP connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr)));
   
   /* Don't want a TCP connection going to a broadcast address */
-  if (chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) { 
+  if (ip_chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) { 
 	DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n"));
 	return(-ENETUNREACH);
   }
@@ -2984,8 +2910,6 @@
 	return(-ENOMEM);
   }
   sk->inuse = 1;
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_SYN_SIZE;
   buff->len = 24;
   buff->sk = sk;
   buff->free = 1;
@@ -3050,6 +2974,8 @@
   sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
 
   sk->prot->queue_xmit(sk, dev, buff, 0);  
+  tcp_statistics.TcpActiveOpens++;
+  tcp_statistics.TcpOutSegs++;
   
   release_sock(sk);
   return(0);
@@ -3091,6 +3017,9 @@
 ignore_it:
 	DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n"));
 
+	if (th->rst)
+		return 0;
+
 	/*
 	 *	Send a reset if we get something not ours and we are
 	 *	unsynchronized. Note: We don't do anything to our end. We
@@ -3103,9 +3032,6 @@
 		return 1;
 	}
 
-	if (th->rst)
-		return 0;
-
 	/* Try to resync things. */
 	tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
 	return 0;
@@ -3124,20 +3050,15 @@
 	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n"));
 	return(0);
   }
-#if 0	/* FIXME: it's ok for protocol to be NULL */
-  if (!protocol) {
-	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv protocol = NULL\n"));
-	return(0);
-  }
 
-  if (!opt) {	/* FIXME: it's ok for opt to be NULL */
-	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv opt = NULL\n"));
-  }
-#endif
-  if (!dev) {
+  if (!dev) 
+  {
 	DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n"));
 	return(0);
   }
+  
+  tcp_statistics.TcpInSegs++;
+  
   th = skb->h.th;
 
   /* Find the socket. */
@@ -3189,16 +3110,7 @@
 	/* We may need to add it to the backlog here. */
 	cli();
 	if (sk->inuse) {
-		if (sk->back_log == NULL) {
-			sk->back_log = skb;
-			skb->next = skb;
-			skb->prev = skb;
-		} else {
-			skb->next = sk->back_log;
-			skb->prev = sk->back_log->prev;
-			skb->prev->next = skb;
-			skb->next->prev = skb;
-		}
+		skb_queue_head(&sk->back_log, skb);
 		sti();
 		return(0);
 	}
@@ -3254,24 +3166,23 @@
 	case TCP_FIN_WAIT2:
 	case TCP_TIME_WAIT:
 		if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
-if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
-#ifdef undef
-/* nice idea, but tcp_sequence already does this.  Maybe it shouldn't?? */
-			if(!th->rst)
-				tcp_send_ack(sk->sent_seq, sk->acked_seq, 
-				     sk, th, saddr);
-#endif
+			if (inet_debug == DBG_SLIP) 
+				printk("\rtcp_rcv: not in seq\n");
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
 		}
 
-		if (th->rst) {
+		if (th->rst) 
+		{
+			tcp_statistics.TcpEstabResets++;
+			tcp_statistics.TcpCurrEstab--;
 			sk->zapped=1;
 			/* This means the thing should really be closed. */
 			sk->err = ECONNRESET;
 
-			if (sk->state == TCP_CLOSE_WAIT) {
+			if (sk->state == TCP_CLOSE_WAIT) 
+			{
 				sk->err = EPIPE;
 			}
 
@@ -3281,19 +3192,18 @@
 			 */
 			sk->state = TCP_CLOSE;
 			sk->shutdown = SHUTDOWN_MASK;
-			if (!sk->dead) {
+			if (!sk->dead) 
+			{
 				sk->state_change(sk);
 			}
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
 		}
-		if (
-#if 0
-		if ((opt && (opt->security != 0 ||
-			    opt->compartment != 0)) || 
-#endif
-				 th->syn) {
+		if (th->syn) 
+		{
+			tcp_statistics.TcpCurrEstab--;
+			tcp_statistics.TcpEstabResets++;
 			sk->err = ECONNRESET;
 			sk->state = TCP_CLOSE;
 			sk->shutdown = SHUTDOWN_MASK;
@@ -3364,15 +3274,8 @@
 			return(0);
 		}
 
-		if (th->syn) {
-#if 0
-			if (opt->security != 0 || opt->compartment != 0) {
-				tcp_reset(daddr, saddr, th, prot, opt,dev);
-				release_sock(sk);
-				return(0);
-			}
-#endif
-
+		if (th->syn) 
+		{
 			/*
 			 * Now we just put the whole thing including
 			 * the header and saddr, and protocol pointer
@@ -3398,41 +3301,33 @@
 
 
 	default:
-		if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
+		if (!tcp_sequence(sk, th, len, opt, saddr,dev)) 
+		{
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
 		}
 
 	case TCP_SYN_SENT:
-		if (th->rst) {
+		if (th->rst) 
+		{
+			tcp_statistics.TcpAttemptFails++;
 			sk->err = ECONNREFUSED;
 			sk->state = TCP_CLOSE;
 			sk->shutdown = SHUTDOWN_MASK;
 			sk->zapped = 1;
-			if (!sk->dead) {
+			if (!sk->dead) 
+			{
 				sk->state_change(sk);
 			}
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
 		}
-#if 0
-		if (opt->security != 0 || opt->compartment != 0) {
-			sk->err = ECONNRESET;
-			sk->state = TCP_CLOSE;
-			sk->shutdown = SHUTDOWN_MASK;
-			tcp_reset(daddr, saddr,  th, sk->prot, opt, dev);
-			if (!sk->dead) {
-				wake_up_interruptible(sk->sleep);
-			}
-			kfree_skb(skb, FREE_READ);
-			release_sock(sk);
-			return(0);
-		}
-#endif
-		if (!th->ack) {
-			if (th->syn) {
+		if (!th->ack) 
+		{
+			if (th->syn) 
+			{
 				sk->state = TCP_SYN_RECV;
 			}
 
@@ -3441,9 +3336,12 @@
 			return(0);
 		}
 
-		switch(sk->state) {
+		switch(sk->state) 
+		{
 			case TCP_SYN_SENT:
-				if (!tcp_ack(sk, th, saddr, len)) {
+				if (!tcp_ack(sk, th, saddr, len)) 
+				{
+					tcp_statistics.TcpAttemptFails++;
 					tcp_reset(daddr, saddr, th,
 							sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
 					kfree_skb(skb, FREE_READ);
@@ -3455,7 +3353,8 @@
 				 * If the syn bit is also set, switch to
 				 * tcp_syn_recv, and then to established.
 				 */
-				if (!th->syn) {
+				if (!th->syn) 
+				{
 					kfree_skb(skb, FREE_READ);
 					release_sock(sk);
 					return(0);
@@ -3468,13 +3367,17 @@
 							sk, th, sk->daddr);
 	
 			case TCP_SYN_RECV:
-				if (!tcp_ack(sk, th, saddr, len)) {
+				if (!tcp_ack(sk, th, saddr, len)) 
+				{
+					tcp_statistics.TcpAttemptFails++;
 					tcp_reset(daddr, saddr, th,
 							sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl);
 					kfree_skb(skb, FREE_READ);
 					release_sock(sk);
 					return(0);
 				}
+
+				tcp_statistics.TcpCurrEstab++;
 				sk->state = TCP_ESTABLISHED;
 
 				/*
@@ -3569,8 +3472,6 @@
   buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
   if (buff == NULL) return;
 
-  buff->mem_addr = buff;
-  buff->mem_len = MAX_ACK_SIZE;
   buff->len = sizeof(struct tcphdr);
   buff->free = 1;
   buff->sk = sk;
@@ -3612,6 +3513,7 @@
    * This will prevent the timer from automatically being restarted.
   */
   sk->prot->queue_xmit(sk, dev, buff, 1);
+  tcp_statistics.TcpOutSegs++;
 }
 
 void
@@ -3629,10 +3531,10 @@
 	sk->prot->retransmits ++;
 }
 
-
 /*
  *	Socket option code for TCP. 
- */  
+ */
+  
 int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 {
 	int val,err;
diff -u --recursive --new-file v1.1.3/linux/net/inet/tcp.h linux/net/inet/tcp.h
--- v1.1.3/linux/net/inet/tcp.h	Thu Apr  7 11:57:33 1994
+++ linux/net/inet/tcp.h	Wed Apr 13 19:34:08 1994
@@ -20,10 +20,10 @@
 
 #include <linux/tcp.h>
 
-#define MAX_SYN_SIZE	44 + sizeof (struct sk_buff) + MAX_HEADER
-#define MAX_FIN_SIZE	40 + sizeof (struct sk_buff) + MAX_HEADER
-#define MAX_ACK_SIZE	40 + sizeof (struct sk_buff) + MAX_HEADER
-#define MAX_RESET_SIZE	40 + sizeof (struct sk_buff) + MAX_HEADER
+#define MAX_SYN_SIZE	44 + MAX_HEADER
+#define MAX_FIN_SIZE	40 + MAX_HEADER
+#define MAX_ACK_SIZE	40 + MAX_HEADER
+#define MAX_RESET_SIZE	40 + MAX_HEADER
 #define MAX_WINDOW	8192
 #define MIN_WINDOW	2048
 #define MAX_ACK_BACKLOG	2
@@ -64,7 +64,6 @@
 #define TCP_NO_CHECK	0	/* turn to one if you want the default
 				 * to be no checksum			*/
 
-#define TCP_WRITE_QUEUE_MAGIC 0xa5f23477
 
 /*
  *	TCP option
diff -u --recursive --new-file v1.1.3/linux/net/inet/timer.c linux/net/inet/timer.c
--- v1.1.3/linux/net/inet/timer.c	Tue Feb  1 09:27:53 1994
+++ linux/net/inet/timer.c	Wed Apr 13 12:52:29 1994
@@ -40,12 +40,12 @@
 #include <linux/timer.h>
 #include <asm/system.h>
 #include <linux/interrupt.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "arp.h"
 
@@ -68,8 +68,7 @@
 {
   delete_timer (t);
 
-  if (timeout != -1)
-    t->timeout = timeout;
+  t->timeout = timeout;
 
 #if 1
   /* FIXME: ??? */
@@ -93,16 +92,19 @@
   int why = sk->timeout;
   /* timeout is overwritten by 'delete_timer' and 'reset_timer' */
 
-  if (sk->inuse || in_inet_bh()) {
+  cli();
+  if (sk->inuse || in_bh) {
     sk->timer.expires = 10;
     add_timer(&sk->timer);
+    sti();
     return;
   }
   sk->inuse = 1;
+  sti();
 
   DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why));
-  if (sk->wfront && 
-      before(sk->window_seq, sk->wfront->h.seq) &&
+  if (skb_peek(&sk->write_queue) && 
+      before(sk->window_seq, sk->write_queue.next->h.seq) &&
       sk->send_head == NULL &&
       sk->ack_backlog == 0 &&
       sk->state != TCP_TIME_WAIT)
@@ -114,7 +116,7 @@
   if (sk->ack_backlog) {
     sk->prot->read_wakeup (sk);
     if (! sk->dead)
-      wake_up_interruptible (sk->sleep);
+    	sk->data_ready(sk,0);
   }
 
   /* Now we need to figure out why the socket was on the timer. */
@@ -147,9 +149,9 @@
 	sk->state = TCP_CLOSE;
 	delete_timer (sk);
 	/* Kill the ARP entry in case the hardware has changed. */
-	arp_destroy_maybe (sk->daddr);
+	arp_destroy (sk->daddr, 0);
 	if (!sk->dead)
-	  wake_up_interruptible (sk->sleep);
+	  sk->state_change(sk);
 	sk->shutdown = SHUTDOWN_MASK;
 	reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME);
 	release_sock (sk);
@@ -162,13 +164,23 @@
 	/* It could be we got here because we needed to send an ack.
 	 * So we need to check for that.
 	 */
-	if (sk->send_head) {
-	  if (jiffies < (sk->send_head->when + sk->rto)) {
-	    reset_timer (sk, TIME_WRITE, 
-			 (sk->send_head->when + sk->rto - jiffies));
+	{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	skb = sk->send_head;
+	if (!skb) {
+	  restore_flags(flags);
+	} else {
+	  if (jiffies < skb->when + sk->rto) {
+	    reset_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
+	    restore_flags(flags);
 	    release_sock (sk);
 	    break;
 	  }
+	  restore_flags(flags);
 	  /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
 	     sk->retransmits, sk->packets_out, sk->cong_window); */
 	  DPRINTF ((DBG_TMR, "retransmitting.\n"));
@@ -176,7 +188,7 @@
 	  if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
 	    || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
 	    DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n"));
-	    arp_destroy_maybe (sk->daddr);
+	    arp_destroy (sk->daddr, 0);
 	    ip_route_check (sk->daddr);
 	  }
 	  if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
@@ -194,6 +206,7 @@
 	}
 	release_sock (sk);
 	break;
+	}
     case TIME_KEEPOPEN:
 	/* Send something to keep the connection open. */
 	if (sk->prot->write_wakeup)
@@ -207,19 +220,19 @@
 	if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
 	  || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
 	  DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n"));
-	  arp_destroy_maybe (sk->daddr);
+	  arp_destroy (sk->daddr, 0);
 	  ip_route_check (sk->daddr);
 	  release_sock (sk);
 	  break;
 	}
 	if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
 	  DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n"));
-	  arp_destroy_maybe (sk->daddr);
+	  arp_destroy (sk->daddr, 0);
 	  sk->err = ETIMEDOUT;
 	  if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) {
 	    sk->state = TCP_TIME_WAIT;
 	    if (!sk->dead)
-	      wake_up_interruptible (sk->sleep);
+	      sk->state_change(sk);
 	    release_sock (sk);
 	  } else {
 	    sk->prot->close (sk, 1);
@@ -229,7 +242,7 @@
 	release_sock (sk);
 	break;
     default:
-	printk ("net timer expired - reason unknown, sk=%08X\n", (int)sk);
+	printk ("net_timer: timer expired - reason unknown\n");
 	release_sock (sk);
 	break;
   }
diff -u --recursive --new-file v1.1.3/linux/net/inet/udp.c linux/net/inet/udp.c
--- v1.1.3/linux/net/inet/udp.c	Tue Apr  5 10:16:24 1994
+++ linux/net/inet/udp.c	Wed Apr 13 12:52:30 1994
@@ -34,6 +34,7 @@
  *		Alan Cox	:	Broadcasting without option set returns EACCES.
  *		Alan Cox	:	No wakeup calls. Instead we now use the callbacks.
  *		Alan Cox	:	Use ip_tos and ip_ttl
+ *		Alan Cox	:	SNMP Mibs
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -54,31 +55,40 @@
 #include <linux/timer.h>
 #include <linux/termios.h>
 #include <linux/mm.h>
-#include "inet.h"
-#include "dev.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "snmp.h"
 #include "ip.h"
 #include "protocol.h"
 #include "tcp.h"
-#include "skbuff.h"
+#include <linux/skbuff.h>
 #include "sock.h"
 #include "udp.h"
 #include "icmp.h"
 
+/*
+ *	SNMP MIB for the UDP layer
+ */
 
-#define min(a,b)	((a)<(b)?(a):(b))
+struct udp_mib		udp_statistics;
 
 
-static void
-print_udp(struct udphdr *uh)
+
+
+#define min(a,b)	((a)<(b)?(a):(b))
+
+static void print_udp(struct udphdr *uh)
 {
-  if (inet_debug != DBG_UDP) return;
+	if (inet_debug != DBG_UDP) 
+		return;
 
-  if (uh == NULL) {
-	printk("(NULL)\n");
-	return;
-  }
-  printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));
-  printk("     len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));
+	if (uh == NULL) 
+	{
+		printk("(NULL)\n");
+		return;
+	}
+	printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));
+	printk("     len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));
 }
 
 
@@ -92,261 +102,332 @@
  * header points to the first 8 bytes of the udp header.  We need
  * to find the appropriate port.
  */
-void
-udp_err(int err, unsigned char *header, unsigned long daddr,
+
+void udp_err(int err, unsigned char *header, unsigned long daddr,
 	unsigned long saddr, struct inet_protocol *protocol)
 {
-  struct udphdr *th;
-  struct sock *sk;
-  struct iphdr *ip=(struct iphdr *)header;
+	struct udphdr *th;
+	struct sock *sk;
+	struct iphdr *ip=(struct iphdr *)header;
   
-  header += 4*ip->ihl;
-  
-  th = (struct udphdr *)header;  
+	header += 4*ip->ihl;
+
+	/*
+	 *	Find the 8 bytes of post IP header ICMP included for usA
+	 */  
+	th = (struct udphdr *)header;  
    
-  DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\
-sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest));
+	DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\
+		sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest));
 
-  sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr);
+	sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr);
 
-  if (sk == NULL) 
-  	return;	/* No socket for error */
+	if (sk == NULL) 
+	  	return;	/* No socket for error */
   	
-  if (err < 0)		/* As per the calling spec */
-  {
-  	sk->err = -err;
-  	sk->error_report(sk);		/* User process wakes to see error */
-  	return;
-  }
-  
-  if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) {	/* Slow down! */
-	if (sk->cong_window > 1) 
-		sk->cong_window = sk->cong_window/2;
-	return;
-  }
+	if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) 
+	{	/* Slow down! */
+		if (sk->cong_window > 1) 
+			sk->cong_window = sk->cong_window/2;
+		return;
+	}
 
-  sk->err = icmp_err_convert[err & 0xff].errno;
+	sk->err = icmp_err_convert[err & 0xff].errno;
 
-  /* It's only fatal if we have connected to them. */
-  if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) {
-	sk->err=ECONNREFUSED;
-  }
-  sk->error_report(sk);
+	/*
+	 *	It's only fatal if we have connected to them. I'm not happy
+	 *	with this code. Some BSD comparisons need doing.
+	 */
+	 
+	if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) 
+	{
+		sk->err=ECONNREFUSED;
+ 	}
+	
+	sk->error_report(sk);
 }
 
 
-static unsigned short
-udp_check(struct udphdr *uh, int len,
-	  unsigned long saddr, unsigned long daddr)
+static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr)
 {
-  unsigned long sum;
+	unsigned long sum;
 
-  DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",
+	DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",
 	   						uh, len, saddr, daddr));
 
-  print_udp(uh);
+	print_udp(uh);
+
+	__asm__(  "\t addl %%ecx,%%ebx\n"
+		  "\t adcl %%edx,%%ebx\n"
+		  "\t adcl $0, %%ebx\n"
+		  : "=b"(sum)
+		  : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256)
+		  : "cx","bx","dx" );
+
+	if (len > 3) 
+	{
+		__asm__("\tclc\n"
+			"1:\n"
+			"\t lodsl\n"
+			"\t adcl %%eax, %%ebx\n"
+			"\t loop 1b\n"
+			"\t adcl $0, %%ebx\n"
+			: "=b"(sum) , "=S"(uh)
+			: "0"(sum), "c"(len/4) ,"1"(uh)
+			: "ax", "cx", "bx", "si" );
+	}
+
+	/*
+	 *	Convert from 32 bits to 16 bits. 
+	 */
 
-  __asm__("\t addl %%ecx,%%ebx\n"
-	  "\t adcl %%edx,%%ebx\n"
-	  "\t adcl $0, %%ebx\n"
-	  : "=b"(sum)
-	  : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256)
-	  : "cx","bx","dx" );
-
-  if (len > 3) {
-	__asm__("\tclc\n"
-		"1:\n"
-		"\t lodsl\n"
-		"\t adcl %%eax, %%ebx\n"
-		"\t loop 1b\n"
-		"\t adcl $0, %%ebx\n"
-		: "=b"(sum) , "=S"(uh)
-		: "0"(sum), "c"(len/4) ,"1"(uh)
-		: "ax", "cx", "bx", "si" );
-  }
-
-  /* Convert from 32 bits to 16 bits. */
-  __asm__("\t movl %%ebx, %%ecx\n"
-	  "\t shrl $16,%%ecx\n"
-	  "\t addw %%cx, %%bx\n"
-	  "\t adcw $0, %%bx\n"
-	  : "=b"(sum)
-	  : "0"(sum)
-	  : "bx", "cx");
-
-  /* Check for an extra word. */
-  if ((len & 2) != 0) {
-	__asm__("\t lodsw\n"
-		"\t addw %%ax,%%bx\n"
-		"\t adcw $0, %%bx\n"
-		: "=b"(sum), "=S"(uh)
-		: "0"(sum) ,"1"(uh)
-		: "si", "ax", "bx");
-  }
-
-  /* Now check for the extra byte. */
-  if ((len & 1) != 0) {
-	__asm__("\t lodsb\n"
-		"\t movb $0,%%ah\n"
-		"\t addw %%ax,%%bx\n"
-		"\t adcw $0, %%bx\n"
-		: "=b"(sum)
-		: "0"(sum) ,"S"(uh)
-		: "si", "ax", "bx");
-  }
+	__asm__("\t movl %%ebx, %%ecx\n"
+		"\t shrl $16,%%ecx\n"
+	  	"\t addw %%cx, %%bx\n"
+	  	"\t adcw $0, %%bx\n"
+	  	: "=b"(sum)
+	  	: "0"(sum)
+	  	: "bx", "cx");
+	
+	/* 
+	 *	Check for an extra word. 
+	 */
+	 
+	if ((len & 2) != 0) 
+	{
+		__asm__("\t lodsw\n"
+			"\t addw %%ax,%%bx\n"
+			"\t adcw $0, %%bx\n"
+			: "=b"(sum), "=S"(uh)
+			: "0"(sum) ,"1"(uh)
+			: "si", "ax", "bx");
+  	}
+
+  	/*
+  	 *	Now check for the extra byte. 
+  	 */
+  	 
+	if ((len & 1) != 0) 
+	{
+		__asm__("\t lodsb\n"
+			"\t movb $0,%%ah\n"
+			"\t addw %%ax,%%bx\n"
+			"\t adcw $0, %%bx\n"
+			: "=b"(sum)
+			: "0"(sum) ,"S"(uh)
+			: "si", "ax", "bx");
+  	}
+
+  	/* 
+  	 *	We only want the bottom 16 bits, but we never cleared the top 16. 
+  	 */
 
-  /* We only want the bottom 16 bits, but we never cleared the top 16. */
-  return((~sum) & 0xffff);
+	return((~sum) & 0xffff);
 }
 
+/*
+ *	Generate UDP checksums. These may be disabled, eg for fast NFS over ethernet
+ *	We default them enabled.. if you turn them off you either know what you are
+ *	doing or get burned...
+ */
 
-static void
-udp_send_check(struct udphdr *uh, unsigned long saddr, 
+static void udp_send_check(struct udphdr *uh, unsigned long saddr, 
 	       unsigned long daddr, int len, struct sock *sk)
 {
-  uh->check = 0;
-  if (sk && sk->no_check) 
-  	return;
-  uh->check = udp_check(uh, len, saddr, daddr);
-  if (uh->check == 0) uh->check = 0xffff;
+	uh->check = 0;
+	if (sk && sk->no_check) 
+	  	return;
+	uh->check = udp_check(uh, len, saddr, daddr);
+	
+	/*
+	 *	FFFF and 0 are the same, pick the right one as 0 in the
+	 *	actual field means no checksum.
+	 */
+	 
+	if (uh->check == 0)
+		uh->check = 0xffff;
 }
 
 
-static int
-udp_send(struct sock *sk, struct sockaddr_in *sin,
+static int udp_send(struct sock *sk, struct sockaddr_in *sin,
 	 unsigned char *from, int len)
 {
-  struct sk_buff *skb;
-  struct device *dev;
-  struct udphdr *uh;
-  unsigned char *buff;
-  unsigned long saddr;
-  int size, tmp;
-  int err;
+	struct sk_buff *skb;
+	struct device *dev;
+	struct udphdr *uh;
+	unsigned char *buff;
+	unsigned long saddr;
+	int size, tmp;
+	int err;
   
-  DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
+	DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
 		in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port),
 		from, len));
 
-  err=verify_area(VERIFY_READ, from, len);
-  if(err)
-  	return(err);
-
-  /* Allocate a copy of the packet. */
-  size = sizeof(struct sk_buff) + sk->prot->max_header + len;
-  skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
-  if (skb == NULL) return(-ENOMEM);
-
-  skb->mem_addr = skb;
-  skb->mem_len  = size;
-  skb->sk       = NULL;	/* to avoid changing sk->saddr */
-  skb->free     = 1;
-  skb->arp      = 0;
-
-  /* Now build the IP and MAC header. */
-  buff = skb->data;
-  saddr = 0;
-  dev = NULL;
-  DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
-			saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
-  tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
-			       &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
-  skb->sk=sk;	/* So memory is freed correctly */
-			    
-  if (tmp < 0 ) {
-	sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
-	return(tmp);
-  }
-  buff += tmp;
-  saddr = dev->pa_addr;
-  DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
+	err=verify_area(VERIFY_READ, from, len);
+	if(err)
+	  	return(err);
 
-  skb->len = tmp + sizeof(struct udphdr) + len;	/* len + UDP + IP + MAC */
-  skb->dev = dev;
+	/* 
+	 *	Allocate an sk_buff copy of the packet.
+	 */
+	 
+	size = sk->prot->max_header + len;
+	skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
+
 
-  /* Fill in the UDP header. */
-  uh = (struct udphdr *) buff;
-  uh->len = htons(len + sizeof(struct udphdr));
-  uh->source = sk->dummy_th.source;
-  uh->dest = sin->sin_port;
-  buff = (unsigned char *) (uh + 1);
+	if (skb == NULL) 
+		return(-ENOMEM);
 
-  /* Copy the user data. */
-  memcpy_fromfs(buff, from, len);
+	skb->sk       = NULL;	/* to avoid changing sk->saddr */
+	skb->free     = 1;
+
+	/*
+	 *	Now build the IP and MAC header. 
+	 */
+	 
+	buff = skb->data;
+	saddr = 0;
+	dev = NULL;
+	DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
+			saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
+	tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
+			&dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
+	skb->sk=sk;	/* So memory is freed correctly */
+	
+	/*
+	 *	Unable to put a header on the packet.
+	 */
+	 		    
+	if (tmp < 0 ) 
+	{
+		sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
+		return(tmp);
+  	}
+  	
+	buff += tmp;
+	saddr = skb->saddr; /*dev->pa_addr;*/
+	DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
+
+	skb->len = tmp + sizeof(struct udphdr) + len;	/* len + UDP + IP + MAC */
+	skb->dev = dev;
+	
+	/*
+	 *	Fill in the UDP header. 
+	 */
+	 
+	uh = (struct udphdr *) buff;
+	uh->len = htons(len + sizeof(struct udphdr));
+	uh->source = sk->dummy_th.source;
+	uh->dest = sin->sin_port;
+	buff = (unsigned char *) (uh + 1);
 
-  /* Set up the UDP checksum. */
-  udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
+	/*
+	 *	Copy the user data. 
+	 */
+	 
+	memcpy_fromfs(buff, from, len);
 
-  /* Send the datagram to the interface. */
-  sk->prot->queue_xmit(sk, dev, skb, 1);
+  	/*
+  	 *	Set up the UDP checksum. 
+  	 */
+  	 
+	udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
 
-  return(len);
+	/* 
+	 *	Send the datagram to the interface. 
+	 */
+	 
+	udp_statistics.UdpOutDatagrams++;
+	 
+	sk->prot->queue_xmit(sk, dev, skb, 1);
+	return(len);
 }
 
 
-static int
-udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
+static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
 	   unsigned flags, struct sockaddr_in *usin, int addr_len)
 {
-  struct sockaddr_in sin;
-  int tmp;
-  int err;
+	struct sockaddr_in sin;
+	int tmp;
+	int err;
 
-  DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
+	DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
 
-  /* Check the flags. */
-  if (flags) 
-  	return(-EINVAL);
-  if (len < 0) 
-  	return(-EINVAL);
-  if (len == 0) 
-  	return(0);
+	/* 
+	 *	Check the flags. We support no flags for UDP sending
+	 */
+	if (flags) 
+	  	return(-EINVAL);
+	if (len < 0) 
+ 	 	return(-EINVAL);
+ 	if (len == 0) 
+  		return(0);
 
-  /* Get and verify the address. */
-  if (usin) {
-	if (addr_len < sizeof(sin)) return(-EINVAL);
-	err=verify_area(VERIFY_READ, usin, sizeof(sin));
-	if(err)
-		return err;
-	memcpy_fromfs(&sin, usin, sizeof(sin));
-	if (sin.sin_family && sin.sin_family != AF_INET) 
-		return(-EINVAL);
-	if (sin.sin_port == 0) 
-		return(-EINVAL);
-  } else {
-	if (sk->state != TCP_ESTABLISHED) return(-EINVAL);
-	sin.sin_family = AF_INET;
-	sin.sin_port = sk->dummy_th.dest;
-	sin.sin_addr.s_addr = sk->daddr;
-  }
+	/*
+	 *	Get and verify the address. 
+	 */
+	 
+	if (usin) 
+	{
+		if (addr_len < sizeof(sin)) 
+			return(-EINVAL);
+		err=verify_area(VERIFY_READ, usin, sizeof(sin));
+		if(err)
+			return err;
+		memcpy_fromfs(&sin, usin, sizeof(sin));
+		if (sin.sin_family && sin.sin_family != AF_INET) 
+			return(-EINVAL);
+		if (sin.sin_port == 0) 
+			return(-EINVAL);
+	} 
+	else 
+	{
+		if (sk->state != TCP_ESTABLISHED) 
+			return(-EINVAL);
+		sin.sin_family = AF_INET;
+		sin.sin_port = sk->dummy_th.dest;
+		sin.sin_addr.s_addr = sk->daddr;
+  	}
   
-  if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
-    	return -EACCES;			/* Must turn broadcast on first */
-  sk->inuse = 1;
+  	/*
+  	 *	BSD socket semantics. You must set SO_BROADCAST to permit
+  	 *	broadcasting of data.
+  	 */
+  	if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+	    	return -EACCES;			/* Must turn broadcast on first */
+
+	sk->inuse = 1;
 
-  /* Send the packet. */
-  tmp = udp_send(sk, &sin, from, len);
+	/* Send the packet. */
+	tmp = udp_send(sk, &sin, from, len);
 
-  /* The datagram has been sent off.  Release the socket. */
-  release_sock(sk);
-  return(tmp);
+	/* The datagram has been sent off.  Release the socket. */
+	release_sock(sk);
+	return(tmp);
 }
 
+/*
+ *	In BSD SOCK_DGRAM a write is just like a send.
+ */
 
-static int
-udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
+static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
 	  unsigned flags)
 {
-  return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
+	return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
 }
 
 
-int
-udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+/*
+ *	IOCTL requests applicable to the UDP protocol
+ */
+ 
+int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
-  int err;
-  switch(cmd) {
-	case DDIOCSDBG:
+	int err;
+	switch(cmd) 
+	{
+		case DDIOCSDBG:
 		{
 			int val;
 
@@ -367,7 +448,7 @@
 			}
 		}
 		break;
-	case TIOCOUTQ:
+		case TIOCOUTQ:
 		{
 			unsigned long amount;
 
@@ -381,14 +462,14 @@
 			return(0);
 		}
 
-	case TIOCINQ:
+		case TIOCINQ:
 		{
 			struct sk_buff *skb;
 			unsigned long amount;
 
 			if (sk->state == TCP_LISTEN) return(-EINVAL);
 			amount = 0;
-			skb = sk->rqueue;
+			skb = skb_peek(&sk->receive_queue);
 			if (skb != NULL) {
 				/*
 				 * We will only return the amount
@@ -405,88 +486,115 @@
 			return(0);
 		}
 
-	default:
-		return(-EINVAL);
-  }
-  return(0);
+		default:
+			return(-EINVAL);
+	}
+	return(0);
 }
 
 
 /*
- * This should be easy, if there is something there we\
- * return it, otherwise we block.
+ * 	This should be easy, if there is something there we\
+ * 	return it, otherwise we block.
  */
-int
-udp_recvfrom(struct sock *sk, unsigned char *to, int len,
+
+int udp_recvfrom(struct sock *sk, unsigned char *to, int len,
 	     int noblock, unsigned flags, struct sockaddr_in *sin,
 	     int *addr_len)
 {
-  int copied = 0;
-  struct sk_buff *skb;
-  int er;
-
+  	int copied = 0;
+  	struct sk_buff *skb;
+  	int er;
 
-  /*
-   * This will pick up errors that occured while the program
-   * was doing something else.
-   */
-  if (sk->err) {
-	int err;
 
-	err = -sk->err;
-	sk->err = 0;
-	return(err);
-  }
-
-  if (len == 0) 
-  	return(0);
-  if (len < 0) 
-  	return(-EINVAL);
+  	/*
+  	 * This will pick up errors that occured while the program
+  	 * was doing something else.
+  	 */
+  
+  	if (sk->err) 
+  	{
+		int err;
+	
+		err = -sk->err;
+		sk->err = 0;
+		return(err);
+  	}
+
+  	if (len == 0) 
+  		return(0);
+  	if (len < 0) 
+  		return(-EINVAL);
 
-  if (addr_len) {
-	er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+	/*
+	 *	Check any passed addresses
+	 */
+	 
+  	if (addr_len) 
+  	{
+		er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+		if(er)
+			return(er);
+		put_fs_long(sizeof(*sin), addr_len);
+  	}
+  
+  	if(sin)
+  	{
+  		er=verify_area(VERIFY_WRITE, sin, sizeof(*sin));
+  		if(er)
+  			return(er);
+  	}
+  	
+  	/*
+  	 *	Check the buffer we were given
+  	 */
+  	 
+  	er=verify_area(VERIFY_WRITE,to,len);
 	if(er)
-		return(er);
-	put_fs_long(sizeof(*sin), addr_len);
-  }
-  if(sin)
-  {
-  	er=verify_area(VERIFY_WRITE, sin, sizeof(*sin));
-  	if(er)
-  		return(er);
-  }
-  er=verify_area(VERIFY_WRITE,to,len);
-  if(er)
-  	return er;
-  skb=skb_recv_datagram(sk,flags,noblock,&er);
-  if(skb==NULL)
-  	return er;
-  copied = min(len, skb->len);
+		return er;
+	
+	/*
+	 *	From here the generic datagram does a lot of the work. Come
+	 *	the finished NET3, it will do _ALL_ the work!
+	 */
+	 	
+	skb=skb_recv_datagram(sk,flags,noblock,&er);
+	if(skb==NULL)
+  		return er;
+  
+  	copied = min(len, skb->len);
+
+  	/*
+  	 *	FIXME : should use udp header size info value 
+  	 */
+  	 
+	skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
+	sk->stamp=skb->stamp;
 
-  /* FIXME : should use udp header size info value */
-  skb_copy_datagram(skb,sizeof(struct udphdr),to,copied);
+	/* Copy the address. */
+	if (sin) 
+	{
+		struct sockaddr_in addr;
 
-  /* Copy the address. */
-  if (sin) {
-	struct sockaddr_in addr;
-
-	addr.sin_family = AF_INET;
-	addr.sin_port = skb->h.uh->source;
-	addr.sin_addr.s_addr = skb->daddr;
-	memcpy_tofs(sin, &addr, sizeof(*sin));
-  }
+		addr.sin_family = AF_INET;
+		addr.sin_port = skb->h.uh->source;
+		addr.sin_addr.s_addr = skb->daddr;
+		memcpy_tofs(sin, &addr, sizeof(*sin));
+  	}
   
-  skb_free_datagram(skb);
-  release_sock(sk);
-  return(copied);
+  	skb_free_datagram(skb);
+  	release_sock(sk);
+  	return(copied);
 }
 
+/*
+ *	Read has the same semantics as recv in SOCK_DGRAM
+ */
 
-int
-udp_read(struct sock *sk, unsigned char *buff, int len, int noblock,
+int udp_read(struct sock *sk, unsigned char *buff, int len, int noblock,
 	 unsigned flags)
 {
-  return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+	return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
 }
 
 
@@ -507,7 +615,7 @@
   if (sin.sin_family && sin.sin_family != AF_INET) 
   	return(-EAFNOSUPPORT);
 
-  if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+  if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
     	return -EACCES;			/* Must turn broadcast on first */
   	
   sk->daddr = sin.sin_addr.s_addr;
@@ -527,74 +635,100 @@
 }
 
 
-/* All we need to do is get the socket, and then do a checksum. */
-int
-udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+/*
+ *	All we need to do is get the socket, and then do a checksum. 
+ */
+ 
+int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
 	unsigned long daddr, unsigned short len,
 	unsigned long saddr, int redo, struct inet_protocol *protocol)
 {
-  struct sock *sk;
-  struct udphdr *uh;
+  	struct sock *sk;
+  	struct udphdr *uh;
+
+	/*
+	 *	Get the header.
+	 */
+  	uh = (struct udphdr *) skb->h.uh;
+  	
+  	ip_statistics.IpInDelivers++;
+
+  	
+  	sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
+	if (sk == NULL) 
+  	{
+  		udp_statistics.UdpNoPorts++;
+		if (ip_chk_addr(daddr) == IS_MYADDR) 
+		{
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
+		}
+		/*
+		 * Hmm.  We got an UDP broadcast to a port to which we
+		 * don't wanna listen.  Ignore it.
+		 */
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+  	}
 
-  uh = (struct udphdr *) skb->h.uh;
-  sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
-  if (sk == NULL) 
-  {
-	if (chk_addr(daddr) == IS_MYADDR) 
+	if (uh->check && udp_check(uh, len, saddr, daddr)) 
 	{
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
+		printk("UDP: bad checksum.\n");
+		DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
+		udp_statistics.UdpInErrors++;
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
 	}
+
+	skb->sk = sk;
+	skb->dev = dev;
+	skb->len = len;
+
 	/*
-	 * Hmm.  We got an UDP broadcast to a port to which we
-	 * don't wanna listen.  The only thing we can do now is
-	 * to ignore the packet... -FvK
+	 *	These are supposed to be switched. 
 	 */
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-	return(0);
-  }
+	 
+	skb->daddr = saddr;
+	skb->saddr = daddr;
 
-  if (uh->check && udp_check(uh, len, saddr, daddr)) {
-	DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-	return(0);
-  }
 
-  skb->sk = sk;
-  skb->dev = dev;
-  skb->len = len;
-
-/* These are supposed to be switched. */
-  skb->daddr = saddr;
-  skb->saddr = daddr;
-
-
-  /* Charge it to the socket. */
-  if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) 
-  {
-	skb->sk = NULL;
-	kfree_skb(skb, FREE_WRITE);
-	release_sock(sk);
-	return(0);
-  }
-  sk->rmem_alloc += skb->mem_len;
+	/*
+	 *	Charge it to the socket, dropping if the queue is full.
+	 */
+	 
+	if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) 
+	{
+		udp_statistics.UdpInErrors++;
+		ip_statistics.IpInDiscards++;
+		ip_statistics.IpInDelivers--;
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_WRITE);
+		release_sock(sk);
+		return(0);
+	}
+	sk->rmem_alloc += skb->mem_len;
+  	udp_statistics.UdpInDatagrams++;
+  	
+	/*
+	 *	At this point we should print the thing out. 
+	 */
 
-  /* At this point we should print the thing out. */
-  DPRINTF((DBG_UDP, "<< \n"));
-  print_udp(uh);
+	DPRINTF((DBG_UDP, "<< \n"));
+	print_udp(uh);
 
-  /* Now add it to the data chain and wake things up. */
+	/*
+	 *	Now add it to the data chain and wake things up. 
+	 */
   
-  skb_queue_tail(&sk->rqueue,skb);
+	skb->len = len - sizeof(*uh);  
+	skb_queue_tail(&sk->receive_queue,skb);
 
-  skb->len = len - sizeof(*uh);
 
-  if (!sk->dead) 
-  	sk->data_ready(sk,skb->len);
+	if (!sk->dead) 
+	  	sk->data_ready(sk,skb->len);
   	
-  release_sock(sk);
-  return(0);
+	release_sock(sk);
+	return(0);
 }
 
 
diff -u --recursive --new-file v1.1.3/linux/net/inet/utils.c linux/net/inet/utils.c
--- v1.1.3/linux/net/inet/utils.c	Wed Dec  1 14:44:15 1993
+++ linux/net/inet/utils.c	Wed Apr 13 12:52:30 1994
@@ -19,6 +19,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  */
+
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <linux/types.h>
@@ -31,117 +32,136 @@
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <stdarg.h>
-#include "inet.h"
-#include "dev.h"
-#include "eth.h"
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include "ip.h"
 #include "protocol.h"
 #include "tcp.h"
-#include "skbuff.h"
-#include "arp.h"
+#include <linux/skbuff.h>
 
 
-/* Display an IP address in readable format. */
+/*
+ *	Display an IP address in readable format. 
+ */
+ 
 char *in_ntoa(unsigned long in)
 {
-  static char buff[18];
-  register char *p;
+	static char buff[18];
+	char *p;
 
-  p = (char *) &in;
-  sprintf(buff, "%d.%d.%d.%d",
-	(p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
-  return(buff);
+	p = (char *) &in;
+	sprintf(buff, "%d.%d.%d.%d",
+		(p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
+	return(buff);
 }
 
 
-/* Convert an ASCII string to binary IP. */
-unsigned long
-in_aton(char *str)
+/*
+ *	Convert an ASCII string to binary IP. 
+ */
+ 
+unsigned long in_aton(char *str)
 {
-  unsigned long l;
-  unsigned int val;
-  int i;
-
-  l = 0;
-  for (i = 0; i < 4; i++) {
-	l <<= 8;
-	if (*str != '\0') {
-		val = 0;
-		while (*str != '\0' && *str != '.') {
-			val *= 10;
-			val += *str - '0';
-			str++;
+	unsigned long l;
+	unsigned int val;
+	int i;
+
+	l = 0;
+	for (i = 0; i < 4; i++) 
+	{
+		l <<= 8;
+		if (*str != '\0') 
+		{
+			val = 0;
+			while (*str != '\0' && *str != '.') 
+			{
+				val *= 10;
+				val += *str - '0';
+				str++;
+			}
+			l |= val;
+			if (*str != '\0') 
+				str++;
 		}
-		l |= val;
-		if (*str != '\0') str++;
 	}
-  }
-  return(htonl(l));
+	return(htonl(l));
 }
 
 
-void
-dprintf(int level, char *fmt, ...)
+/*
+ *	Debugging print out
+ */
+ 
+void dprintf(int level, char *fmt, ...)
 {
-  va_list args;
-  char *buff;
-  extern int vsprintf(char * buf, const char * fmt, va_list args);
-
-  if (level != inet_debug) return;
-
-  buff = (char *) kmalloc(256, GFP_ATOMIC);
-  if (buff != NULL) {
-	va_start(args, fmt);
-	vsprintf(buff, fmt, args);
-	va_end(args);
-	printk(buff);
-	kfree(buff);
-  }
+	va_list args;
+	char *buff;
+	extern int vsprintf(char * buf, const char * fmt, va_list args);
+
+	if (level != inet_debug) 
+		return;
+
+	buff = (char *) kmalloc(256, GFP_ATOMIC);
+	if (buff != NULL) 
+	{
+		va_start(args, fmt);
+		vsprintf(buff, fmt, args);
+		va_end(args);
+		printk(buff);
+		kfree(buff);
+  	}
+  	else
+		printk("Debugging output lost: No free memory.\n");  	
 }
 
-
-int
-dbg_ioctl(void *arg, int level)
+/*
+ *	Debugging ioctl() requests
+ */
+ 
+int dbg_ioctl(void *arg, int level)
 {
-  int val;
-  int err;
+	int val;
+	int err;
   
-  if (!suser()) return(-EPERM);
-  err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));
-  if(err)
-  	return err;
-  val = get_fs_long((int *)arg);
-  switch(val) {
-	case 0:	/* OFF */
-		inet_debug = DBG_OFF;
-		break;
-	case 1:	/* ON, INET */
-		inet_debug = level;
-		break;
-
-	case DBG_RT:		/* modules */
-	case DBG_DEV:
-	case DBG_ETH:
-	case DBG_PROTO:
-	case DBG_TMR:
-	case DBG_PKT:
-	case DBG_RAW:
-
-	case DBG_LOOPB:		/* drivers */
-	case DBG_SLIP:
-
-	case DBG_ARP:		/* protocols */
-	case DBG_IP:
-	case DBG_ICMP:
-	case DBG_TCP:
-	case DBG_UDP:
-
-		inet_debug = val;
-		break;
-
-	default:
-		return(-EINVAL);
-  }
-
-  return(0);
+	if (!suser()) 
+		return(-EPERM);
+	err=verify_area(VERIFY_READ, (void *)arg, sizeof(int));
+	if(err)
+  		return err;
+	val = get_fs_long((int *)arg);
+	switch(val) 
+	{
+		case 0:	/* OFF */
+			inet_debug = DBG_OFF;
+			break;
+		case 1:	/* ON, INET */
+			inet_debug = level;
+			break;
+
+		case DBG_RT:		/* modules */
+		case DBG_DEV:
+		case DBG_ETH:
+		case DBG_PROTO:
+		case DBG_TMR:
+		case DBG_PKT:
+		case DBG_RAW:
+	
+		case DBG_LOOPB:		/* drivers */
+		case DBG_SLIP:
+	
+		case DBG_ARP:		/* protocols */
+		case DBG_IP:
+		case DBG_ICMP:
+		case DBG_TCP:
+		case DBG_UDP:
+	
+			inet_debug = val;
+			break;
+	
+		default:
+			return(-EINVAL);
+	}
+	
+	return(0);
 }
diff -u --recursive --new-file v1.1.3/linux/net/socket.c linux/net/socket.c
--- v1.1.3/linux/net/socket.c	Mon Jan 31 16:47:34 1994
+++ linux/net/socket.c	Wed Apr 13 12:52:30 1994
@@ -17,6 +17,14 @@
  *		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 module is effectively the top level interface to the BSD socket
+ *	paradigm. Because it is very simple it works well for Unix domain sockets,
+ *	but requires a whole layer of substructure for the other protocols.
+ *
+ *	In addition it lacks an effective kernel -> kernel interface to go with
+ *	the user one.
  */
 
 #include <linux/config.h>
diff -u --recursive --new-file v1.1.3/linux/net/unix/proc.c linux/net/unix/proc.c
--- v1.1.3/linux/net/unix/proc.c	Fri Mar  4 09:25:21 1994
+++ linux/net/unix/proc.c	Wed Apr 13 12:52:30 1994
@@ -35,44 +35,53 @@
 
 
 /* Called from PROCfs. */
-int unix_get_info(char *buffer)
+int unix_get_info(char *buffer, char **start, off_t offset, int length)
 {
-  char *pos;
-  int i;
+  	off_t pos=0;
+  	off_t begin=0;
+  	int len=0;
+  	int i;
 
-  pos = buffer;
-  pos += sprintf(pos, "Num RefCount Protocol Flags    Type St Path\n");
+  	len += sprintf(buffer, "Num RefCount Protocol Flags    Type St Path\n");
 
-  for(i = 0; i < NSOCKETS; i++) {
-	if (unix_datas[i].refcnt>0) {
-		pos += sprintf(pos, "%2d: %08X %08X %08lX %04X %02X", i,
-			unix_datas[i].refcnt,
-			unix_datas[i].protocol,
-			unix_datas[i].socket->flags,
-			unix_datas[i].socket->type,
-			unix_datas[i].socket->state
-		);
+  	for(i = 0; i < NSOCKETS; i++) 
+  	{
+		if (unix_datas[i].refcnt>0) 
+		{
+			len += sprintf(buffer+len, "%2d: %08X %08X %08lX %04X %02X", i,
+				unix_datas[i].refcnt,
+				unix_datas[i].protocol,
+				unix_datas[i].socket->flags,
+				unix_datas[i].socket->type,
+				unix_datas[i].socket->state
+			);
 
-		/* If socket is bound to a filename, we'll print it. */
-		if(unix_datas[i].sockaddr_len>0) {
-			pos += sprintf(pos, " %s\n",
+			/* If socket is bound to a filename, we'll print it. */
+			if(unix_datas[i].sockaddr_len>0) 
+			{
+				len += sprintf(buffer+len, " %s\n",
 				unix_datas[i].sockaddr_un.sun_path);
-		} else { /* just add a newline */
-			*pos='\n';
-			pos++;
-			*pos='\0';
+			} 
+			else 
+			{ /* just add a newline */
+				buffer[len++]='\n';
+				buffer[len++]='\0';
+			}
+			
+			pos=begin+len;
+			if(pos<offset)
+			{
+				len=0;
+				begin=pos;
+			}
+			if(pos>offset+length)
+				break;
 		}
-
-		/*
-		 * Check whether buffer _may_ overflow in the next loop.
-		 * Since sockets may have very very long paths, we make
-		 * PATH_MAX+80 the minimum space left for a new line.
-		 */
-		if (pos > buffer+PAGE_SIZE-80-PATH_MAX) {
-			printk("UNIX: netinfo: oops, too many sockets.\n");
-			return(pos - buffer);
-		}
 	}
-  }
-  return(pos - buffer);
+	
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	return len;
 }
diff -u --recursive --new-file v1.1.3/linux/net/unix/sock.c linux/net/unix/sock.c
--- v1.1.3/linux/net/unix/sock.c	Thu Mar 17 10:01:26 1994
+++ linux/net/unix/sock.c	Wed Apr 13 12:52:30 1994
@@ -413,13 +413,10 @@
   old_fs = get_fs();
   set_fs(get_ds());
   i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0);
-  if (i == -EEXIST)
-  	i = -EADDRINUSE;
-  if (i == 0)
-  	i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
+  if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
   set_fs(old_fs);
   if (i < 0) {
-	dprintf(1, "UNIX: bind: can't open socket %s\n", fname);
+	printk("UNIX: bind: can't open socket %s\n", fname);
 	return(i);
   }
   upd->sockaddr_len = sockaddr_len;	/* now its legal */
