diff -u --recursive --new-file v1.1.68/linux/Makefile linux/Makefile
--- v1.1.68/linux/Makefile	Tue Nov 29 18:51:23 1994
+++ linux/Makefile	Tue Nov 29 18:48:42 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 68
+SUBLEVEL = 69
 
 ARCH = i386
 
diff -u --recursive --new-file v1.1.68/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.1.68/linux/arch/i386/config.in	Mon Nov 28 14:21:43 1994
+++ linux/arch/i386/config.in	Wed Nov 30 21:53:47 1994
@@ -20,6 +20,9 @@
 bool 'TCP/IP networking' CONFIG_INET y
 if [ "$CONFIG_INET" "=" "y" ]; then
 bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD n
+#bool 'IP multicasting (ALPHA)' CONFIG_IP_MULTICAST n
+#bool 'IP firewalling' CONFIG_IP_FIREWALL n
+#bool 'IP accounting' CONFIG_IP_ACCT n
 comment '(it is safe to leave these untouched)'
 bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n
 bool 'Reverse ARP' CONFIG_INET_RARP n
@@ -95,6 +98,10 @@
 	bool 'WD80*3 support' CONFIG_WD80x3 n
 	bool 'SMC Ultra support' CONFIG_ULTRA n
 fi
+bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n
+if [ "$CONFIG_LANCE" = "y" ]; then
+	bool '   with PCnet/PCI support' CONFIG_PCI y
+fi
 bool '3COM cards' CONFIG_NET_VENDOR_3COM y
 if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
 	bool '3c501 support' CONFIG_EL1 n
@@ -107,32 +114,41 @@
 fi
 bool 'Other ISA cards' CONFIG_NET_ISA n
 if [ "$CONFIG_NET_ISA" = "y" ]; then
-	bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n
-	bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n
+	bool 'Cabletron E21xx support' CONFIG_E2100 n
 	bool 'DEPCA support' CONFIG_DEPCA n
 	bool 'EtherWorks 3 support' CONFIG_EWRK3 n
 	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-		bool 'EtherExpress support' CONFIG_EEXPRESS n
+#		bool 'Arcnet support' CONFIG_ARCNET n
 		bool 'AT1700 support' CONFIG_AT1700 n
+#		bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n
+		bool 'EtherExpress support' CONFIG_EEXPRESS n
 		bool 'NI5210 support' CONFIG_NI52 n
 		bool 'NI6510 support' CONFIG_NI65 n
 	fi
-	bool 'HP PCLAN support' CONFIG_HPLAN n
-	bool 'HP PCLAN PLUS support' CONFIG_HPLAN_PLUS n
+	bool 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n
+	bool 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n
 	bool 'NE2000/NE1000 support' CONFIG_NE2000 y
 	bool 'SK_G16 support' CONFIG_SK_G16 n
 fi
-bool 'EISA and on board controllers' CONFIG_NET_EISA n
+bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n
+if [ "$CONFIG_NET_EISA" = "y" ]; then
 	if [ "$CONFIG_NET_ALPHA" = "y" ]; then
 		bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
 	fi
 	bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
+#	bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n
+#	bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n
+#	bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n
+	bool 'Zenith Z-Note support' CONFIG_ZNET y
+fi
 bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n
 if [ "$CONFIG_NET_POCKET" = "y" ]; then
+	bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
 	bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n
 	bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n
-	bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n
-	bool 'Zenith Z-Note support' CONFIG_ZNET n
+#	bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n
+#	bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n
+#	bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n
 fi
 fi
 fi
diff -u --recursive --new-file v1.1.68/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- v1.1.68/linux/drivers/block/blk.h	Sat Nov 26 18:36:04 1994
+++ linux/drivers/block/blk.h	Wed Nov 30 22:01:07 1994
@@ -117,7 +117,7 @@
 
 #define DEVICE_NAME "scsitape"
 #define DEVICE_INTR do_st  
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (MINOR(device) & 0x7f)
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
diff -u --recursive --new-file v1.1.68/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v1.1.68/linux/drivers/char/ChangeLog	Tue Nov 22 15:41:10 1994
+++ linux/drivers/char/ChangeLog	Tue Nov 29 18:58:28 1994
@@ -1,3 +1,32 @@
+Tue Nov 29 10:21:09 1994  Theodore Y. Ts'o  (tytso@rt-11)
+
+	* tty_io.c (tty_unregister_driver): Fix bug in
+		tty_unregister_driver where the pointer to the refcount is
+		tested, instead of the refcount itself.  This caused
+		tty_unregister_driver to always return EBUSY.
+
+Sat Nov 26 11:59:24 1994  Theodore Y. Ts'o  (tytso@rt-11)
+
+	* tty_io.c (tty_ioctl): Add support for the new ioctl
+		TIOCTTYGSTRUCT, which allow a kernel debugging program
+		direct read access to the tty and tty_driver structures.
+
+Fri Nov 25 17:26:22 1994  Theodore Y. Ts'o  (tytso@rt-11)
+
+	* serial.c (rs_set_termios): Don't wake up processes blocked in
+		open when the CLOCAL flag changes, since a blocking
+		open only samples the CLOCAL flag once when it blocks,
+		and doesn't check it again.  (n.b.  FreeBSD has a
+		different behavior for blocking opens; it's not clear
+		whether Linux or FreeBSD's interpretation is correct.
+		POSIX doesn't give clear guidance on this issue, so
+		this may change in the future....)
+
+	* serial.c (block_til_ready): Use the correct termios structure to
+		check the CLOCAL flag.  If the cuaXX device is active,
+		then check the saved termios for the ttySXX device.
+		Otherwise, use the currently active termios structure.
+
 Sun Nov  6 21:05:44 1994  Theodore Y. Ts'o  (tytso@rt-11)
 
 	* serial.c (change_speed): Add support for direct access of
diff -u --recursive --new-file v1.1.68/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v1.1.68/linux/drivers/char/lp.c	Mon Nov 28 14:21:44 1994
+++ linux/drivers/char/lp.c	Tue Nov 29 18:03:53 1994
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/lp.h>
 #include <linux/malloc.h>
+#include <linux/ioport.h>
 
 #include <asm/io.h>
 #include <asm/segment.h>
diff -u --recursive --new-file v1.1.68/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.1.68/linux/drivers/char/serial.c	Tue Nov 22 15:41:11 1994
+++ linux/drivers/char/serial.c	Tue Nov 29 18:58:28 1994
@@ -1773,9 +1773,17 @@
 		rs_start(tty);
 	}
 
+#if 0
+	/*
+	 * No need to wake up processes in open wait, since they
+	 * sample the CLOCAL flag once, and don't recheck it.
+	 * XXX  It's not clear whether the current behavior is correct
+	 * or not.  Hence, this may change.....
+	 */
 	if (!(old_termios->c_cflag & CLOCAL) &&
 	    (tty->termios->c_cflag & CLOCAL))
 		wake_up_interruptible(&info->open_wait);
+#endif
 }
 
 /*
@@ -1967,9 +1975,14 @@
 		return 0;
 	}
 
-	if (info->normal_termios.c_cflag & CLOCAL)
-		do_clocal = 1;
-
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
 	/*
 	 * Block waiting for the carrier detect and the line to become
 	 * free (i.e., not in use by the callout).  While we are in
diff -u --recursive --new-file v1.1.68/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.1.68/linux/drivers/char/tty_io.c	Mon Nov 28 14:21:44 1994
+++ linux/drivers/char/tty_io.c	Tue Nov 29 18:58:28 1994
@@ -1438,6 +1438,14 @@
 				default: 
 					return -EINVAL;
 			}
+		case TIOCTTYGSTRUCT:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct tty_struct));
+			if (retval)
+				return retval;
+			memcpy_tofs((struct tty_struct *) arg,
+				    tty, sizeof(struct tty_struct));
+			return 0;
 		default:
 			if (tty->driver.ioctl) {
 				retval = (tty->driver.ioctl)(tty, file,
diff -u --recursive --new-file v1.1.68/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.1.68/linux/drivers/net/3c509.c	Sun Oct  9 19:28:47 1994
+++ linux/drivers/net/3c509.c	Thu Dec  1 12:23:30 1994
@@ -633,8 +633,13 @@
 set_multicast_list(struct device *dev, int num_addrs, void *addrs)
 {
 	short ioaddr = dev->base_addr;
-	if (el3_debug > 1)
-		printk("%s: Setting Rx mode to %d addresses.\n", dev->name, num_addrs);
+	if (el3_debug > 1) {
+		static int old = 0;
+		if (old != num_addrs) {
+			old = num_addrs;
+			printk("%s: Setting Rx mode to %d addresses.\n", dev->name, num_addrs);
+		}
+	}
 	if (num_addrs > 0) {
 		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
 	} else if (num_addrs < 0) {
diff -u --recursive --new-file v1.1.68/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.1.68/linux/drivers/net/eexpress.c	Tue Nov 29 18:51:24 1994
+++ linux/drivers/net/eexpress.c	Wed Nov 30 10:19:12 1994
@@ -18,7 +18,7 @@
 	Rework the board error reset
 	The statistics need to be updated correctly.
 
-        Modularized my Pauline Middelink <middelin@polyware.iaf.nl>
+        Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
 */
 
 static char *version =
@@ -653,6 +653,9 @@
 	free_irq(dev->irq);
 
 	irq2dev_map[dev->irq] = 0;
+
+	/* release the ioport-region */
+	release_region(ioaddr, 16);
 
 	/* Update the statistics here. */
 
diff -u --recursive --new-file v1.1.68/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v1.1.68/linux/drivers/net/lance.c	Tue Nov 22 15:41:11 1994
+++ linux/drivers/net/lance.c	Wed Nov 30 10:19:12 1994
@@ -15,7 +15,7 @@
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 */
 
-static char *version = "lance.c:v1.05 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "lance.c:v1.06 11/29/94 becker@cesdis.gsfc.nasa.gov\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -26,6 +26,8 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -37,7 +39,7 @@
 struct device *init_etherdev(struct device *dev, int sizeof_private,
 							 unsigned long *mem_startp);
 static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
-unsigned long lance_probe1(short ioaddr, unsigned long mem_start);
+unsigned long lance_probe1(int ioaddr, unsigned long mem_start);
 
 #ifdef HAVE_DEVLIST
 struct netdev_entry lance_drv =
@@ -72,7 +74,7 @@
 Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
 channel.  This driver probes the likely base addresses:
 {0x300, 0x320, 0x340, 0x360}.
-After the board is found it generates an DMA-timeout interrupt and uses
+After the board is found it generates a DMA-timeout interrupt and uses
 autoIRQ to find the IRQ line.  The DMA channel can be set with the low bits
 of the otherwise-unused dev->mem_start value (aka PARAM1).  If unset it is
 probed for by enabling each free DMA channel in turn and checking if
@@ -102,7 +104,7 @@
 avoid the administrative overhead. For the Rx side this avoids dynamically
 allocating full-sized buffers "just in case", at the expense of a
 memory-to-memory data copy for each packet received.  For most systems this
-is an good tradeoff: the Rx buffer will always be in low memory, the copy
+is a good tradeoff: the Rx buffer will always be in low memory, the copy
 is inexpensive, and it primes the cache for later packet processing.  For Tx
 the buffers are only used when needed as low-memory bounce buffers.
 
@@ -218,12 +220,17 @@
 	{0x0003, "PCnet/ISA 79C960", 0},	/* 79C960 PCnet/ISA.  */
 	{0x2260, "PCnet/ISA+ 79C961", 0},	/* 79C961 PCnet/ISA+, Plug-n-Play.  */
 	{0x2420, "PCnet/PCI 79C970", 0},	/* 79C970 or 79C974 PCnet-SCSI, PCI. */
-	{0x2430, "PCnet/VLB 79C965", 0},	/* 79C965 PCnet for VL bus. */
+	/* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
+		it the PCnet32. */
+	{0x2430, "PCnet32", 0},				/* 79C965 PCnet for VL bus. */
 	{0x0, 	 "PCnet (unknown)", 0},
 };
 
 enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, LANCE_UNKNOWN=5};
 
+/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
+static unsigned char pci_irq_line = 0;
+
 static int lance_open(struct device *dev);
 static void lance_init_ring(struct device *dev);
 static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
@@ -239,12 +246,41 @@
 
 /* This lance probe is unlike the other board probes in 1.0.*.  The LANCE may
    have to allocate a contiguous low-memory region for bounce buffers.
-   This requirement is satisfied by having the lance initialization occur before the
-   memory management system is started, and thus well before the other probes. */
+   This requirement is satisfied by having the lance initialization occur
+   before the memory management system is started, and thus well before the
+   other probes. */
+
 unsigned long lance_init(unsigned long mem_start, unsigned long mem_end)
 {
 	int *port;
 
+#if defined(CONFIG_PCI)
+#define AMD_VENDOR_ID 0x1022
+#define AMD_DEVICE_ID 0x2000
+    if (pcibios_present()) {
+	    int pci_index;
+		printk("lance.c: PCI bios is present, checking for devices...\n");
+		for (pci_index = 0; pci_index < 8; pci_index++) {
+			unsigned char pci_bus, pci_device_fn;
+			unsigned long pci_ioaddr;
+		
+			if (pcibios_find_device (AMD_VENDOR_ID, AMD_DEVICE_ID, pci_index,
+									 &pci_bus, &pci_device_fn) != 0)
+				break;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
+			printk("Found PCnet/PCI at %#lx, irq %d (mem_start is %#lx).\n",
+				   pci_ioaddr, pci_irq_line, mem_start);
+			mem_start = lance_probe1(pci_ioaddr, mem_start);
+			pci_irq_line = 0;
+		}
+	}
+#endif  /* defined(CONFIG_PCI) */
+
 	for (port = lance_portlist; *port; port++) {
 		int ioaddr = *port;
 
@@ -258,7 +294,7 @@
 	return mem_start;
 }
 
-unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
+unsigned long lance_probe1(int ioaddr, unsigned long mem_start)
 {
 	struct device *dev;
 	struct lance_private *lp;
@@ -365,7 +401,10 @@
 	outw(0x0000, ioaddr+LANCE_ADDR);
 	inw(ioaddr+LANCE_ADDR);
 
-	if (hp_builtin) {
+	if (pci_irq_line) {
+		dev->dma = 4;			/* Native bus-master, no DMA channel needed. */
+		dev->irq = pci_irq_line;
+	} else if (hp_builtin) {
 		char dma_tbl[4] = {3, 5, 6, 0};
 		char irq_tbl[8] = {3, 4, 5, 9};
 		unsigned char port_val = inb(hp_builtin);
@@ -494,7 +533,8 @@
 	int ioaddr = dev->base_addr;
 	int i;
 
-	if (request_irq(dev->irq, &lance_interrupt, 0, "lance")) {
+	if (dev->irq == 0 ||
+		request_irq(dev->irq, &lance_interrupt, 0, "lance")) {
 		return -EAGAIN;
 	}
 
@@ -840,7 +880,7 @@
 		int status = lp->rx_ring[entry].base >> 24;
 
 		if (status != 0x03) {			/* There was an error. */
-			/* There is an tricky error noted by John Murphy,
+			/* There is a tricky error noted by John Murphy,
 			   <murf@perftech.com> to Russ Nelson: Even with full-sized
 			   buffers it's possible for a jabber packet to use two
 			   buffers, with only the last correctly noting the error. */
@@ -971,6 +1011,8 @@
 		}
 		outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
 	} else {
+		/* Log any net taps. */
+		printk("%s: Promiscuous mode enabled.\n", dev->name);
 		outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
 	}
 
diff -u --recursive --new-file v1.1.68/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v1.1.68/linux/drivers/net/net_init.c	Sun Oct  9 19:28:49 1994
+++ linux/drivers/net/net_init.c	Wed Nov 30 21:53:47 1994
@@ -165,7 +165,7 @@
 	}
 
 	/* New-style flags. */
-	dev->flags		= IFF_BROADCAST;
+	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;
 	dev->family		= AF_INET;
 	dev->pa_addr	= 0;
 	dev->pa_brdaddr = 0;
diff -u --recursive --new-file v1.1.68/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.1.68/linux/drivers/net/plip.c	Tue Nov 29 18:51:24 1994
+++ linux/drivers/net/plip.c	Wed Nov 30 10:19:12 1994
@@ -545,7 +545,7 @@
     enable_irq(dev->irq);
 }
 
-/* PLIP_ERROR --- wait til other end settled */
+/* PLIP_ERROR --- wait till other end settled */
 static int
 plip_error(struct device *dev)
 {
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/ChangeLog linux/drivers/scsi/ChangeLog
--- v1.1.68/linux/drivers/scsi/ChangeLog	Tue Nov 22 15:41:12 1994
+++ linux/drivers/scsi/ChangeLog	Thu Dec  1 00:15:12 1994
@@ -1,3 +1,203 @@
+Tue Nov 29 15:43:50 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.68 released.
+
+	Add support for 12 byte vendor specific commands in scsi-generics,
+	more (i.e. the last manditory) low-level changes to support
+	loadable modules, plus a few other changes people have requested
+	lately.  Changes by me (ERY) unless otherwise noted.  Spelling
+	changes appear from some unknown corner of the universe.
+
+	* Throughout: Change COMMAND_SIZE() to use SCpnt->cmd_len.
+
+	* Throughout: Change info() low level function to take a Scsi_Host
+	pointer.  This way the info function can return specific
+	information about the host in question, if desired.
+
+	* All low-level drivers: Add NULL in initializer for the
+	usage_count field added to Scsi_Host_Template.
+
+	* aha152x.[c,h]: Remove redundant info() function.
+
+	* aha1542.[c,h]: Likewise.
+
+	* aha1740.[c,h]: Likewise.
+
+	* aha274x.[c,h]: Likewise.
+
+	* eata.[c,h]: Likewise.
+
+	* pas16.[c,h]: Likewise.
+
+	* scsi_debug.[c,h]: Likewise.
+
+	* t128.[c,h]: Likewise.
+
+	* u14-34f.[c,h]: Likewise.
+
+	* ultrastor.[c,h]: Likewise.
+
+	* wd7000.[c,h]: Likewise.
+
+	* aha1542.c: Add support for command line options with lilo to set
+	DMA parameters, I/O port.  From Matt Aarnio.
+
+	* buslogic.[c,h]: New version (1.13) from Dave Gentzel.
+
+	* hosts.h: Add new field to Scsi_Hosts "block" to allow blocking
+	all I/O to certain other cards.  Helps prevent problems with some
+	ISA motherboards.
+
+	* hosts.h: Add usage_count to Scsi_Host_Template.
+
+	* hosts.h: Add n_io_port to Scsi_Host (used when releasing module).
+
+	* hosts.c: Initialize block field.
+	
+	* in2000.c: Remove "static" declarations from exported functions.
+
+	* in2000.h: Likewise.
+
+	* scsi.c: Correctly set cmd_len field as required.  Save and
+	change setting when doing a request_sense, restore when done.
+	Move abort timeout message.  Fix panic in request_queueable to
+	print correct function name.
+
+	* scsi.c: When incrementing usage count, walk block linked list
+	for host, and or in SCSI_HOST_BLOCK bit.  When decrementing usage
+	count to 0, clear this bit to allow usage to continue, wake up
+	processes waiting.
+
+
+	* scsi_ioctl.c: If we have an info() function, call it, otherwise
+	if we have a "name" field, use it, else do nothing.
+
+	* sd.c, sr.c: Clear cmd_len field prior to each command we
+	generate.
+
+	* sd.h: Add "has_part_table" bit to rscsi_disks.
+
+	* sg.[c,h]: Add support for vendor specific 12 byte commands (i.e.
+	override command length in COMMAND_SIZE).
+
+	* sr.c: Bugfix from Gerd in photocd code.
+
+	* sr.c: Bugfix in get_sectorsize - always use scsi_malloc buffer -
+	we cannot guarantee that the stack is < 16Mb.
+
+Tue Nov 22 15:40:46 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.67 released.
+
+	* sr.c: Change spelling of manufactor to manufacturer.
+
+	* scsi.h: Likewise.
+
+	* scsi.c: Likewise.
+
+	* qlogic.c: Spelling corrections.
+
+	* in2000.h: Spelling corrections.
+
+	* in2000.c: Update from Bill Earnest, change from
+	jshiffle@netcom.com.  Support new bios versions.
+
+	* README.qlogic: Spelling correction.
+
+Tue Nov 22 15:40:46 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.66 released.
+
+	* u14-34f.c: Spelling corrections.
+
+	* sr.[h,c]: Add support for multi-session CDs from Gerd Knorr.
+
+	* scsi.h: Add manufactor field for keeping track of device
+	manufacturer.
+
+	* scsi.c: More spelling corrections.
+
+	* qlogic.h, qlogic.c, README.qlogic: New driver from Tom Zerucha.
+
+	* in2000.c, in2000.h: New driver from Brad McLean/Bill Earnest.
+
+	* fdomain.c: Spelling correction.
+
+	* eata.c: Spelling correction.
+
+Fri Nov 18 15:22:44 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.65 released.
+
+	* eata.h: Update version string to 1.08.00.
+
+	* eata.c: Set sg_tablesize correctly for DPT PM2012 boards.
+
+	* aha274x.seq: Spell checking.
+
+	* README.st: Likewise.
+
+	* README.aha274x: Likewise.
+
+	* ChangeLog: Likewise.
+
+Tue Nov 15 15:35:08 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.64 released.
+
+	* u14-34f.h: Update version number to 1.10.01.
+
+	* u14-34f.c: Use Scsi_Host can_queue variable instead of one from template.
+
+	* eata.[c,h]: New driver for DPT boards from Dario Ballabio.
+
+	* buslogic.c: Use can_queue field.
+
+Wed Nov 30 12:09:09 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.63 released.
+
+	* sd.c: Give I/O error if we attempt 512 byte I/O to a disk with
+	1024 byte sectors.
+
+	* scsicam.c: Make sure we do read from whole disk (mask off
+	partition).
+
+	* scsi.c: Use can_queue in Scsi_Host structure.
+	Fix panic message about invalid host.
+
+	* hosts.c: Initialize can_queue from template.
+
+	* hosts.h: Add can_queue to Scsi_Host structure.
+
+	* aha1740.c: Print out warning about NULL ecbptr.
+
+Fri Nov  4 12:40:30 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.62 released.
+
+	* fdomain.c: Update to version 5.20. (From Rik Faith).  Support
+	BIOS version 3.5.
+
+	* st.h: Add ST_EOD symbol.
+
+	* st.c: Patches from Kai Makisara - support additional densities,
+	add support for MTFSS, MTBSS, MTWSM commands.
+
+	* README.st: Update to document new commands.
+
+	* scsi.c: Add Mediavision CDR-H93MV to blacklist.
+
+Sat Oct 29 20:57:36 1994  Eric Youngdale  (eric@andante.aib.com)
+
+	* Linux 1.1.60 released.
+
+	* u14-34f.[c,h]: New driver from Dario Ballabio.
+
+	* aic7770.c, aha274x_seq.h, aha274x.seq, aha274x.h, aha274x.c,
+	README.aha274x: New files, new driver from John Aycock.
+
+
 Tue Oct 11 08:47:39 1994  Eric Youngdale  (eric@andante)
 
 	* Linux 1.1.54 released.
@@ -6,6 +206,13 @@
 
 	* buslogic.c: Set BUSLOGIC_CMDLUN back to 1 [Eric].
 
+	* ultrastor.c: Fix asm directives for new GCC.
+
+	* sr.c, sd.c: Use new end_scsi_request function.
+
+	* scsi.h(end_scsi_request): Return pointer to block if still
+	active, else return NULL if inactive.  Fixes race condition.
+
 Sun Oct  9 20:23:14 1994  Eric Youngdale  (eric@andante)
 
 	* Linux 1.1.53 released.
@@ -140,6 +347,140 @@
 	* Makefile: Fix typo in NCR53C7xx.
 
 	* st.c: Print correct number for device.
+
+Tue Aug  2 11:29:14 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.38 released.
+
+	Lots of changes in 1.1.38.  All from Drew unless otherwise noted.
+
+	* 53c7,8xx.c: New file from Drew.  PCI driver.
+
+	* 53c7,8xx.h: Likewise.
+
+	* 53c7,8xx.scr: Likewise.
+
+	* 53c8xx_d.h, 53c8xx_u.h, script_asm.pl: Likewise.
+
+	* scsicam.c: New file from Drew.  Read block 0 on the disk and
+	read the partition table.  Attempt to deduce the geometry from
+	the partition table if possible.  Only used by 53c[7,8]xx right
+	now, but could be used by any device for which we have no way
+	of identifying the geometry.
+
+	* sd.c: Use device letters instead of sd%d in a lot of messages.
+
+	* seagate.c: Fix bug that resulted in lockups with some devices.
+
+	* sr.c (sr_open): Return -EROFS, not -EACCES if we attempt to open
+	device for write.
+
+	* hosts.c, Makefile: Update for new driver.
+
+	* NCR5380.c, NCR5380.h, g_NCR5380.h: Update from Drew to support
+	53C400 chip.
+
+	* constants.c: Define CONST_CMND and CONST_MSG.  Other minor
+	cleanups along the way.  Improve handling of CONST_MSG.
+
+	* fdomain.c, fdomain.h: New version from Rik Faith.  Update to
+	5.18.  Should now support TMC-3260 PCI card with 18C30 chip.
+
+	* pas16.c: Update with new irq initialization.
+
+	* t128.c: Update with minor cleanups.
+
+	* scsi.c (scsi_pid): New variable - gives each command a unique
+	id. Add Quantum LPS5235S to blacklist.  Change in_scan to
+	in_scan_scsis and make global.
+
+	* scsi.h: Add some defines for extended message handling,
+	INITIATE/RELEASE_RECOVERY.  Add a few new fields to support sync
+	transfers.
+
+	* scsi_ioctl.h: Add ioctl to request synchronous transfers.
+
+
+Tue Jul 26 21:36:58 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.37 released.
+
+	* aha1542.c: Always call aha1542_mbenable, use new udelay
+	mechanism so we do not wait a long time if the board does not
+	implement this command.
+
+	* g_NCR5380.c: Remove #include <linux/config.h> and #if
+	defined(CONFIG_SCSI_*).
+
+	* seagate.c: Likewise.
+
+	Next round of changes to support loadable modules.  Getting closer
+	now, still not possible to do anything remotely usable.
+
+	hosts.c: Create a linked list of detected high level devices.
+	(scsi_register_device): New function to insert into this list.
+	(scsi_init): Call scsi_register_device for each of the known high
+	level drivers.
+
+	hosts.h: Add prototype for linked list header.  Add structure
+	definition for device template structure which defines the linked
+	list.
+
+	scsi.c: (scan_scsis): Use linked list instead of knowledge about
+	existing high level device drivers.
+	(scsi_dev_init): Use init functions for drivers on linked list
+	instead of explicit list to initialize and attach devices to high
+	level drivers.
+
+	scsi.h: Add new field "attached" to scsi_device - count of number
+	of high level devices attached.
+
+	sd.c, sr.c, sg.c, st.c: Adjust init/attach functions to use new
+	scheme.
+
+Sat Jul 23 13:03:17 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.35 released.
+
+	* ultrastor.c: Change constraint on asm() operand so that it works
+	with gcc 2.6.0.
+
+Thu Jul 21 10:37:39 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.33 released.
+
+	* sr.c(sr_open): Do not allow opens with write access.
+
+Mon Jul 18 09:51:22 1994 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.31 released.
+
+	* sd.c: Increase SD_TIMEOUT from 300 to 600.
+
+	* sr.c: Remove stray task_struct* variable that was no longer
+	used.
+
+	* sr_ioctl.c: Fix typo in up() call.
+
+Sun Jul 17 16:25:29 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.30 released.
+
+	* scsi.c (scan_scsis): Fix detection of some Toshiba CDROM drives
+	that report themselves as disk drives.
+
+	* (Throughout): Use request.sem instead of request.waiting.
+	Should fix swap problem with fdomain.
+
+Thu Jul 14 10:51:42 1994  Eric Youngdale  (eric@esp22)
+
+	* Linux 1.1.29 released.
+
+	* scsi.c (scan_scsis): Add new devices to end of linked list, not
+	to the beginning.
+
+	* scsi.h (SCSI_SLEEP): Remove brain dead hack to try and save
+	the task state before sleeping.
 
 Sat Jul  9 15:01:03 1994  Eric Youngdale  (eric@esp22)
 
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v1.1.68/linux/drivers/scsi/README.st	Tue Nov 22 15:41:12 1994
+++ linux/drivers/scsi/README.st	Wed Nov 30 22:01:07 1994
@@ -1,5 +1,5 @@
 This file contains brief information about the SCSI tape driver.
-Last modified: Sun Oct 16 19:20:03 1994 by root@kai.home
+Last modified: Wed Nov 30 20:34:03 1994 by root@kai.home
 
 BASICS
 
@@ -128,8 +128,8 @@
         The current block size and the density code are stored in the field
         mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
         MT_ST_DENSITY_SHIFT).
-	The WR_PROT, BOT, EOF, EOT, EOD, and D_[800,1600,6250]_BPI
-	status bits reflect the tape status. The other bits are not used.
+	The GMT_xxx status bits reflect the drive status. GMT_DR_OPEN
+	is set if there is no tape in the drive.
 
 
 MISCELLANEOUS COMPILE OPTIONS
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v1.1.68/linux/drivers/scsi/aha1542.c	Tue Nov 29 18:51:25 1994
+++ linux/drivers/scsi/aha1542.c	Wed Nov 30 10:19:12 1994
@@ -45,7 +45,7 @@
 /* The adaptec can be configured for quite a number of addresses, but
 I generally do not want the card poking around at random.  We allow
 two addresses - this allows people to use the Adaptec with a Midi
-card, which also used 0x330 -- can be overriden with LILO! */
+card, which also used 0x330 -- can be overridden with LILO! */
 
 #define MAXBOARDS 2	/* Increase this and the sizes of the
 			   arrays below, if you need more.. */
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c
--- v1.1.68/linux/drivers/scsi/buslogic.c	Tue Nov 29 18:51:26 1994
+++ linux/drivers/scsi/buslogic.c	Wed Nov 30 22:03:47 1994
@@ -24,7 +24,6 @@
  *	5. Allow multiple boards to share an IRQ if the bus allows (EISA, MCA,
  *	   and PCI).
  *	6. Avoid using the 445S workaround for board revs >= D.
- *	7. Get cmd_per_lun put in the Scsi_Host structure.
  */
 
 /*
@@ -130,7 +129,7 @@
    bump up this number. */
 #define BUSLOGIC_MAILBOXES 16
 
-#define BUSLOGIC_NONISA_CMDLUN 4	/* ??? Arbitrary (> 1) */
+#define BUSLOGIC_CMDLUN 4		/* ??? Arbitrary */
 
 /* BusLogic boards can be configured for quite a number of port addresses (six
    to be exact), but I generally do not want the driver poking around at
@@ -390,9 +389,10 @@
     return (hosterr << 16) | scsierr;
 }
 
-const char *buslogic_info(void)
+/* ??? this should really be "const struct Scsi_Host *" */
+const char *buslogic_info(struct Scsi_Host *shpnt)
 {
-    return "BusLogic SCSI driver version " BUSLOGIC_VERSION;
+    return "BusLogic SCSI driver " BUSLOGIC_VERSION;
 }
 
 /* A "high" level interrupt handler. */
@@ -613,7 +613,7 @@
 			target, *cmd, i, bufflen);
 	buslogic_stat(scpnt->host->io_port);
 	buslogic_printk("buslogic_queuecommand: dumping scsi cmd:");
-	for (i = 0; i < (COMMAND_SIZE(*cmd)); i++)
+	for (i = 0; i < scpnt->cmd_len; i++)
 	    printk(" %02X", cmd[i]);
 	printk("\n");
 	if (*cmd == WRITE_10 || *cmd == WRITE_6)
@@ -665,7 +665,7 @@
 
     memset(&ccb[mbo], 0, sizeof (struct ccb));
 
-    ccb[mbo].cdblen = COMMAND_SIZE(*cmd);	/* SCSI Command Descriptor
+    ccb[mbo].cdblen = scpnt->cmd_len;		/* SCSI Command Descriptor
 						   Block Length */
 
     direction = 0;
@@ -1229,13 +1229,8 @@
 #endif
 	    /* Have to keep cmd_per_lun at 1 for ISA machines otherwise lots
 	       of memory gets sucked up for bounce buffers.  */
-	    /* ??? Unfortunately, cmd_per_lun is only in the
-	       Scsi_Host_Template structure, not the Scsi_Host structure.
-	       Therefore, this could cause high memory consumption if a system
-	       has multiple BusLogic adapters which are a mix of ISA and
-	       non-ISA. */
-	    if (!shpnt->unchecked_isa_dma)
-		shpnt->hostt->cmd_per_lun = BUSLOGIC_NONISA_CMDLUN;
+	    shpnt->cmd_per_lun
+		= (shpnt->unchecked_isa_dma ? 1 : BUSLOGIC_CMDLUN);
 	    shpnt->sg_tablesize = max_sg;
 	    if (shpnt->sg_tablesize > BUSLOGIC_MAX_SG)
 		shpnt->sg_tablesize = BUSLOGIC_MAX_SG;
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/buslogic.h linux/drivers/scsi/buslogic.h
--- v1.1.68/linux/drivers/scsi/buslogic.h	Tue Nov 29 18:51:26 1994
+++ linux/drivers/scsi/buslogic.h	Wed Nov 30 22:08:19 1994
@@ -15,22 +15,14 @@
 int buslogic_detect(Scsi_Host_Template *);
 int buslogic_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int buslogic_abort(Scsi_Cmnd *);
-const char *buslogic_info(void);
+const char *buslogic_info(struct Scsi_Host *);
 int buslogic_reset(Scsi_Cmnd *);
 int buslogic_biosparam(Disk *, int, int *);
 
-#define BUSLOGIC_CMDLUN 1	/* Do not set this too high.  It sucks
-				   up lots of memory on ISA machines
-				   with > 16MB because of the huge number of
-				   bounce buffers that need to be allocated.
-				   For boards that use non-ISA bus, we can
-				   bump this in the board detect routine.  
-							10/8/94 ERY */
-
-#define BUSLOGIC { NULL,			\
+#define BUSLOGIC { NULL, NULL,			\
 		   "BusLogic",			\
 		   buslogic_detect,		\
-		   NULL,			\
+		   0,	/* no release func */	\
 		   buslogic_info,		\
 		   0,	/* no command func */	\
 		   buslogic_queuecommand,	\
@@ -41,7 +33,7 @@
 		   0,	/* set by driver */	\
 		   0,	/* set by driver */	\
 		   0,	/* set by driver */	\
-		   BUSLOGIC_CMDLUN,		\
+		   0,	/* set by driver */	\
 		   0,				\
 		   0,	/* set by driver */	\
 		   ENABLE_CLUSTERING		\
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v1.1.68/linux/drivers/scsi/eata.c	Tue Nov 29 18:51:26 1994
+++ linux/drivers/scsi/eata.c	Wed Nov 30 22:03:48 1994
@@ -1,9 +1,13 @@
 /*
  *      eata.c - Low-level SCSI driver for EISA EATA SCSI controllers.
  *
+ *      30 Nov 1994 rev. 1.09 for linux 1.1.68
+ *          Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
+ *          Added optional support for using a single board at a time.
+ *
  *      18 Nov 1994 rev. 1.08 for linux 1.1.64
- *                  Forces sg_tablesize = 64 and can_queue = 64 if these
- *                  values are not correctly detected (DPT PM2012).
+ *          Forces sg_tablesize = 64 and can_queue = 64 if these
+ *          values are not correctly detected (DPT PM2012).
  *
  *      14 Nov 1994 rev. 1.07 for linux 1.1.63  Final BETA release.
  *      04 Aug 1994 rev. 1.00 for linux 1.1.39  First BETA release.
@@ -56,6 +60,7 @@
 #define NO_DEBUG_DETECT
 #define NO_DEBUG_INTERRUPT
 #define NO_DEBUG_STATISTICS
+#define NO_SINGLE_HOST_OPERATIONS
 
 #define MAX_TARGET 8
 #define MAX_IRQ 16
@@ -303,7 +308,7 @@
    sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
    sh[j]->this_id = (ushort) ntohl(info.host_addr);
    sh[j]->can_queue = (ushort) ntohs(info.queue_size);
-   sh[j]->hostt->cmd_per_lun = MAX_CMD_PER_LUN;
+   sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
    sh[j]->unchecked_isa_dma = FALSE;
    memset(HD(j), 0, sizeof(struct hostdata));
    HD(j)->board_number = j;
@@ -313,7 +318,7 @@
    printk("%s: SCSI ID %d, PORT 0x%03x, IRQ %u, SG %d, "\
           "Mbox %d, CmdLun %d.\n", BN(j), sh[j]->this_id, 
            sh[j]->io_port, sh[j]->irq, sh[j]->sg_tablesize, 
-           sh[j]->can_queue, sh[j]->hostt->cmd_per_lun);
+           sh[j]->can_queue, sh[j]->cmd_per_lun);
 
    /* DPT PM2012 does not allow to detect sg_tablesize correctly */
    if (sh[j]->sg_tablesize > MAX_SGLIST || sh[j]->sg_tablesize < 2) {
@@ -363,6 +368,16 @@
       port_base++;
       }
 
+#if defined (SINGLE_HOST_OPERATIONS)
+   /* Create a circular linked list among the detected boards. */
+   if (j > 1) {
+
+      for (k = 0; k < (j - 1); k++) sh[k]->block = sh[k + 1];
+
+      sh[j - 1]->block = sh[0];
+      }
+#endif
+
    restore_flags(flags);
    return j;
 }
@@ -764,7 +779,7 @@
    
                   /* If there was a bus reset, redo operation on each target */
                   else if (spp->target_status == CONDITION_GOOD
-                                        && SCpnt->device->type != TYPE_TAPE
+                                        && SCpnt->device->type == TYPE_DISK
                                         && HD(j)->target_reset[SCpnt->target])
                      status = DID_BUS_BUSY << 16;
                   else
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v1.1.68/linux/drivers/scsi/eata.h	Tue Nov 29 18:51:26 1994
+++ linux/drivers/scsi/eata.h	Wed Nov 30 22:03:48 1994
@@ -5,7 +5,7 @@
 #ifndef _EISA_EATA_H
 #define _EISA_EATA_H
 
-#define EATA_VERSION "1.08.00"
+#define EATA_VERSION "1.09.01"
 
 int eata_detect(Scsi_Host_Template *);
 int eata_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
@@ -14,7 +14,7 @@
 int eata_bios_param(Disk *, int, int*);
 
 #define EATA {  NULL, /* Ptr for modules */                    \
-		  NULL, /* usage count for modules */	       \
+                NULL, /* usage count for modules */	       \
                 "EISA EATA 2.0A rev. " EATA_VERSION " by "     \
                 "Dario_Ballabio@milano.europe.dg.com.",        \
                 eata_detect,	        	               \
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.1.68/linux/drivers/scsi/hosts.c	Tue Nov 29 18:51:26 1994
+++ linux/drivers/scsi/hosts.c	Wed Nov 30 22:03:48 1994
@@ -247,6 +247,7 @@
 	retval->this_id = tpnt->this_id;
 	retval->can_queue = tpnt->can_queue;
 	retval->sg_tablesize = tpnt->sg_tablesize;
+	retval->cmd_per_lun = tpnt->cmd_per_lun;
 	retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
 
 	if(!scsi_hostlist)
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v1.1.68/linux/drivers/scsi/hosts.h	Tue Nov 29 18:51:26 1994
+++ linux/drivers/scsi/hosts.h	Wed Nov 30 22:03:48 1994
@@ -228,7 +228,7 @@
 	be two Scsi_Host entries, but only 1 Scsi_Host_Template entries.
 */
 
-#define SCSI_HOST_BLOCK 0x800
+#define SCSI_HOST_BLOCK 0x80
 
 struct Scsi_Host
 	{
@@ -259,6 +259,7 @@
 		
 		int this_id;
 		int can_queue;
+		short cmd_per_lun;
 		short unsigned int sg_tablesize;
 		unsigned unchecked_isa_dma:1;
 		/*
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.1.68/linux/drivers/scsi/scsi.c	Tue Nov 29 18:51:27 1994
+++ linux/drivers/scsi/scsi.c	Wed Nov 30 23:49:08 1994
@@ -150,6 +150,7 @@
 				     * controller, which causes SCSI code to reset bus.*/
    {"SEAGATE", "ST296","921"},   /* Responds to all lun */
    {"SONY","CD-ROM CDU-541","4.3d"},
+   {"SONY","CD-ROM CDU-55S","1.0i"},
    {"TANDBERG","TDC 3600","U07"},  /* Locks up if polled for lun != 0 */
    {"TEAC","CD-ROM","1.06"},	/* causes failed REQUEST SENSE on lun 1 for seagate
 				 * controller, which causes SCSI code to reset bus.*/
@@ -898,7 +899,7 @@
 	      if (host->block) {
 		struct Scsi_Host * block;
 		for(block = host->block; block != host; block = block->block)
-		  block->host_busy |= ~SCSI_HOST_BLOCK;
+		  block->host_busy |= SCSI_HOST_BLOCK;
 	      }
 	      sti();
 	      break;
@@ -1810,7 +1811,7 @@
 	  for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
 	    if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
 	  if(SDpnt->type != -1){
-	    for(j=0;j<SDpnt->host->hostt->cmd_per_lun;j++){
+	    for(j=0;j<SDpnt->host->cmd_per_lun;j++){
 	      SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd));
 	      SCpnt->host = SDpnt->host;
 	      SCpnt->device = SDpnt;
@@ -1843,13 +1844,13 @@
 	  if(SDpnt->type != TYPE_TAPE)
 	    dma_sectors += ((host->sg_tablesize * 
 			     sizeof(struct scatterlist) + 511) >> 9) * 
-			       host->hostt->cmd_per_lun;
+			       host->cmd_per_lun;
 	  
 	  if(host->unchecked_isa_dma &&
 	     memory_end - 1 > ISA_DMA_THRESHOLD &&
 	     SDpnt->type != TYPE_TAPE) {
 	    dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
-	      host->hostt->cmd_per_lun;
+	      host->cmd_per_lun;
 	    need_isa_buffer++;
 	  };
 	};
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v1.1.68/linux/drivers/scsi/scsi_ioctl.c	Tue Nov 29 18:51:27 1994
+++ linux/drivers/scsi/scsi_ioctl.c	Wed Nov 30 22:03:48 1994
@@ -168,6 +168,7 @@
 	  if (buf_needed > MAX_BUF) buf_needed = MAX_BUF;
 	  buf = (char *) scsi_malloc(buf_needed);
 	  if (!buf) return -ENOMEM;
+	  memset(buf, 0, buf_needed);
 	} else
 	  buf = NULL;
 
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.1.68/linux/drivers/scsi/seagate.c	Tue Nov 29 18:51:27 1994
+++ linux/drivers/scsi/seagate.c	Wed Nov 30 17:17:39 1994
@@ -328,13 +328,14 @@
 	} /* (! controller_type) */
  
 	tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+	tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
 
 	if (base_address)
 		{
 		st0x_cr_sr =(void *) (((unsigned char *) base_address) + (controller_type == SEAGATE ? 0x1a00 : 0x1c00)); 
 		st0x_dr = (void *) (((unsigned char *) base_address ) + (controller_type == SEAGATE ? 0x1c00 : 0x1e00));
 #ifdef DEBUG
-		printk("ST0x detected. Base address = %x, cr = %x, dr = %x\n", base_address, st0x_cr_sr, st0x_dr);
+		printk("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);
 #endif
 /*
  *	At all times, we will use IRQ 5.  Should also check for IRQ3 if we 
@@ -382,8 +383,8 @@
 #ifdef LINKED
 " LINKED"
 #endif
-              "\n", hostno, (controller_type == SEAGATE) ? "seagate" : 
-              "FD TMC-8xx", irq, base_address);
+              "\n", hostno, (controller_type == SEAGATE) ? ST0X_ID_STR : 
+              FD_ID_STR, irq, base_address);
         return buffer;
 }
 
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/seagate.h linux/drivers/scsi/seagate.h
--- v1.1.68/linux/drivers/scsi/seagate.h	Tue Nov 29 18:51:27 1994
+++ linux/drivers/scsi/seagate.h	Wed Nov 30 17:17:39 1994
@@ -129,6 +129,8 @@
 #define SEAGATE 1	/* these determine the type of the controller */
 #define FD	2
 
+#define ST0X_ID_STR	"Seagate ST-01/ST-02"
+#define FD_ID_STR	"TMC-8XX/TMC-950"
 
 #endif
 
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.1.68/linux/drivers/scsi/sg.c	Tue Nov 29 18:51:27 1994
+++ linux/drivers/scsi/sg.c	Wed Nov 30 22:03:48 1994
@@ -274,7 +274,7 @@
   /* now issue command */
   SCpnt->request.dev=dev;
   SCpnt->sense_buffer[0]=0;
-  size=COMMAND_SIZE(get_fs_byte(buf));
+  opcode = get_fs_byte(buf);
   size=COMMAND_SIZE(opcode);
   if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
   SCpnt->cmd_len = size;
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.1.68/linux/drivers/scsi/sr.c	Tue Nov 29 18:51:27 1994
+++ linux/drivers/scsi/sr.c	Wed Nov 30 15:11:07 1994
@@ -326,7 +326,6 @@
       frame = (unsigned long)buffer[17]/16*10 + (unsigned long)buffer[17]%16;
       sector = min*60*75 + sec*75 + frame;
       if (sector) {
-      	sector -= CD_BLOCK_OFFSET;
 	printk("sr_photocd: multisession PhotoCD detected\n");
       }
     }
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.1.68/linux/drivers/scsi/st.c	Wed Nov  9 14:56:34 1994
+++ linux/drivers/scsi/st.c	Wed Nov 30 22:01:07 1994
@@ -11,7 +11,7 @@
   Copyright 1992, 1993, 1994 Kai Makisara
 		 email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
 
-  Last modified: Tue Oct 25 19:37:33 1994 by root@kai.home
+  Last modified: Wed Nov 30 21:09:53 1994 by root@kai.home
 */
 
 #include <linux/fs.h>
@@ -348,6 +348,9 @@
   STp = &(scsi_tapes[dev]);
   STbuffer = STp->buffer;
 
+  if (STp->ready != ST_READY)
+    return 0;
+
   if (STp->rw == ST_WRITING)  /* Writing */
     return flush_write_buffer(dev);
 
@@ -415,6 +418,7 @@
 
     STp->dirty = 0;
     STp->rw = ST_IDLE;
+    STp->ready = ST_READY;
     if (STp->eof != ST_EOD)  /* Save EOD across opens */
       STp->eof = ST_NOEOF;
     STp->eof_hit = 0;
@@ -459,14 +463,21 @@
 	  (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
         (STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
 	printk("st%d: No tape.\n", dev);
+	STp->ready = ST_NO_TAPE;
       } else {
 	printk("st%d: Error %x.\n", dev, SCpnt->result);
         (STp->mt_status)->mt_fileno = STp->drv_block = (-1);
+	STp->ready = ST_NOT_READY;
       }
-      (STp->buffer)->in_use = 0;
-      STp->in_use = 0;
       SCpnt->request.dev = -1;  /* Mark as not busy */
-      return (-EIO);
+      (STp->buffer)->in_use = 0;
+      STp->buffer = NULL;
+      STp->density = 0;   /* Clear the errorneus "residue" */
+      STp->write_prot = 0;
+      STp->block_size = 0;
+      STp->eof = ST_NOEOF;
+      (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+      return 0;
     }
 
     SCpnt->sense_buffer[0]=0;
@@ -533,10 +544,10 @@
 	(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
 #ifdef DEBUG
       if (debugging)
-	printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n",
+	printk("st%d: Density %x, tape length: %x, drv buffer: %d\n",
 	       dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
 	       (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
-	       STp->block_size, STp->drv_buffer);
+	       STp->drv_buffer);
 #endif
       if (STp->block_size > st_buffer_size) {
 	printk("st%d: Blocksize %d too large for buffer.\n", dev,
@@ -651,7 +662,8 @@
     if (rewind)
       st_int_ioctl(inode, filp, MTREW, 1);
 
-    (STp->buffer)->in_use = 0;
+    if (STp->buffer != NULL)
+      (STp->buffer)->in_use = 0;
     STp->in_use = 0;
 
     return;
@@ -672,6 +684,8 @@
 
     dev = MINOR(inode->i_rdev) & 127;
     STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
 #ifdef DEBUG
     if (!STp->in_use) {
       printk("st%d: Incorrect device.\n", dev);
@@ -884,6 +898,7 @@
     else
       SCpnt->request.dev = -1;  /* Mark as not busy */
 
+    STp->at_sm &= (total != 0);
     return( total);
 }   
 
@@ -901,6 +916,8 @@
 
     dev = MINOR(inode->i_rdev) & 127;
     STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
 #ifdef DEBUG
     if (!STp->in_use) {
       printk("st%d: Incorrect device.\n", dev);
@@ -930,8 +947,8 @@
 	     (STp->buffer)->buffer_bytes);
 #endif
     if (((STp->buffer)->buffer_bytes == 0) &&
-	STp->eof == ST_EOM_OK)  /* EOM or Blank Check */
-      return (-EIO);
+	(STp->eof == ST_EOM_OK || STp->eof == ST_EOD))
+      return (-EIO);  /* EOM or Blank Check */
 
     STp->rw = ST_READING;
 
@@ -975,6 +992,7 @@
 
 	(STp->buffer)->read_pointer = 0;
 	STp->eof_hit = 0;
+	STp->at_sm = 0;
 
 	if ((STp->buffer)->last_result_fatal) {
 #ifdef DEBUG
@@ -1180,12 +1198,15 @@
    unsigned char cmd[10];
    Scsi_Cmnd * SCpnt;
    Scsi_Tape * STp;
-   int fileno, blkno, undone, datalen;
+   int fileno, blkno, at_sm, undone, datalen;
 
    dev = dev & 127;
    STp = &(scsi_tapes[dev]);
+   if (STp->ready != ST_READY)
+     return (-EIO);
    fileno = (STp->mt_status)->mt_fileno ;
    blkno = STp->drv_block;
+   at_sm = STp->at_sm;
 
    memset(cmd, 0, 10);
    datalen = 0;
@@ -1204,6 +1225,7 @@
 #endif
        fileno += arg;
        blkno = 0;
+       at_sm &= (arg != 0);
        break; 
      case MTBSF:
      case MTBSFM:
@@ -1223,6 +1245,7 @@
 #endif
        fileno -= arg;
        blkno = (-1);  /* We can't know the block number */
+       at_sm &= (arg != 0);
        break; 
       case MTFSR:
        cmd[0] = SPACE;
@@ -1237,6 +1260,7 @@
 #endif
        if (blkno >= 0)
 	 blkno += arg;
+       at_sm &= (arg != 0);
        break; 
      case MTBSR:
        cmd[0] = SPACE;
@@ -1255,6 +1279,7 @@
 #endif
        if (blkno >= 0)
 	 blkno -= arg;
+       at_sm &= (arg != 0);
        break; 
       case MTFSS:
        cmd[0] = SPACE;
@@ -1267,8 +1292,10 @@
 	 printk("st%d: Spacing tape forward %d setmarks.\n", dev,
 		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
 #endif
-       if (arg != 0)
+       if (arg != 0) {
 	 blkno = fileno = (-1);
+	 at_sm = 1;
+       }
        break; 
      case MTBSS:
        cmd[0] = SPACE;
@@ -1285,8 +1312,10 @@
 	 printk("st%d: Spacing tape backward %ld setmarks.\n", dev, (-ltmp));
        }
 #endif
-       if (arg != 0)
+       if (arg != 0) {
 	 blkno = fileno = (-1);
+	 at_sm = 1;
+       }
        break; 
      case MTWEOF:
      case MTWSM:
@@ -1311,6 +1340,7 @@
 #endif
        fileno += arg;
        blkno = 0;
+       at_sm = (cmd_in == MTWSM);
        break; 
      case MTREW:
        cmd[0] = REZERO_UNIT;
@@ -1322,7 +1352,7 @@
        if (debugging)
 	 printk("st%d: Rewinding tape.\n", dev);
 #endif
-       fileno = blkno = 0 ;
+       fileno = blkno = at_sm = 0 ;
        break; 
      case MTOFFL:
        cmd[0] = START_STOP;
@@ -1334,7 +1364,7 @@
        if (debugging)
 	 printk("st%d: Unloading tape.\n", dev);
 #endif
-       fileno = blkno = 0 ;
+       fileno = blkno = at_sm = 0 ;
        break; 
      case MTNOP:
 #ifdef DEBUG
@@ -1354,7 +1384,7 @@
        if (debugging)
 	 printk("st%d: Retensioning tape.\n", dev);
 #endif
-       fileno = blkno = 0 ;
+       fileno = blkno = at_sm = 0;
        break; 
      case MTEOM:
        /* space to the end of tape */
@@ -1371,6 +1401,7 @@
 	 printk("st%d: Spacing to end of recorded medium.\n", dev);
 #endif
        blkno = (-1);
+       at_sm = 0;
        break; 
      case MTERASE:
        if (STp->write_prot)
@@ -1381,7 +1412,7 @@
        if (debugging)
 	 printk("st%d: Erasing tape.\n", dev);
 #endif
-       fileno = blkno = 0 ;
+       fileno = blkno = at_sm = 0 ;
        break;
      case MTSEEK:
        if ((STp->device)->scsi_level < SCSI_2) {
@@ -1408,6 +1439,7 @@
 	 printk("st%d: Seeking tape to block %ld.\n", dev, arg);
 #endif
        fileno = blkno = (-1);
+       at_sm = 0;
        break;
      case MTSETBLK:  /* Set block length */
      case MTSETDENSITY: /* Set tape density */
@@ -1481,9 +1513,12 @@
      if (cmd_in != MTSEEK) {
        STp->drv_block = blkno;
        (STp->mt_status)->mt_fileno = fileno;
+       STp->at_sm = at_sm;
      }
-     else
+     else {
        STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
+       STp->at_sm = 0;
+     }
      if (cmd_in == MTFSF)
        STp->moves_after_eof = 0;
      else
@@ -1642,6 +1677,12 @@
        (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
      else if (STp->density == 3)
        (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+     if (STp->ready == ST_READY)
+       (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+     if (STp->ready == ST_NO_TAPE)
+       (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+     if (STp->at_sm)
+       (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
 
      memcpy_tofs((char *)arg, (char *)(STp->mt_status),
 		 sizeof(struct mtget));
@@ -1650,6 +1691,8 @@
      return 0;
    }
    else if (cmd == (MTIOCPOS & IOCCMD_MASK)) {
+     if (STp->ready != ST_READY)
+       return (-EIO);
 #ifdef DEBUG
      if (debugging)
        printk("st%d: get tape position.\n", dev);
@@ -1712,8 +1755,10 @@
      memcpy_tofs((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
      return result;
    }
-   else
+   else if (STp->ready == ST_READY)
      return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+   else
+     return (-EIO);
 }
 
 
@@ -1829,6 +1874,7 @@
     STp->write_threshold = st_write_threshold;
     STp->drv_block = 0;
     STp->moves_after_eof = 1;
+    STp->at_sm = 0;
     STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget));
     /* Initialize status */
     memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v1.1.68/linux/drivers/scsi/st.h	Wed Nov  9 14:56:34 1994
+++ linux/drivers/scsi/st.h	Wed Nov 30 22:01:07 1994
@@ -27,6 +27,7 @@
   Scsi_Device* device;
   unsigned char dirty;
   unsigned char rw;
+  unsigned char ready;
   unsigned char eof;
   unsigned char write_prot;
   unsigned char drv_write_prot;
@@ -46,6 +47,7 @@
   int recover_count;
   int drv_block;	/* The block where the drive head is */
   unsigned char moves_after_eof;
+  unsigned char at_sm;
   struct mtget * mt_status;
   Scsi_Cmnd SCpnt;
 } Scsi_Tape;
@@ -61,6 +63,11 @@
 #define	ST_IDLE		0
 #define	ST_READING	1
 #define	ST_WRITING	2
+
+/* Values of ready state */
+#define ST_READY	0
+#define ST_NOT_READY	1
+#define ST_NO_TAPE	2
 
 /* Positioning SCSI-commands for Tandberg, etc. drives */
 #define	QFA_REQUEST_BLOCK	0x02
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v1.1.68/linux/drivers/scsi/u14-34f.c	Tue Nov 29 18:51:28 1994
+++ linux/drivers/scsi/u14-34f.c	Wed Nov 30 22:03:48 1994
@@ -1,6 +1,10 @@
 /*
  *      u14-34f.c - Low-level SCSI driver for UltraStor 14F/34F
  *
+ *      30 Nov 1994 rev. 1.09 for linux 1.1.68
+ *          Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
+ *          Added optional support for using a single board at a time.
+ *
  *      14 Nov 1994 rev. 1.10 for linux 1.1.63
  *
  *      28 Oct 1994 rev. 1.09 for linux 1.1.58  Final BETA release.
@@ -140,6 +144,7 @@
 #define NO_DEBUG_DETECT
 #define NO_DEBUG_INTERRUPT
 #define NO_DEBUG_STATISTICS
+#define SINGLE_HOST_OPERATIONS
 
 #define MAX_TARGET 8
 #define MAX_IRQ 16
@@ -327,7 +332,7 @@
    sh[j]->irq = irq;
    sh[j]->this_id = config_2.ha_scsi_id;
    sh[j]->can_queue = MAX_MAILBOXES;
-   sh[j]->hostt->cmd_per_lun = MAX_CMD_PER_LUN;
+   sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
    sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
    lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
 
@@ -381,7 +386,7 @@
           "Mbox %d, CmdLun %d, C%d.\n", BN(j), sh[j]->io_port, 
           (int)sh[j]->base, sh[j]->irq, 
           sh[j]->dma_channel, sh[j]->sg_tablesize, 
-          sh[j]->can_queue, sh[j]->hostt->cmd_per_lun,
+          sh[j]->can_queue, sh[j]->cmd_per_lun,
           sh[j]->hostt->use_clustering);
    return TRUE;
 }
@@ -412,6 +417,16 @@
       port_base++;
       }
 
+#if defined (SINGLE_HOST_OPERATIONS)
+   /* Create a circular linked list among the detected boards. */
+   if (j > 1) {
+
+      for (k = 0; k < (j - 1); k++) sh[k]->block = sh[k + 1];
+
+      sh[j - 1]->block = sh[0];
+      }
+#endif
+
    restore_flags(flags);
    return j;
 }
@@ -784,7 +799,7 @@
 
                /* If there was a bus reset, redo operation on each target */
                else if (spp->target_status == CONDITION_GOOD
-                                     && SCpnt->device->type != TYPE_TAPE
+                                     && SCpnt->device->type == TYPE_DISK
                                      && HD(j)->target_reset[SCpnt->target])
                   status = DID_BUS_BUSY << 16;
                else
diff -u --recursive --new-file v1.1.68/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v1.1.68/linux/drivers/scsi/u14-34f.h	Tue Nov 29 18:51:28 1994
+++ linux/drivers/scsi/u14-34f.h	Wed Nov 30 22:03:48 1994
@@ -10,13 +10,13 @@
 int u14_34f_reset(Scsi_Cmnd *);
 int u14_34f_biosparam(Disk *, int, int *);
 
-#define U14_34F_VERSION "1.10.01"
+#define U14_34F_VERSION "1.11.01"
 
 #define ULTRASTOR_14_34F {                                            \
                 NULL,                                                 \
                 NULL,                                                 \
                 "UltraStor 14F/34F rev. " U14_34F_VERSION " by "      \
-                "Dario_Ballabio@milano.europe.dg.com.",\
+                "Dario_Ballabio@milano.europe.dg.com.",               \
                 u14_34f_detect,                                       \
                 NULL,                                                 \
                 NULL,		                                      \
diff -u --recursive --new-file v1.1.68/linux/fs/Makefile linux/fs/Makefile
--- v1.1.68/linux/fs/Makefile	Tue Nov 22 15:41:13 1994
+++ linux/fs/Makefile	Wed Nov 30 22:03:47 1994
@@ -28,6 +28,8 @@
 endif
 ifdef CONFIG_ISO9660_FS
 FS_SUBDIRS := $(FS_SUBDIRS) isofs
+else
+MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) isofs
 endif
 ifdef CONFIG_NFS_FS
 FS_SUBDIRS := $(FS_SUBDIRS) nfs
@@ -50,7 +52,7 @@
 ifdef CONFIG_BINFMT_ELF
 BINFMTS := $(BINFMTS) binfmt_elf.o
 else
-MODULES := $(MODULES) binfmt_elf.o
+MODULE_OBJS := $(MODULE_OBJS) binfmt_elf.o
 endif
 
 .c.s:
diff -u --recursive --new-file v1.1.68/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v1.1.68/linux/fs/binfmt_elf.c	Tue Nov 22 15:41:13 1994
+++ linux/fs/binfmt_elf.c	Wed Nov 30 17:17:39 1994
@@ -93,7 +93,6 @@
 		mpnt->vm_flags = VM_GROWSDOWN;
 #  endif
 #endif
-		mpnt->vm_share = NULL;
 		mpnt->vm_inode = NULL;
 		mpnt->vm_offset = 0;
 		mpnt->vm_ops = NULL;
diff -u --recursive --new-file v1.1.68/linux/fs/buffer.c linux/fs/buffer.c
--- v1.1.68/linux/fs/buffer.c	Tue Nov 15 15:35:40 1994
+++ linux/fs/buffer.c	Wed Nov 30 21:47:39 1994
@@ -979,6 +979,22 @@
 	}
 }
 
+/*
+ * This actually gets enough info to try to align the stuff,
+ * but we don't bother yet.. We'll have to check that nobody
+ * else uses the buffers etc.
+ *
+ * "address" points to the new page we can use to move things
+ * around..
+ */
+static unsigned long try_to_align(struct buffer_head ** bh, int nrbuf,
+	unsigned long address)
+{
+	while (nrbuf-- > 0)
+		brelse(bh[nrbuf]);
+	return 0;
+}
+
 static unsigned long check_aligned(struct buffer_head * first, unsigned long address,
 	dev_t dev, int *b, int size)
 {
@@ -987,15 +1003,13 @@
 	unsigned long offset;
 	int block;
 	int nrbuf;
+	int aligned = 1;
 
-	page = (unsigned long) first->b_data;
-	if (page & ~PAGE_MASK) {
-		brelse(first);
-		return 0;
-	}
-	mem_map[MAP_NR(page)]++;
 	bh[0] = first;
 	nrbuf = 1;
+	page = (unsigned long) first->b_data;
+	if (page & ~PAGE_MASK)
+		aligned = 0;
 	for (offset = size ; offset < PAGE_SIZE ; offset += size) {
 		block = *++b;
 		if (!block)
@@ -1005,8 +1019,11 @@
 			goto no_go;
 		bh[nrbuf++] = first;
 		if (page+offset != (unsigned long) first->b_data)
-			goto no_go;
+			aligned = 0;
 	}
+	if (!aligned)
+		return try_to_align(bh, nrbuf, address);
+	mem_map[MAP_NR(page)]++;
 	read_buffers(bh,nrbuf);		/* make sure they are actually read correctly */
 	while (nrbuf-- > 0)
 		brelse(bh[nrbuf]);
@@ -1016,7 +1033,6 @@
 no_go:
 	while (nrbuf-- > 0)
 		brelse(bh[nrbuf]);
-	free_page(page);
 	return 0;
 }
 
diff -u --recursive --new-file v1.1.68/linux/fs/exec.c linux/fs/exec.c
--- v1.1.68/linux/fs/exec.c	Mon Nov 28 14:21:45 1994
+++ linux/fs/exec.c	Wed Nov 30 17:17:39 1994
@@ -326,7 +326,6 @@
 		mpnt->vm_end = TASK_SIZE;
 		mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY;
 		mpnt->vm_flags = VM_STACK_FLAGS;
-		mpnt->vm_share = NULL;
 		mpnt->vm_ops = NULL;
 		mpnt->vm_offset = 0;
 		mpnt->vm_inode = NULL;
@@ -545,6 +544,7 @@
 		mpnt1 = mpnt->vm_next;
 		if (mpnt->vm_ops && mpnt->vm_ops->close)
 			mpnt->vm_ops->close(mpnt);
+		remove_shared_vm_struct(mpnt);
 		if (mpnt->vm_inode)
 			iput(mpnt->vm_inode);
 		kfree(mpnt);
diff -u --recursive --new-file v1.1.68/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v1.1.68/linux/fs/ext2/balloc.c	Wed Nov  9 14:56:35 1994
+++ linux/fs/ext2/balloc.c	Wed Nov 30 21:47:39 1994
@@ -232,7 +232,7 @@
 	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 
 	mark_buffer_dirty(bh, 1);
-	if (sb->s_flags & MS_SYNC) {
+	if (sb->s_flags & MS_SYNCHRONOUS) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
@@ -441,7 +441,7 @@
 	j = tmp;
 
 	mark_buffer_dirty(bh, 1);
-	if (sb->s_flags & MS_SYNC) {
+	if (sb->s_flags & MS_SYNCHRONOUS) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
diff -u --recursive --new-file v1.1.68/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v1.1.68/linux/fs/ext2/ialloc.c	Wed Oct 26 09:42:21 1994
+++ linux/fs/ext2/ialloc.c	Wed Nov 30 21:47:39 1994
@@ -247,7 +247,7 @@
 		set_inode_dtime (inode, gdp);
 	}
 	mark_buffer_dirty(bh, 1);
-	if (sb->s_flags & MS_SYNC) {
+	if (sb->s_flags & MS_SYNCHRONOUS) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
@@ -414,7 +414,7 @@
 			goto repeat;
 		}
 		mark_buffer_dirty(bh, 1);
-		if (sb->s_flags & MS_SYNC) {
+		if (sb->s_flags & MS_SYNCHRONOUS) {
 			ll_rw_block (WRITE, 1, &bh);
 			wait_on_buffer (bh);
 		}
@@ -476,7 +476,7 @@
 	inode->u.ext2_i.i_block_group = i;
 	inode->i_op = NULL;
 	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
-		inode->i_flags |= MS_SYNC;
+		inode->i_flags |= MS_SYNCHRONOUS;
 	insert_inode_hash(inode);
 	inc_inode_version (inode, gdp, mode);
 
diff -u --recursive --new-file v1.1.68/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v1.1.68/linux/fs/ext2/inode.c	Wed Oct 26 09:42:21 1994
+++ linux/fs/ext2/inode.c	Wed Nov 30 21:47:39 1994
@@ -567,7 +567,7 @@
 	else if (S_ISFIFO(inode->i_mode))
 		init_fifo(inode);
 	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
-		inode->i_flags |= MS_SYNC;
+		inode->i_flags |= MS_SYNCHRONOUS;
 	if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL)
 		inode->i_flags |= S_APPEND;
 	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)
diff -u --recursive --new-file v1.1.68/linux/fs/inode.c linux/fs/inode.c
--- v1.1.68/linux/fs/inode.c	Tue Nov 15 15:35:40 1994
+++ linux/fs/inode.c	Wed Nov 30 17:17:39 1994
@@ -406,6 +406,11 @@
 		goto repeat;
 	}
 	inode->i_count--;
+	if (inode->i_mmap) {
+		printk("iput: inode %lu on device %d/%d still has mappings.\n",
+			inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+		inode->i_mmap = NULL;
+	}
 	nr_free_inodes++;
 	return;
 }
diff -u --recursive --new-file v1.1.68/linux/fs/isofs/Makefile linux/fs/isofs/Makefile
--- v1.1.68/linux/fs/isofs/Makefile	Wed Dec  1 14:44:15 1993
+++ linux/fs/isofs/Makefile	Wed Nov 30 22:03:47 1994
@@ -7,6 +7,10 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
+ifndef CONFIG_ISO9660_FS
+CFLAGS := $(CFLAGS) -DMODULE
+endif
+
 .c.s:
 	$(CC) $(CFLAGS) -S $<
 .c.o:
diff -u --recursive --new-file v1.1.68/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.1.68/linux/fs/isofs/inode.c	Fri Nov 18 15:22:10 1994
+++ linux/fs/isofs/inode.c	Wed Nov 30 22:03:47 1994
@@ -20,6 +20,11 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 
+#ifdef MODULE
+#include <linux/module.h>
+#include "../../tools/version.h"
+#endif
+
 #ifdef LEAK_CHECK
 static int check_malloc = 0;
 static int check_bread = 0;
@@ -35,6 +40,9 @@
 #endif
 	sb->s_dev = 0;
 	unlock_super(sb);
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
 	return;
 }
 
@@ -301,7 +309,12 @@
 		return NULL;
 	}
 
-	if(!check_disk_change(s->s_dev)) return s;
+	if(!check_disk_change(s->s_dev)) {
+#ifdef MODULE
+	  MOD_INC_USE_COUNT;
+#endif
+	  return s;
+	}
  out: /* Kick out for various error conditions */
 	brelse(bh);
 	s->s_dev = 0;
@@ -705,3 +718,30 @@
 }
 
 #endif
+
+#ifdef MODULE
+
+char kernel_version[] = UTS_RELEASE;
+
+static struct file_system_type iso9660_fs_type = {
+	isofs_read_super, "iso9660", 1, NULL
+};
+
+int init_module(void)
+{
+	register_filesystem(&iso9660_fs_type);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	if (MOD_IN_USE)
+		printk("ne: device busy, remove delayed\n");
+	else
+	{
+		unregister_filesystem(&iso9660_fs_type);
+	}
+}
+
+#endif
+
diff -u --recursive --new-file v1.1.68/linux/fs/locks.c linux/fs/locks.c
--- v1.1.68/linux/fs/locks.c	Tue Nov 29 18:51:28 1994
+++ linux/fs/locks.c	Wed Nov 30 10:01:43 1994
@@ -186,7 +186,7 @@
 				if (ret_val) return -EDEADLOCK;
 			}
 			dlock_wait = dlock_wait->next;
-		} while (dlock_wait != NULL);
+		} while (dlock_wait != fl->fl_wait);
 	}
 	return 0;
 }
@@ -473,14 +473,18 @@
 	if (tmp->fl_owner != NULL)
 		panic("alloc_lock: broken free list\n");
 
-	*tmp = *fl;
-
 	tmp->fl_next = *pos;	/* insert into file's list */
 	*pos = tmp;
 
 	tmp->fl_owner = current;	/* FIXME: needed? */
 	tmp->fl_fd = fd;		/* FIXME: needed? */
 	tmp->fl_wait = NULL;
+
+	tmp->fl_type = fl->fl_type;
+	tmp->fl_whence = fl->fl_whence;
+	tmp->fl_start = fl->fl_start;
+	tmp->fl_end = fl->fl_end;
+
 	return tmp;
 }
 
diff -u --recursive --new-file v1.1.68/linux/fs/nfs/mmap.c linux/fs/nfs/mmap.c
--- v1.1.68/linux/fs/nfs/mmap.c	Thu Oct  6 08:28:17 1994
+++ linux/fs/nfs/mmap.c	Wed Nov 30 21:47:39 1994
@@ -74,13 +74,18 @@
 	}
 	return page;
 }
+
 struct vm_operations_struct nfs_file_mmap = {
 	NULL,			/* open */
 	NULL,			/* close */
+	NULL,			/* unmap */
+	NULL,			/* protect */
+	NULL,			/* sync */
+	NULL,			/* advise */
 	nfs_file_mmap_nopage,	/* nopage */
 	NULL,			/* wppage */
-	NULL,			/* share */
-	NULL,			/* unmap */
+	NULL,			/* swapout */
+	NULL,			/* swapin */
 };
 
 
diff -u --recursive --new-file v1.1.68/linux/fs/proc/fd.c linux/fs/proc/fd.c
--- v1.1.68/linux/fs/proc/fd.c	Sat Aug  6 20:28:13 1994
+++ linux/fs/proc/fd.c	Tue Nov 29 21:51:58 1994
@@ -62,11 +62,10 @@
 	ino = dir->i_ino;
 	pid = ino >> 16;
 	ino &= 0x0000ffff;
-	ino -= 7;
 	if (!dir)
 		return -ENOENT;
 	sb = dir->i_sb;
-	if (!pid || ino || !S_ISDIR(dir->i_mode)) {
+	if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) {
 		iput(dir);
 		return -ENOENT;
 	}
@@ -76,7 +75,7 @@
 			*result = dir;
 			return 0;
 		}
-		if (!(*result = iget(sb,(pid << 16)+2))) {
+		if (!(*result = iget(sb,(pid << 16)+PROC_PID_INO))) {
 			iput(dir);
 			return -ENOENT;
 		}
@@ -108,7 +107,7 @@
 	if (fd >= NR_OPEN || !p->files->fd[fd] || !p->files->fd[fd]->f_inode)
 	  return -ENOENT;
 
-	ino = (pid << 16) + 0x100 + fd;
+	ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
 
 	if (!(*result = iget(sb,ino)))
 		return -ENOENT;
@@ -127,8 +126,7 @@
 	ino = inode->i_ino;
 	pid = ino >> 16;
 	ino &= 0x0000ffff;
-	ino -= 7;
-	if (ino)
+	if (ino != PROC_PID_FD)
 		return 0;
 	while (1) {
 		fd = filp->f_pos;
@@ -138,7 +136,7 @@
 			if (!fd)
 				fd = inode->i_ino;
 			else
-				fd = (inode->i_ino & 0xffff0000) | 2;
+				fd = (inode->i_ino & 0xffff0000) | PROC_PID_INO;
 			put_fs_long(fd, &dirent->d_ino);
 			put_fs_word(i, &dirent->d_reclen);
 			put_fs_byte(0, i+dirent->d_name);
@@ -165,7 +163,7 @@
 			i++;
 		}
 		j = i;
-		ino = (pid << 16) + 0x100 + fd;
+		ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
 
 		put_fs_long(ino, &dirent->d_ino);
 		put_fs_word(i, &dirent->d_reclen);
diff -u --recursive --new-file v1.1.68/linux/ibcs/binfmt_elf.c linux/ibcs/binfmt_elf.c
--- v1.1.68/linux/ibcs/binfmt_elf.c	Tue Aug  9 09:33:00 1994
+++ linux/ibcs/binfmt_elf.c	Wed Nov 30 17:17:39 1994
@@ -67,7 +67,6 @@
 		mpnt->vm_end = TASK_SIZE;
 		mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY;
 		mpnt->vm_flags = VM_STACK_FLAGS;
-		mpnt->vm_share = NULL;
 		mpnt->vm_ops = NULL;
 		mpnt->vm_inode = NULL;
 		mpnt->vm_offset = 0;
diff -u --recursive --new-file v1.1.68/linux/include/asm-i386/dma.h linux/include/asm-i386/dma.h
--- v1.1.68/linux/include/asm-i386/dma.h	Tue Nov 29 18:51:28 1994
+++ linux/include/asm-i386/dma.h	Thu Dec  1 12:40:03 1994
@@ -70,7 +70,7 @@
 #define MAX_DMA_CHANNELS	8
 
 /* The maximum address that we can perform a DMA transfer to on this platform */
-#define MAX_DMA_ADDRESS      0xffffff
+#define MAX_DMA_ADDRESS      0x1000000
 
 /* 8237 DMA controllers */
 #define IO_DMA1_BASE	0x00	/* 8 bit slave DMA, channels 0..3 */
diff -u --recursive --new-file v1.1.68/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.1.68/linux/include/linux/fs.h	Mon Nov 28 14:21:46 1994
+++ linux/include/linux/fs.h	Wed Nov 30 21:47:40 1994
@@ -63,12 +63,12 @@
 /*
  * These are the fs-independent mount-flags: up to 16 flags are supported
  */
-#define MS_RDONLY    1 /* mount read-only */
-#define MS_NOSUID    2 /* ignore suid and sgid bits */
-#define MS_NODEV     4 /* disallow access to device special files */
-#define MS_NOEXEC    8 /* disallow program execution */
-#define MS_SYNC     16 /* writes are synced at once */
-#define	MS_REMOUNT  32 /* alter flags of a mounted FS */
+#define MS_RDONLY	 1 /* mount read-only */
+#define MS_NOSUID	 2 /* ignore suid and sgid bits */
+#define MS_NODEV	 4 /* disallow access to device special files */
+#define MS_NOEXEC	 8 /* disallow program execution */
+#define MS_SYNCHRONOUS	16 /* writes are synced at once */
+#define MS_REMOUNT	32 /* alter flags of a mounted FS */
 
 #define S_APPEND    256 /* append-only file */
 #define S_IMMUTABLE 512 /* immutable file */
@@ -96,7 +96,7 @@
 #define IS_NOSUID(inode) ((inode)->i_flags & MS_NOSUID)
 #define IS_NODEV(inode) ((inode)->i_flags & MS_NODEV)
 #define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
-#define IS_SYNC(inode) ((inode)->i_flags & MS_SYNC)
+#define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
 
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
 #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
diff -u --recursive --new-file v1.1.68/linux/include/linux/if.h linux/include/linux/if.h
--- v1.1.68/linux/include/linux/if.h	Fri Jun 17 07:53:56 1994
+++ linux/include/linux/if.h	Wed Nov 30 21:53:47 1994
@@ -31,13 +31,15 @@
 #define	IFF_NOTRAILERS	0x20		/* avoid use of trailers	*/
 #define	IFF_RUNNING	0x40		/* resources allocated		*/
 #define	IFF_NOARP	0x80		/* no ARP protocol		*/
-#define	IFF_PROMISC	0x100		/* recve all packets		*/
-/* These are not yet used: */
-#define	IFF_ALLMULTI	0x200		/* recve all multicast packets	*/
+#define	IFF_PROMISC	0x100		/* receive all packets		*/
+/* Not supported */
+#define	IFF_ALLMULTI	0x200		/* receive all multicast packets*/
 
 #define IFF_MASTER	0x400		/* master of a load balancer 	*/
 #define IFF_SLAVE	0x800		/* slave of a load balancer	*/
 
+#define IFF_MULTICAST	0x1000		/* Supports multicast		*/
+
 /*
  * The ifaddr structure contains information about one address
  * of an interface.  They are maintained by the different address
@@ -45,15 +47,18 @@
  * and are linked together so all addresses for an interface can
  * be located.
  */
-struct ifaddr {
-  struct sockaddr	ifa_addr;	/* address of interface		*/
-  union {
-	struct sockaddr	ifu_broadaddr;
-	struct sockaddr	ifu_dstaddr;
-  } ifa_ifu;
-  struct iface		*ifa_ifp;	/* back-pointer to interface	*/
-  struct ifaddr		*ifa_next;	/* next address for interface	*/
+ 
+struct ifaddr 
+{
+	struct sockaddr	ifa_addr;	/* address of interface		*/
+	union {
+		struct sockaddr	ifu_broadaddr;
+		struct sockaddr	ifu_dstaddr;
+	} ifa_ifu;
+	struct iface		*ifa_ifp;	/* back-pointer to interface	*/
+	struct ifaddr		*ifa_next;	/* next address for interface	*/
 };
+
 #define	ifa_broadaddr	ifa_ifu.ifu_broadaddr	/* broadcast address	*/
 #define	ifa_dstaddr	ifa_ifu.ifu_dstaddr	/* other end of link	*/
 
@@ -67,7 +72,8 @@
  *	being very small might be worth keeping for clean configuration.
  */
 
-struct ifmap {
+struct ifmap 
+{
 	unsigned long mem_start;
 	unsigned long mem_end;
 	unsigned short base_addr; 
@@ -83,7 +89,9 @@
  * definitions which begin with ifr_name.  The
  * remainder may be interface specific.
  */
-struct ifreq {
+
+struct ifreq 
+{
 #define IFHWADDRLEN	6
 #define	IFNAMSIZ	16
 	union
@@ -127,9 +135,12 @@
  * for machine (useful for programs which
  * must know all networks accessible).
  */
-struct ifconf {
+
+struct ifconf 
+{
 	int	ifc_len;			/* size of buffer	*/
-	union {
+	union 
+	{
 		caddr_t	ifcu_buf;
 		struct	ifreq *ifcu_req;
 	} ifc_ifcu;
diff -u --recursive --new-file v1.1.68/linux/include/linux/in.h linux/include/linux/in.h
--- v1.1.68/linux/include/linux/in.h	Fri Mar 18 10:30:26 1994
+++ linux/include/linux/in.h	Wed Nov 30 21:53:47 1994
@@ -23,7 +23,7 @@
 enum {
   IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
   IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
-  IPPROTO_GGP = 2,		/* Gateway Protocol (deprecated)	*/
+  IPPROTO_IGMP = 2,		/* Internet Gateway Management Protocol */
   IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
   IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
   IPPROTO_PUP = 12,		/* PUP protocol				*/
@@ -40,6 +40,14 @@
 	unsigned long int	s_addr;
 };
 
+/* Request struct for multicast socket ops */
+
+struct ip_mreq 
+{
+	struct in_addr imr_multiaddr;	/* IP multicast address of group */
+	struct in_addr imr_interface;	/* local IP address of interface */
+};
+
 
 /* Structure describing an Internet (IP) socket address. */
 #define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
@@ -121,7 +129,7 @@
 extern unsigned long int	htonl(unsigned long int);
 extern unsigned short int	htons(unsigned short int);
 
-static __inline__ unsigned long int
+extern __inline__ unsigned long int
 __ntohl(unsigned long int x)
 {
 	__asm__("xchgb %b0,%h0\n\t"	/* swap lower bytes	*/
@@ -132,7 +140,7 @@
 	return x;
 }
 
-static __inline__ unsigned long int
+extern __inline__ unsigned long int
 __constant_ntohl(unsigned long int x)
 {
 	return (((x & 0x000000ffU) << 24) |
@@ -141,7 +149,7 @@
 		((x & 0xff000000U) >> 24));
 }
 
-static __inline__ unsigned short int
+extern __inline__ unsigned short int
 __ntohs(unsigned short int x)
 {
 	__asm__("xchgb %b0,%h0"		/* swap bytes		*/
@@ -150,7 +158,7 @@
 	return x;
 }
 
-static __inline__ unsigned short int
+extern __inline__ unsigned short int
 __constant_ntohs(unsigned short int x)
 {
 	return (((x & 0x00ff) << 8) |
diff -u --recursive --new-file v1.1.68/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.1.68/linux/include/linux/ip_fw.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/ip_fw.h	Wed Nov 30 21:53:47 1994
@@ -0,0 +1,121 @@
+/*
+ *	IP firewalling code. This is taken from 4.4BSD. Please note the 
+ *	copyright message below. As per the GPL it must be maintained
+ *	and the licenses thus do not conflict. While this port is subject
+ *	to the GPL I also place my modifications under the original 
+ *	license in recognition of the original copyright. 
+ *
+ *	Ported from BSD to Linux,
+ *		Alan Cox 22/Nov/1994.
+ *
+ *	All the real work was done by .....
+ */
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+/*
+ * 	Format of an IP firewall descriptor
+ *
+ * 	src, dst, src_mask, dst_mask are always stored in network byte order.
+ * 	flags and num_*_ports are stored in host byte order (of course).
+ * 	Port numbers are stored in HOST byte order.
+ */
+ 
+#ifndef _IP_FW_H
+#define _IP_FW_H
+
+struct ip_fw 
+{
+	struct ip_fw *next;			/* Next firewall on chain */
+	struct in_addr src, dst;		/* Source and destination IP addr */
+	struct in_addr src_mask, dst_mask;	/* Mask for src and dest IP addr */
+	unsigned short flags;			/* Flags word */
+	unsigned short n_src_p, n_dst_p;        /* # of src ports and # of dst ports */
+						/* in ports array (dst ports follow */
+    						/* src ports; max of 10 ports in all; */
+    						/* count of 0 means match all ports) */
+#define IP_FW_MAX_PORTS	10      		/* A reasonable maximum */
+	unsigned short ports[IP_FW_MAX_PORTS];  /* Array of port numbers to match */
+	unsigned long p_cnt,b_cnt;		/* Packet and byte counters */
+};
+
+/*
+ *	Values for "flags" field .
+ */
+
+#define IP_FW_F_ALL	0x00	/* This is a universal packet firewall*/
+#define IP_FW_F_TCP	0x01	/* This is a TCP packet firewall      */
+#define IP_FW_F_UDP	0x02	/* This is a UDP packet firewall      */
+#define IP_FW_F_ICMP	0x03	/* This is a ICMP packet firewall     */
+#define IP_FW_F_KIND	0x03	/* Mask to isolate firewall kind      */
+#define IP_FW_F_ACCEPT	0x04	/* This is an accept firewall (as     *
+				 *         opposed to a deny firewall)*
+				 *                                    */
+#define IP_FW_F_SRNG	0x08	/* The first two src ports are a min  *
+				 * and max range (stored in host byte *
+				 * order).                            *
+				 *                                    */
+#define IP_FW_F_DRNG	0x10	/* The first two dst ports are a min  *
+				 * and max range (stored in host byte *
+				 * order).                            *
+				 * (ports[0] <= port <= ports[1])     *
+				 *                                    */
+#define IP_FW_F_PRN	0x20	/* In verbose mode print this firewall*/
+#define IP_FW_F_BIDIR	0x40	/* For accounting-count two way       */
+#define IP_FW_F_MASK	0x7F	/* All possible flag bits mask        */
+
+/*    
+ *	New IP firewall options for [gs]etsockopt at the RAW IP level.
+ *	Unlike BSD Linux inherits IP options so you don't have to use
+ *	a raw socket for this. Instead we check rights in the calls.
+ */     
+
+#define IP_FW_BASE_CTL	64
+
+#define IP_FW_ADD_BLK (IP_FW_BASE_CTL)
+#define IP_FW_ADD_FWD (IP_FW_BASE_CTL+1)   
+#define IP_FW_CHK_BLK (IP_FW_BASE_CTL+2)
+#define IP_FW_CHK_FWD (IP_FW_BASE_CTL+3)
+#define IP_FW_DEL_BLK (IP_FW_BASE_CTL+4)
+#define IP_FW_DEL_FWD (IP_FW_BASE_CTL+5)
+#define IP_FW_FLUSH   (IP_FW_BASE_CTL+6)
+#define IP_FW_POLICY  (IP_FW_BASE_CTL+7) 
+
+#define IP_ACCT_ADD   (IP_FW_BASE_CTL+10)
+#define IP_ACCT_DEL   (IP_FW_BASE_CTL+11)
+#define IP_ACCT_FLUSH (IP_FW_BASE_CTL+12)
+#define IP_ACCT_ZERO  (IP_FW_BASE_CTL+13)
+
+
+/*
+ *	Main firewall chains definitions and global var's definitions.
+ */
+
+#ifdef __KERNEL__
+#ifdef CONFIG_IP_FIREWALL
+extern struct ip_fw *ip_fw_blk_chain;
+extern struct ip_fw *ip_fw_fwd_chain;
+extern int ip_fw_policy;
+extern int ip_fw_chk(struct iphdr *, struct ip_fw *);
+extern int ip_fw_ctl(int, void *, int);
+#endif
+#ifdef CONFIG_IP_ACCT
+extern struct ip_fw *ip_acct_chain;
+extern void ip_acct_cnt(struct iphdr *, struct ip_fw *, int);
+extern int ip_acct_ctl(int, void *, int);
+#endif
+#endif /* KERNEL */
+
+#endif /* _IP_FW_H */
diff -u --recursive --new-file v1.1.68/linux/include/linux/ipc.h linux/include/linux/ipc.h
--- v1.1.68/linux/include/linux/ipc.h	Thu Nov  3 17:53:30 1994
+++ linux/include/linux/ipc.h	Wed Nov 30 17:17:39 1994
@@ -58,6 +58,8 @@
 #define SHMGET 		23
 #define SHMCTL 		24
 
+#define IPCCALL(version,op)	((version)<<16 | (op))
+
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_IPC_H */
diff -u --recursive --new-file v1.1.68/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.1.68/linux/include/linux/mm.h	Tue Nov 29 18:51:28 1994
+++ linux/include/linux/mm.h	Wed Nov 30 22:42:04 1994
@@ -33,8 +33,14 @@
 	unsigned long vm_end;
 	unsigned short vm_page_prot;
 	unsigned short vm_flags;
-	struct vm_area_struct * vm_next;	/* linked list */
-	struct vm_area_struct * vm_share;	/* linked list */
+/* linked list of VM areas per task, sorted by address */
+	struct vm_area_struct * vm_next;
+/* for areas with inode, the circular list inode->i_mmap */
+/* for shm areas, the linked list of attaches */
+/* otherwise unused */
+	struct vm_area_struct * vm_next_share;
+	struct vm_area_struct * vm_prev_share;
+/* more */
 	struct vm_operations_struct * vm_ops;
 	unsigned long vm_offset;
 	struct inode * vm_inode;
@@ -64,21 +70,22 @@
 #define VM_STACK_FLAGS	0x0177
 
 /*
- * These are the virtual MM functions - opening of an area, closing it (needed to
- * keep files on disk up-to-date etc), pointer to the functions called when a
- * no-page or a wp-page exception occurs, and the function which decides on sharing
- * of pages between different processes.
+ * These are the virtual MM functions - opening of an area, closing and
+ * unmapping it (needed to keep files on disk up-to-date etc), pointer
+ * to the functions called when a no-page or a wp-page exception occurs. 
  */
 struct vm_operations_struct {
 	void (*open)(struct vm_area_struct * area);
 	void (*close)(struct vm_area_struct * area);
+	void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
+	void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
+	void (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
+	void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
 	unsigned long (*nopage)(struct vm_area_struct * area, unsigned long address,
 		unsigned long page, int error_code);
 	unsigned long (*wppage)(struct vm_area_struct * area, unsigned long address,
 		unsigned long page);
-	int (*share)(struct vm_area_struct * from, struct vm_area_struct * to, unsigned long address);
-	int (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
-	void (*swapout)(struct vm_area_struct *,  unsigned long *);
+	void (*swapout)(struct vm_area_struct *,  unsigned long, unsigned long *);
 	unsigned long (*swapin)(struct vm_area_struct *,  unsigned long);
 };
 
@@ -136,6 +143,7 @@
  */
 #define __get_free_page(priority) __get_free_pages((priority),0)
 extern unsigned long __get_free_pages(int priority, unsigned long gfporder);
+extern unsigned long __get_dma_pages(int priority, unsigned long gfporder);
 extern inline unsigned long get_free_page(int priority)
 {
 	unsigned long page;
@@ -197,6 +205,7 @@
 	unsigned long prot, unsigned long flags, unsigned long off);
 extern void merge_segments(struct vm_area_struct *);
 extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *);
+extern void remove_shared_vm_struct(struct vm_area_struct *);
 extern int do_munmap(unsigned long, size_t);
 extern unsigned long get_unmapped_area(unsigned long);
 
diff -u --recursive --new-file v1.1.68/linux/include/linux/mman.h linux/include/linux/mman.h
--- v1.1.68/linux/include/linux/mman.h	Tue Aug  2 11:41:03 1994
+++ linux/include/linux/mman.h	Thu Dec  1 12:26:22 1994
@@ -16,4 +16,8 @@
 #define MAP_DENYWRITE	0x0800		/* ETXTBSY */
 #define MAP_EXECUTABLE	0x1000		/* mark it as a executable */
 
+#define MS_ASYNC	1	/* sync memory asynchronously */
+#define MS_INVALIDATE	2	/* invalidate the caches */
+#define MS_SYNC		4	/* synchronous memory sync */
+
 #endif /* _LINUX_MMAN_H */
diff -u --recursive --new-file v1.1.68/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.1.68/linux/include/linux/netdevice.h	Sat Nov 26 18:36:07 1994
+++ linux/include/linux/netdevice.h	Wed Nov 30 21:53:47 1994
@@ -40,6 +40,18 @@
 #define IS_MULTICAST	5		/* Multicast IP address */
 
 /*
+ *	We tag these structures with multicasts.
+ */
+ 
+struct dev_mc_list
+{	
+	struct dev_mc_list *next;
+	char dmi_addr[MAX_ADDR_LEN];
+	unsigned short dmi_addrlen;
+	unsigned short dmi_users;
+};
+
+/*
  * 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
@@ -107,7 +119,12 @@
   unsigned long		  pa_dstaddr;	/* protocol P-P other side addr	*/
   unsigned long		  pa_mask;	/* protocol netmask		*/
   unsigned short	  pa_alen;	/* protocol address length	*/
+
+  struct dev_mc_list	 *mc_list;	/* Multicast mac addresses	*/
+  int			 mc_count;	/* Number of installed mcasts	*/
   
+  struct ip_mc_list	 *ip_mc_list;	/* IP multicast filter chain    */
+    
   /* For load balancing driver pair support */
   
   unsigned long		   pkt_queue;	/* Packets queued */
@@ -201,6 +218,13 @@
 /* Support for loadable net-drivers */
 extern int		register_netdev(struct device *dev);
 extern void		unregister_netdev(struct device *dev);
+
+/* Functions used for multicast support */
+
+extern void		dev_mc_upload(struct device *dev);
+extern void 		dev_mc_delete(struct device *dev, void *addr, int alen, int all);
+extern void		dev_mc_add(struct device *dev, void *addr, int alen, int newonly);
+extern void		dev_mc_discard(struct device *dev);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v1.1.68/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.1.68/linux/include/linux/skbuff.h	Thu Aug 11 20:38:52 1994
+++ linux/include/linux/skbuff.h	Wed Nov 30 22:40:54 1994
@@ -17,7 +17,7 @@
 #include <linux/wait.h>
 #include <linux/time.h>
 
-#define CONFIG_SKB_CHECK	1
+#undef CONFIG_SKB_CHECK
 
 #define HAVE_ALLOC_SKB		/* For the drivers to know */
 
@@ -94,7 +94,9 @@
 /*
  *	Handling routines are only of interest to the kernel
  */
- 
+
+#include <asm/system.h>
+
 #if 0
 extern void			print_skb(struct sk_buff *);
 #endif
@@ -131,8 +133,146 @@
 #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
+#define IS_SKB(skb)		
+#define IS_SKB_HEAD(skb)	
+
+extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
+{
+	list->prev = (struct sk_buff *)list;
+	list->next = (struct sk_buff *)list;
+}
+
+/*
+ *	Insert an sk_buff at the start of a list.
+ */
+
+extern __inline__ void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
+{
+	unsigned long flags;
+	struct sk_buff *list = (struct sk_buff *)list_;
+
+	save_flags(flags);
+	cli();
+	newsk->next = list->next;
+	newsk->prev = list;
+	newsk->next->prev = newsk;
+	newsk->prev->next = newsk;
+	restore_flags(flags);
+}
+
+/*
+ *	Insert an sk_buff at the end of a list.
+ */
+
+extern __inline__ void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
+{
+	unsigned long flags;
+	struct sk_buff *list = (struct sk_buff *)list_;
+
+	save_flags(flags);
+	cli();
+
+	newsk->next = list;
+	newsk->prev = list->prev;
+
+	newsk->next->prev = newsk;
+	newsk->prev->next = newsk;
+
+	restore_flags(flags);
+}
+
+/*
+ *	Remove an sk_buff from a list. This routine is also interrupt safe
+ *	so you can grab read and free buffers as another process adds them.
+ */
+
+extern __inline__ 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();
+
+	result = list->next;
+	if (result == list) {
+		restore_flags(flags);
+		return NULL;
+	}
+
+	result->next->prev = list;
+	list->next = result->next;
+
+	result->next = NULL;
+	result->prev = NULL;
+
+	restore_flags(flags);
+
+	return result;
+}
+
+/*
+ *	Insert a packet before another one in a list.
+ */
+
+extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	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.
+ */
+
+extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	newsk->prev = old;
+	newsk->next = old->next;
+	newsk->next->prev = newsk;
+	old->next = newsk;
+
+	restore_flags(flags);
+}
+
+/*
+ *	Remove an sk_buff from its list. Works even without knowing the list it
+ *	is sitting on, which can be handy at times. It also means that THE LIST
+ *	MUST EXIST when you unlink. Thus a list must have its contents unlinked
+ *	_FIRST_.
+ */
+
+extern __inline__ void skb_unlink(struct sk_buff *skb)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if(skb->prev && skb->next)
+	{
+		skb->next->prev = skb->prev;
+		skb->prev->next = skb->next;
+		skb->next = NULL;
+		skb->prev = NULL;
+	}
+	restore_flags(flags);
+}
+
 #endif
 
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
diff -u --recursive --new-file v1.1.68/linux/include/linux/socket.h linux/include/linux/socket.h
--- v1.1.68/linux/include/linux/socket.h	Fri Oct 21 09:39:32 1994
+++ linux/include/linux/socket.h	Wed Nov 30 21:53:47 1994
@@ -32,6 +32,7 @@
 #define AF_INET		2
 #define AF_AX25		3
 #define AF_IPX		4
+#define AF_APPLETALK	5
 
 #define AF_MAX		8	/* For now.. */
 
@@ -41,8 +42,10 @@
 #define PF_INET		AF_INET
 #define PF_AX25		AF_AX25
 #define PF_IPX		AF_IPX
+#define PF_APPLETALK	AF_APPLETALK
 
 #define PF_MAX		AF_MAX
+
 /* Flags we can use with send/ and recv. */
 #define MSG_OOB		1
 #define MSG_PEEK	2
@@ -70,6 +73,7 @@
 #define SO_NO_CHECK	11
 #define SO_PRIORITY	12
 #define SO_LINGER	13
+/* To add :#define SO_REUSEPORT 14 */
 
 /* IP options */
 #define IP_TOS		1
@@ -77,7 +81,21 @@
 #define	IPTOS_THROUGHPUT	0x08
 #define	IPTOS_RELIABILITY	0x04
 #define IP_TTL		2
+#define IP_HRDINCL	3
+#define IP_OPTIONS	4
+
+#define IP_MULTICAST_IF			32
+#define IP_MULTICAST_TTL 		33
+#define IP_MULTICAST_LOOP 		34
+#define IP_ADD_MEMBERSHIP		35
+#define IP_DROP_MEMBERSHIP		36
+
 
+/* These need to appear somewhere around here */
+#define IP_DEFAULT_MULTICAST_TTL        1
+#define IP_DEFAULT_MULTICAST_LOOP       1
+#define IP_MAX_MEMBERSHIPS              20
+ 
 /* IPX options */
 #define IPX_TYPE	1
 
diff -u --recursive --new-file v1.1.68/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.1.68/linux/include/linux/sockios.h	Wed Aug 10 19:26:44 1994
+++ linux/include/linux/sockios.h	Wed Nov 30 21:53:47 1994
@@ -59,6 +59,10 @@
 #define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
 #define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
 #define SIOCSIFSLAVE	0x8930
+/* begin multicast support change */
+#define SIOCADDMULTI  0x8931
+#define SIOCDELMULTI  0x8932
+/* end multicast support change */
 
 /* Routing table calls (oldrtent - don't use) */
 #define SIOCADDRTOLD	0x8940		/* add routing table entry	*/
diff -u --recursive --new-file v1.1.68/linux/include/linux/termios.h linux/include/linux/termios.h
--- v1.1.68/linux/include/linux/termios.h	Tue Nov 22 15:41:14 1994
+++ linux/include/linux/termios.h	Tue Nov 29 18:58:28 1994
@@ -43,6 +43,7 @@
 #define TIOCSETD	0x5423
 #define TIOCGETD	0x5424
 #define TCSBRKP		0x5425	/* Needed for POSIX tcsendbreak() */
+#define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOASYNC	0x5452
diff -u --recursive --new-file v1.1.68/linux/ipc/shm.c linux/ipc/shm.c
--- v1.1.68/linux/ipc/shm.c	Wed Nov  9 14:56:37 1994
+++ linux/ipc/shm.c	Wed Nov 30 21:47:40 1994
@@ -351,7 +351,7 @@
  * shmd->vm_start	virt addr of attach, multiple of SHMLBA
  * shmd->vm_end		multiple of SHMLBA
  * shmd->vm_next	next attach for task
- * shmd->vm_share	next attach for segment
+ * shmd->vm_next_share	next attach for segment
  * shmd->vm_offset	offset into segment
  * shmd->vm_pte		signature for this attach
  */
@@ -359,10 +359,12 @@
 static struct vm_operations_struct shm_vm_ops = {
 	shm_open,		/* open */
 	shm_close,		/* close */
+	NULL,			/* unmap */
+	NULL,			/* protect */
+	NULL,			/* sync */
+	NULL,			/* advise */
 	NULL,			/* nopage (done with swapin) */
 	NULL,			/* wppage */
-	NULL,			/* share */
-	NULL,			/* unmap */
 	NULL,			/* swapout (hardcoded right now) */
 	shm_swap_in		/* swapin */
 };
@@ -438,7 +440,6 @@
 
 /*
  * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
- * raddr is needed to return addresses above 2Gig.
  */
 int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
 {
@@ -453,12 +454,6 @@
 		return -EINVAL;
 	}
 
-	if (raddr) {
-		err = verify_area(VERIFY_WRITE, raddr, sizeof(ulong));
-		if (err)
-			return err;
-	}
-
 	shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
 	if (shp == IPC_UNUSED || shp == IPC_NOID) {
 		/* printk("shmat() -> EINVAL because shmid = %d is invalid\n",shmid); */
@@ -510,7 +505,7 @@
 	shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
 			 | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
 			 | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
-	shmd->vm_share = NULL;
+	shmd->vm_next_share = NULL;
 	shmd->vm_inode = NULL;
 	shmd->vm_offset = 0;
 	shmd->vm_ops = &shm_vm_ops;
@@ -523,14 +518,12 @@
 		return err;
 	}
 
-	shmd->vm_share = shp->attaches;
+	shmd->vm_next_share = shp->attaches;
 	shp->attaches = shmd;
 	shp->shm_lpid = current->pid;
 	shp->shm_atime = CURRENT_TIME;
 
-	if (!raddr)
-		return addr;
-	put_fs_long (addr, raddr);
+	*raddr = addr;
 	return 0;
 }
 
@@ -546,7 +539,7 @@
 		printk("shm_open: unused id=%d PANIC\n", id);
 		return;
 	}
-	shmd->vm_share = shp->attaches;
+	shmd->vm_next_share = shp->attaches;
 	shp->attaches = shmd;
 	shp->shm_nattch++;
 	shp->shm_atime = CURRENT_TIME;
@@ -570,9 +563,9 @@
 	/* remove from the list of attaches of the shm segment */
 	id = (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK;
 	shp = shm_segs[id];
-	for (shmdp = &shp->attaches; *shmdp; shmdp = &(*shmdp)->vm_share)
+	for (shmdp = &shp->attaches; *shmdp; shmdp = &(*shmdp)->vm_next_share)
 		if (*shmdp == shmd) {
-			*shmdp = shmd->vm_share;
+			*shmdp = shmd->vm_next_share;
 			goto found;
 		}
 	printk("shm_close: shm segment (id=%d) attach list inconsistent\n",id);
@@ -714,7 +707,7 @@
 		swap_free (swap_nr);
 		return 0;
 	}
-	for (shmd = shp->attaches; shmd; shmd = shmd->vm_share) {
+	for (shmd = shp->attaches; shmd; shmd = shmd->vm_next_share) {
 		unsigned long tmp, *pte;
 		if ((shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK) != id) {
 			printk ("shm_swap: id=%ld does not match shmd->vm_pte.id=%ld\n", id, shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK);
diff -u --recursive --new-file v1.1.68/linux/ipc/util.c linux/ipc/util.c
--- v1.1.68/linux/ipc/util.c	Wed Nov  9 14:56:37 1994
+++ linux/ipc/util.c	Wed Nov 30 17:17:39 1994
@@ -13,7 +13,7 @@
 #include <linux/stat.h>
 
 void ipc_init (void);
-asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr);
+asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth);
 
 #ifdef CONFIG_SYSVIPC
 
@@ -62,9 +62,13 @@
 	return 0;
 }
 
-asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr) 
+asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
 {
-	
+	int version;
+
+	version = call >> 16; /* hack for backward compatibility */
+	call &= 0xffff;
+
 	if (call <= SEMCTL)
 		switch (call) {
 		case SEMOP:
@@ -89,17 +93,21 @@
 		case MSGSND:
 			return sys_msgsnd (first, (struct msgbuf *) ptr, 
 					   second, third);
-		case MSGRCV: {
-			struct ipc_kludge tmp;
-			int err;
-			if (!ptr)
-				return -EINVAL;
-			if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
-				return err;
-			memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
-				       sizeof (tmp));
-			return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp,
-					 	third);
+		case MSGRCV:
+			switch (version) {
+			case 0: {
+				struct ipc_kludge tmp;
+				int err;
+				if (!ptr)
+					return -EINVAL;
+				if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
+					return err;
+				memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
+					       sizeof (tmp));
+				return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
+				}
+			case 1: default:
+				return sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third);
 			}
 		case MSGGET:
 			return sys_msgget ((key_t) first, second);
@@ -111,8 +119,21 @@
 	if (call <= SHMCTL) 
 		switch (call) {
 		case SHMAT:
-			return sys_shmat (first, (char *) ptr, second, 
-							(ulong *) third);
+			switch (version) {
+			case 0: default: {
+				ulong raddr;
+				int err;
+				if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
+					return err;
+				err = sys_shmat (first, (char *) ptr, second, &raddr);
+				if (err)
+					return err;
+				put_fs_long (raddr, (ulong *) third);
+				return 0;
+				}
+			case 1:
+				return sys_shmat (first, (char *) ptr, second, (ulong *) third);
+			}
 		case SHMDT: 
 			return sys_shmdt ((char *)ptr);
 		case SHMGET:
@@ -127,7 +148,7 @@
 
 #else /* not CONFIG_SYSVIPC */
 
-asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr) 
+asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) 
 {
     return -ENOSYS;
 }
diff -u --recursive --new-file v1.1.68/linux/kernel/exit.c linux/kernel/exit.c
--- v1.1.68/linux/kernel/exit.c	Wed Nov  9 14:56:37 1994
+++ linux/kernel/exit.c	Wed Nov 30 17:17:39 1994
@@ -364,6 +364,7 @@
 		struct vm_area_struct * next = mpnt->vm_next;
 		if (mpnt->vm_ops && mpnt->vm_ops->close)
 			mpnt->vm_ops->close(mpnt);
+		remove_shared_vm_struct(mpnt);
 		if (mpnt->vm_inode)
 			iput(mpnt->vm_inode);
 		kfree(mpnt);
diff -u --recursive --new-file v1.1.68/linux/kernel/fork.c linux/kernel/fork.c
--- v1.1.68/linux/kernel/fork.c	Mon Nov 28 14:21:47 1994
+++ linux/kernel/fork.c	Wed Nov 30 17:17:39 1994
@@ -100,8 +100,13 @@
 		*tmp = *mpnt;
 		tmp->vm_task = tsk;
 		tmp->vm_next = NULL;
-		if (tmp->vm_inode)
+		if (tmp->vm_inode) {
 			tmp->vm_inode->i_count++;
+			/* insert tmp into the share list, just after mpnt */
+			tmp->vm_next_share->vm_prev_share = tmp;
+			mpnt->vm_next_share = tmp;
+			tmp->vm_prev_share = mpnt;
+		}
 		if (tmp->vm_ops && tmp->vm_ops->open)
 			tmp->vm_ops->open(tmp);
 		*p = tmp;
diff -u --recursive --new-file v1.1.68/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.1.68/linux/kernel/ksyms.c	Mon Nov 28 14:21:47 1994
+++ linux/kernel/ksyms.c	Wed Nov 30 22:03:47 1994
@@ -105,9 +105,12 @@
 	X(set_blocksize),
 	X(getblk),
 	X(bread),
+	X(breada),
 	X(brelse),
 	X(ll_rw_block),
 	X(__wait_on_buffer),
+	X(dcache_lookup),
+	X(dcache_add),
 
 	/* device registration */
 	X(register_chrdev),
diff -u --recursive --new-file v1.1.68/linux/kernel/sys.c linux/kernel/sys.c
--- v1.1.68/linux/kernel/sys.c	Fri Nov 18 15:22:10 1994
+++ linux/kernel/sys.c	Wed Nov 30 21:47:40 1994
@@ -32,7 +32,7 @@
 
 asmlinkage int sys_ni_syscall(void)
 {
-	return -EINVAL;
+	return -ENOSYS;
 }
 
 asmlinkage int sys_idle(void)
diff -u --recursive --new-file v1.1.68/linux/mm/Makefile linux/mm/Makefile
--- v1.1.68/linux/mm/Makefile	Mon Jul 25 22:03:14 1994
+++ linux/mm/Makefile	Thu Dec  1 12:00:50 1994
@@ -14,7 +14,7 @@
 .c.s:
 	$(CC) $(CFLAGS) -S $<
 
-OBJS	= memory.o swap.o mmap.o mprotect.o kmalloc.o vmalloc.o
+OBJS	= memory.o swap.o mmap.o filemap.o mprotect.o kmalloc.o vmalloc.o
 
 mm.o: $(OBJS)
 	$(LD) -r -o mm.o $(OBJS)
diff -u --recursive --new-file v1.1.68/linux/mm/filemap.c linux/mm/filemap.c
--- v1.1.68/linux/mm/filemap.c	Thu Jan  1 02:00:00 1970
+++ linux/mm/filemap.c	Thu Dec  1 12:02:11 1994
@@ -0,0 +1,227 @@
+/*
+ *	linux/mm/filemmap.c
+ *
+ * Copyright (C) 1994 Linus Torvalds
+ */
+
+/*
+ * This file handles the generic file mmap semantics used by
+ * most "normal" filesystems (but you don't /have/ to use this:
+ * the NFS filesystem does this differently, for example)
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * Shared mappings implemented 30.11.1994. It's not fully working yet,
+ * though.
+ */
+
+static unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address,
+	unsigned long page, int no_share)
+{
+	struct inode * inode = area->vm_inode;
+	unsigned int block;
+	int nr[8];
+	int i, *p;
+
+	address &= PAGE_MASK;
+	block = address - area->vm_start + area->vm_offset;
+	block >>= inode->i_sb->s_blocksize_bits;
+	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+	p = nr;
+	do {
+		*p = bmap(inode,block);
+		i--;
+		block++;
+		p++;
+	} while (i > 0);
+	return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share);
+}
+
+/*
+ * NOTE! mmap sync doesn't really work yet. This is mainly a stub for it,
+ * which only works if the buffers and the page were already sharing the
+ * same physical page (that's actually pretty common, especially if the
+ * file has been mmap'ed before being read the normal way).
+ *
+ * Todo:
+ * - non-shared pages also need to be synced with the buffers.
+ * - the "swapout()" function needs to swap out the page to
+ *   the shared file instead of using the swap device.
+ */
+static inline void file_mmap_sync_page(struct vm_area_struct * vma,
+	unsigned long offset,
+	unsigned long page)
+{
+	struct buffer_head * bh;
+
+	bh = buffer_pages[MAP_NR(page)];
+	if (bh) {
+		/* whee.. just mark the buffer heads dirty */
+		struct buffer_head * tmp = bh;
+		do {
+			mark_buffer_dirty(tmp, 0);
+			tmp = tmp->b_this_page;
+		} while (tmp != bh);
+		return;
+	}
+	/* we'll need to go fetch the buffer heads etc.. RSN */
+	printk("msync: %ld: [%08lx]\n", offset, page);
+	printk("Can't handle non-shared page yet\n");
+	return;
+}
+
+static void file_mmap_sync(struct vm_area_struct * vma, unsigned long start,
+	size_t size, unsigned int flags)
+{
+	unsigned long page_dir;
+	unsigned long *page_table, *dir;
+	unsigned long poff, pcnt, pc;
+
+	size = size >> PAGE_SHIFT;
+	dir = PAGE_DIR_OFFSET(current->tss.cr3,start);
+	poff = (start >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
+	start -= vma->vm_start;
+	if ((pcnt = PTRS_PER_PAGE - poff) > size)
+		pcnt = size;
+
+	for ( ; size > 0; ++dir, size -= pcnt,
+	     pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size)) {
+		if (!(PAGE_PRESENT & (page_dir = *dir))) {
+			if (page_dir)
+				printk("file_mmap_sync: bad page directory.\n");
+			poff = 0;
+			start += pcnt*PAGE_SIZE;
+			continue;
+		}
+		page_table = (unsigned long *)(PAGE_MASK & page_dir);
+		if (poff) {
+			page_table += poff;
+			poff = 0;
+		}
+		for (pc = pcnt; pc--; page_table++, start += PAGE_SIZE) {
+			unsigned long page = *page_table;
+			if (!(page & PAGE_PRESENT))
+				continue;
+			if (!(page & PAGE_DIRTY))
+				continue;
+			mem_map[MAP_NR(page)]++;
+			if (flags & MS_INVALIDATE) {
+				*page_table = 0;
+				free_page(page);
+			} else
+				*page_table = page & ~PAGE_DIRTY;
+			file_mmap_sync_page(vma, start, page);
+			free_page(page);
+		}
+	}
+	invalidate();
+	return;
+}
+
+/*
+ * This handles area unmaps..
+ */
+static void file_mmap_unmap(struct vm_area_struct *vma, unsigned long start, size_t len)
+{
+	if (vma->vm_page_prot & PAGE_RW)
+		file_mmap_sync(vma, start, len, MS_ASYNC);
+}
+
+/*
+ * This handles complete area closes..
+ */
+static void file_mmap_close(struct vm_area_struct * vma)
+{
+	if (vma->vm_page_prot & PAGE_RW)
+		file_mmap_sync(vma, vma->vm_start, vma->vm_end - vma->vm_start, MS_ASYNC);
+}
+
+/*
+ * This isn't implemented yet: you'll get a warning and incorrect behaviour.
+ *
+ * Note that the page is free'd by the higher-level after return,
+ * so we have to either write it out or just forget it. We currently
+ * forget it..
+ */
+void file_mmap_swapout(struct vm_area_struct * vma,
+	unsigned long offset,
+	unsigned long *pte)
+{
+	printk("swapout not implemented on shared files..\n");
+	*pte = 0;
+}
+
+/*
+ * Shared mappings need to be able to do the right thing at
+ * close/unmap/sync. They will also use the private file as
+ * backing-store for swapping..
+ */
+static struct vm_operations_struct file_shared_mmap = {
+	NULL,			/* open */
+	file_mmap_close,	/* close */
+	file_mmap_unmap,	/* unmap */
+	NULL,			/* protect */
+	file_mmap_sync,		/* sync */
+	NULL,			/* advise */
+	file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	file_mmap_swapout,	/* swapout */
+	NULL,			/* swapin */
+};
+
+/*
+ * Private mappings just need to be able to load in the map
+ *
+ * (this is actually used for shared mappings as well, if we
+ * know they can't ever get write permissions..)
+ */
+static struct vm_operations_struct file_private_mmap = {
+	NULL,			/* open */
+	NULL,			/* close */
+	NULL,			/* unmap */
+	NULL,			/* protect */
+	NULL,			/* sync */
+	NULL,			/* advise */
+	file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	NULL,			/* swapout */
+	NULL,			/* swapin */
+};
+
+/* This is used for a general mmap of a disk file */
+int generic_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+	struct vm_operations_struct * ops;
+
+	if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+		return -EINVAL;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	if (!inode->i_op || !inode->i_op->bmap)
+		return -ENOEXEC;
+	ops = &file_private_mmap;
+	if (vma->vm_flags & VM_SHARED) {
+		if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))
+			ops = &file_shared_mmap;
+	}
+	if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+	vma->vm_inode = inode;
+	inode->i_count++;
+	vma->vm_ops = ops;
+	return 0;
+}
diff -u --recursive --new-file v1.1.68/linux/mm/kmalloc.c linux/mm/kmalloc.c
--- v1.1.68/linux/mm/kmalloc.c	Fri Aug 26 10:27:07 1994
+++ linux/mm/kmalloc.c	Wed Nov 30 22:03:47 1994
@@ -84,6 +84,7 @@
  */
 struct size_descriptor {
 	struct page_descriptor *firstfree;
+	struct page_descriptor *dmafree; /* DMA-able memory */
 	int size;
 	int nblocks;
 
@@ -100,20 +101,20 @@
  */
 
 struct size_descriptor sizes[] = { 
-	{ NULL,  32,127, 0,0,0,0, 0},
-	{ NULL,  64, 63, 0,0,0,0, 0 },
-	{ NULL, 128, 31, 0,0,0,0, 0 },
-	{ NULL, 252, 16, 0,0,0,0, 0 },
-	{ NULL, 508,  8, 0,0,0,0, 0 },
-	{ NULL,1020,  4, 0,0,0,0, 0 },
-	{ NULL,2040,  2, 0,0,0,0, 0 },
-	{ NULL,4096-16,  1, 0,0,0,0, 0 },
-	{ NULL,8192-16,  1, 0,0,0,0, 1 },
-	{ NULL,16384-16,  1, 0,0,0,0, 2 },
-	{ NULL,32768-16,  1, 0,0,0,0, 3 },
-	{ NULL,65536-16,  1, 0,0,0,0, 4 },
-	{ NULL,131072-16,  1, 0,0,0,0, 5 },
-	{ NULL,   0,  0, 0,0,0,0, 0 }
+	{ NULL, NULL,  32,127, 0,0,0,0, 0},
+	{ NULL, NULL,  64, 63, 0,0,0,0, 0 },
+	{ NULL, NULL, 128, 31, 0,0,0,0, 0 },
+	{ NULL, NULL, 252, 16, 0,0,0,0, 0 },
+	{ NULL, NULL, 508,  8, 0,0,0,0, 0 },
+	{ NULL, NULL,1020,  4, 0,0,0,0, 0 },
+	{ NULL, NULL,2040,  2, 0,0,0,0, 0 },
+	{ NULL, NULL,4096-16,  1, 0,0,0,0, 0 },
+	{ NULL, NULL,8192-16,  1, 0,0,0,0, 1 },
+	{ NULL, NULL,16384-16,  1, 0,0,0,0, 2 },
+	{ NULL, NULL,32768-16,  1, 0,0,0,0, 3 },
+	{ NULL, NULL,65536-16,  1, 0,0,0,0, 4 },
+	{ NULL, NULL,131072-16,  1, 0,0,0,0, 5 },
+	{ NULL, NULL,   0,  0, 0,0,0,0, 0 }
 };
 
 
@@ -164,9 +165,13 @@
 {
 	unsigned long flags;
 	int order,tries,i,sz;
+	int dma_flag;
 	struct block_header *p;
 	struct page_descriptor *page;
 
+	dma_flag = (priority & GFP_DMA);
+	priority &= GFP_LEVEL_MASK;
+	  
 /* Sanity check... */
 	if (intr_count && priority != GFP_ATOMIC) {
 		static int count = 0;
@@ -193,7 +198,7 @@
     {
     /* Try to allocate a "recently" freed memory block */
     cli ();
-    if ((page = sizes[order].firstfree) &&
+    if ((page = (dma_flag ? sizes[order].dmafree : sizes[order].firstfree)) &&
         (p    =  page->firstfree))
         {
         if (p->bh_flags == MF_FREE)
@@ -224,7 +229,11 @@
     sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */
 
     /* This can be done with ints on: This is private to this invocation */
-    page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
+    if (dma_flag)
+      page = (struct page_descriptor *) __get_dma_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
+    else
+      page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
+
     if (!page) {
         static unsigned long last = 0;
         if (last + 10*HZ < jiffies) {
@@ -262,7 +271,10 @@
      * here, but you never know.... 
      */
     page->next = sizes[order].firstfree;
-    sizes[order].firstfree = page;
+    if (dma_flag)
+      sizes[order].dmafree = page;
+    else
+      sizes[order].firstfree = page;
     restore_flags(flags);
     }
 
@@ -280,7 +292,6 @@
 return NULL;
 }
 
-
 void kfree_s (void *ptr,int size)
 {
 unsigned long flags;
@@ -315,7 +326,8 @@
 page->nfree ++;
 
 if (page->nfree == 1)
-   { /* Page went from full to one free block: put it on the freelist */
+   { /* Page went from full to one free block: put it on the freelist.  Do not bother
+      trying to put it on the DMA list. */
    if (page->next)
         {
         printk ("Page %p already on freelist dazed and confused....\n", page);
@@ -337,11 +349,20 @@
         {
         sizes[order].firstfree = page->next;
         }
+    else if (sizes[order].dmafree == page)
+        {
+        sizes[order].dmafree = page->next;
+        }
     else
         {
         for (pg2=sizes[order].firstfree;
                 (pg2 != NULL) && (pg2->next != page);
                         pg2=pg2->next)
+            /* Nothing */;
+	if (!pg2)
+	  for (pg2=sizes[order].dmafree;
+	       (pg2 != NULL) && (pg2->next != page);
+	       pg2=pg2->next)
             /* Nothing */;
         if (pg2 != NULL)
             pg2->next = page->next;
diff -u --recursive --new-file v1.1.68/linux/mm/memory.c linux/mm/memory.c
--- v1.1.68/linux/mm/memory.c	Mon Nov 28 14:21:47 1994
+++ linux/mm/memory.c	Wed Nov 30 21:47:40 1994
@@ -84,7 +84,7 @@
  */
 void oom(struct task_struct * task)
 {
-	printk("\nOut of memory.\n");
+	printk("\nOut of memory for %s.\n", current->comm);
 	task->sigaction[SIGKILL-1].sa_handler = NULL;
 	task->blocked &= ~(1<<(SIGKILL-1));
 	send_sig(SIGKILL,task,1);
@@ -804,10 +804,10 @@
 	unsigned long error_code, unsigned long newpage)
 {
 	struct inode * inode;
-	struct task_struct ** p;
 	unsigned long offset;
 	unsigned long from_address;
 	unsigned long give_page;
+	struct vm_area_struct * mpnt;
 
 	if (!area || !(inode = area->vm_inode) || inode->i_count < 2)
 		return 0;
@@ -819,34 +819,29 @@
 		give_page = newpage;
 	}
 	offset = address - area->vm_start + area->vm_offset;
-	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
-		struct vm_area_struct * mpnt;
-		if (!*p)
+	/* See if there is something in the VM we can share pages with. */
+	/* Traverse the entire circular i_mmap list, except `area' itself. */
+	for (mpnt = area->vm_next_share; mpnt != area; mpnt = mpnt->vm_next_share) {
+		/* must be same inode */
+		if (mpnt->vm_inode != inode) {
+			printk("Aiee! Corrupt vm_area_struct i_mmap ring\n");
+			break;	
+		}
+		/* offsets must be mutually page-aligned */
+		if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK)
 			continue;
-		if (area->vm_task == *p)
+		/* the other area must actually cover the wanted page.. */
+		from_address = offset + mpnt->vm_start - mpnt->vm_offset;
+		if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end)
 			continue;
-		/* Now see if there is something in the VMM that
-		   we can share pages with */
-		for (mpnt = (*p)->mm->mmap; mpnt; mpnt = mpnt->vm_next) {
-			/* must be same inode */
-			if (mpnt->vm_inode != inode)
-				continue;
-			/* offsets must be mutually page-aligned */
-			if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK)
-				continue;
-			/* the other area must actually cover the wanted page.. */
-			from_address = offset + mpnt->vm_start - mpnt->vm_offset;
-			if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end)
-				continue;
-			/* .. NOW we can actually try to use the same physical page */
-			if (!try_to_share(address, area, from_address, mpnt, give_page))
-				continue;
-			/* free newpage if we never used it.. */
-			if (give_page || !newpage)
-				return 1;
-			free_page(newpage);
+		/* .. NOW we can actually try to use the same physical page */
+		if (!try_to_share(address, area, from_address, mpnt, give_page))
+			continue;
+		/* free newpage if we never used it.. */
+		if (give_page || !newpage)
 			return 1;
-		}
+		free_page(newpage);
+		return 1;
 	}
 	return 0;
 }
@@ -1283,38 +1278,3 @@
 	val->sharedram <<= PAGE_SHIFT;
 	return;
 }
-
-
-/*
- * This handles a generic mmap of a disk file.
- */
-static unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address,
-	unsigned long page, int no_share)
-{
-	struct inode * inode = area->vm_inode;
-	unsigned int block;
-	int nr[8];
-	int i, *p;
-
-	address &= PAGE_MASK;
-	block = address - area->vm_start + area->vm_offset;
-	block >>= inode->i_sb->s_blocksize_bits;
-	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
-	p = nr;
-	do {
-		*p = bmap(inode,block);
-		i--;
-		block++;
-		p++;
-	} while (i > 0);
-	return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share);
-}
-
-struct vm_operations_struct file_mmap = {
-	NULL,			/* open */
-	NULL,			/* close */
-	file_mmap_nopage,	/* nopage */
-	NULL,			/* wppage */
-	NULL,			/* share */
-	NULL,			/* unmap */
-};
diff -u --recursive --new-file v1.1.68/linux/mm/mmap.c linux/mm/mmap.c
--- v1.1.68/linux/mm/mmap.c	Wed Nov  9 14:56:37 1994
+++ linux/mm/mmap.c	Thu Dec  1 11:48:12 1994
@@ -341,15 +341,16 @@
 		mpnt = free;
 		free = free->vm_next;
 
+		remove_shared_vm_struct(mpnt);
+
 		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
 		end = addr+len;
 		end = end > mpnt->vm_end ? mpnt->vm_end : end;
 
 		if (mpnt->vm_ops && mpnt->vm_ops->unmap)
 			mpnt->vm_ops->unmap(mpnt, st, end-st);
-		else
-			unmap_fixup(mpnt, st, end-st);
 
+		unmap_fixup(mpnt, st, end-st);
 		kfree(mpnt);
 	}
 
@@ -357,35 +358,14 @@
 	return 0;
 }
 
-/* This is used for a general mmap of a disk file */
-int generic_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
-{
-	extern struct vm_operations_struct file_mmap;
-
-	if (vma->vm_page_prot & PAGE_RW)	/* only PAGE_COW or read-only supported right now */
-		return -EINVAL;
-	if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
-		return -EINVAL;
-	if (!inode->i_sb || !S_ISREG(inode->i_mode))
-		return -EACCES;
-	if (!inode->i_op || !inode->i_op->bmap)
-		return -ENOEXEC;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
-	vma->vm_inode = inode;
-	inode->i_count++;
-	vma->vm_ops = &file_mmap;
-	return 0;
-}
-
 /*
- * Insert vm structure into process list sorted by address.
+ * Insert vm structure into process list sorted by address
+ * and into the inode's i_mmap ring.
  */
 void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
 {
-	struct vm_area_struct **p, *mpnt;
+	struct vm_area_struct **p, *mpnt, *share;
+	struct inode * inode;
 
 	p = &t->mm->mmap;
 	while ((mpnt = *p) != NULL) {
@@ -397,6 +377,43 @@
 	}
 	vmp->vm_next = mpnt;
 	*p = vmp;
+
+	inode = vmp->vm_inode;
+	if (!inode)
+		return;
+
+	/* insert vmp into inode's circular share list */
+	if ((share = inode->i_mmap)) {
+		vmp->vm_next_share = share->vm_next_share;
+		vmp->vm_next_share->vm_prev_share = vmp;
+		share->vm_next_share = vmp;
+		vmp->vm_prev_share = share;
+	} else
+		inode->i_mmap = vmp->vm_next_share = vmp->vm_prev_share = vmp;
+}
+
+/*
+ * Remove one vm structure from the inode's i_mmap ring.
+ */
+void remove_shared_vm_struct(struct vm_area_struct *mpnt)
+{
+	struct inode * inode = mpnt->vm_inode;
+
+	if (!inode)
+		return;
+
+	if (mpnt->vm_next_share == mpnt) {
+		if (inode->i_mmap != mpnt)
+			printk("Inode i_mmap ring corrupted\n");
+		inode->i_mmap = NULL;
+		return;
+	}
+
+	if (inode->i_mmap == mpnt)
+		inode->i_mmap = mpnt->vm_next_share;
+
+	mpnt->vm_prev_share->vm_next_share = mpnt->vm_next_share;
+	mpnt->vm_next_share->vm_prev_share = mpnt->vm_prev_share;
 }
 
 /*
@@ -451,6 +468,7 @@
 			mpnt->vm_start = mpnt->vm_end;
 			mpnt->vm_ops->close(mpnt);
 		}
+		remove_shared_vm_struct(mpnt);
 		if (mpnt->vm_inode)
 			mpnt->vm_inode->i_count--;
 		kfree_s(mpnt, sizeof(*mpnt));
diff -u --recursive --new-file v1.1.68/linux/mm/swap.c linux/mm/swap.c
--- v1.1.68/linux/mm/swap.c	Tue Nov 15 15:35:41 1994
+++ linux/mm/swap.c	Thu Dec  1 12:41:29 1994
@@ -19,6 +19,7 @@
 #include <linux/stat.h>
 #include <linux/fs.h>
 
+#include <asm/dma.h>
 #include <asm/system.h> /* for cli()/sti() */
 #include <asm/bitops.h>
 
@@ -287,7 +288,7 @@
 	return page | PAGE_DIRTY | PAGE_PRESENT;
 }
 
-static inline int try_to_swap_out(unsigned long * table_ptr)
+static inline int try_to_swap_out(struct vm_area_struct* vma, unsigned offset, unsigned long * table_ptr)
 {
 	unsigned long page, entry;
 
@@ -311,13 +312,18 @@
 		page &= PAGE_MASK;
 		if (mem_map[MAP_NR(page)] != 1)
 			return 0;
-		if (!(entry = get_swap_page()))
-			return 0;
-		*table_ptr = entry;
-		invalidate();
-		write_swap_page(entry, (char *) page);
+		if (vma->vm_ops && vma->vm_ops->swapout)
+			vma->vm_ops->swapout(vma, offset, table_ptr);
+		else
+		{
+			if (!(entry = get_swap_page()))
+				return 0;
+			*table_ptr = entry;
+			invalidate();
+			write_swap_page(entry, (char *) page);
+		}
 		free_page(page);
-		return 1;
+		return 1 + mem_map[MAP_NR(page)];
 	}
         if ((entry = find_in_swap_cache(page)))  {
 		if (mem_map[MAP_NR(page)] != 1) {
@@ -370,11 +376,28 @@
 	unsigned long offset;
 	unsigned long *pgdir;
 	unsigned long pg_table;
+	struct vm_area_struct* vma;
 
 	/*
 	 * Go through process' page directory.
 	 */
 	address = p->mm->swap_address;
+	p->mm->swap_address = 0;
+
+	/*
+	 * Find the proper vm-area
+	 */
+	vma = p->mm->mmap;
+	for (;;) {
+		if (!vma)
+			return 0;
+		if (address <= vma->vm_end)
+			break;
+		vma = vma->vm_next;
+	}
+	if (address < vma->vm_start)
+		address = vma->vm_start;
+
 	pgdir = (address >> PGDIR_SHIFT) + (unsigned long *) p->tss.cr3;
 	offset = address & ~PGDIR_MASK;
 	address &= PGDIR_MASK;
@@ -397,7 +420,18 @@
 		 * Go through this page table.
 		 */
 		for( ; offset < ~PGDIR_MASK ; offset += PAGE_SIZE) {
-			switch(try_to_swap_out((unsigned long *) (pg_table + (offset >> 10)))) {
+			/*
+			 * Update vma again..
+			 */
+			for (;;) {
+				if (address+offset < vma->vm_end)
+					break;
+				vma = vma->vm_next;
+				if (!vma)
+					return 0;
+			}
+
+			switch(try_to_swap_out(vma, offset+address-vma->vm_start, (unsigned long *) (pg_table + (offset >> 10)))) {
 				case 0:
 					break;
 
@@ -415,9 +449,8 @@
 	}
 	/*
 	 * Finish work with this process, if we reached the end of the page
-	 * directory.  Mark restart from the beginning the next time.
+	 * directory.
 	 */
-	p->mm->swap_address = 0;
 	return 0;
 }
 
@@ -647,10 +680,10 @@
  */
 unsigned long __get_dma_pages(int priority, unsigned long order)
 {
-	unsigned long list = 0; 
+	unsigned long list = 0;
 	unsigned long result;
-	unsigned long limit = 16*1024*1024;
-	
+	unsigned long limit = MAX_DMA_ADDRESS;
+
 	/* if (EISA_bus) limit = ~0UL; */
 	if (priority != GFP_ATOMIC)
 		priority = GFP_BUFFER;
diff -u --recursive --new-file v1.1.68/linux/net/inet/Makefile linux/net/inet/Makefile
--- v1.1.68/linux/net/inet/Makefile	Wed Aug 10 21:38:27 1994
+++ linux/net/inet/Makefile	Wed Nov 30 21:53:47 1994
@@ -15,19 +15,19 @@
 	$(CC) $(CFLAGS) -S $<
 
 
-OBJS	:= sock.o eth.o dev.o skbuff.o datagram.o
+OBJS	:= sock.o eth.o dev.o dev_mcast.o skbuff.o datagram.o
 
 ifdef CONFIG_INET
 
 OBJS	:= $(OBJS) utils.o route.o proc.o timer.o protocol.o packet.o \
-		   arp.o ip.o raw.o icmp.o tcp.o udp.o devinet.o af_inet.o
-
-endif
+		   arp.o ip.o raw.o icmp.o tcp.o udp.o devinet.o af_inet.o \
+		   igmp.o ip_fw.o 
 
 ifdef CONFIG_INET_RARP
 
 OBJS	:= $(OBJS) rarp.o
 
+endif
 endif
 
 ifdef CONFIG_AX25
diff -u --recursive --new-file v1.1.68/linux/net/inet/af_inet.c linux/net/inet/af_inet.c
--- v1.1.68/linux/net/inet/af_inet.c	Mon Nov 28 14:21:47 1994
+++ linux/net/inet/af_inet.c	Wed Nov 30 22:49:29 1994
@@ -652,6 +652,11 @@
 	sk->dummy_th.dest = 0;
 	sk->ip_tos=0;
 	sk->ip_ttl=64;
+#ifdef CONFIG_IP_MULTICAST
+	sk->ip_mc_loop=0;
+	sk->ip_mc_ttl=1;
+	*sk->ip_mc_name=0;
+#endif
   	
 	sk->state_change = def_callback1;
 	sk->data_ready = def_callback2;
@@ -1227,6 +1232,12 @@
 		case SIOCSIFFLAGS:
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
+
+/* begin multicast support change */
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+/* end multicast support change */
+		
 		case SIOCGIFDSTADDR:
 		case SIOCSIFDSTADDR:
 		case SIOCGIFBRDADDR:
@@ -1331,6 +1342,79 @@
   	return result;
 }
 
+/*
+ *	Deliver a datagram to raw sockets.
+ */
+ 
+struct sock *get_sock_raw(struct sock *sk, 
+				unsigned short num,
+				unsigned long raddr,
+				unsigned long laddr)
+{
+	struct sock *s;
+
+	s=sk;
+
+	for(; s != NULL; s = s->next) 
+	{
+		if (s->num != num) 
+			continue;
+		if(s->dead && (s->state == TCP_CLOSE))
+			continue;
+		if(s->daddr && s->daddr!=raddr)
+			continue;
+ 		if(s->saddr  && s->saddr!=laddr)
+			continue;
+		return(s);
+  	}
+  	return(NULL);
+}
+
+#ifdef CONFIG_IP_MULTICAST
+/*
+ *	Deliver a datagram to broadcast/multicast sockets.
+ */
+ 
+struct sock *get_sock_mcast(struct sock *sk, 
+				unsigned short num,
+				unsigned long raddr,
+				unsigned short rnum, unsigned long laddr)
+{
+	struct sock *s;
+	unsigned short hnum;
+
+	hnum = ntohs(num);
+
+	/*
+	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
+	 * than a prime unless 3 or more sockets end up using the same
+	 * array entry.  This should not be a problem because most
+	 * well known sockets don't overlap that much, and for
+	 * the other ones, we can just be careful about picking our
+	 * socket number when we choose an arbitrary one.
+	 */
+	
+	s=sk;
+
+	for(; s != NULL; s = s->next) 
+	{
+		if (s->num != hnum) 
+			continue;
+		if(s->dead && (s->state == TCP_CLOSE))
+			continue;
+		if(s->daddr && s->daddr!=raddr)
+			continue;
+		if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) 
+			continue;
+ 		if(s->saddr  && s->saddr!=laddr)
+			continue;
+		return(s);
+  	}
+  	return(NULL);
+}
+
+#endif
+
 static struct proto_ops inet_proto_ops = {
 	AF_INET,
 
@@ -1369,7 +1453,7 @@
 	int i;
 
 
-	printk("Swansea University Computer Society TCP/IP for NET3.017\n");
+	printk("Swansea University Computer Society TCP/IP for NET3.018\n");
 
 	/*
 	 *	Tell SOCKET that we are alive... 
diff -u --recursive --new-file v1.1.68/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.68/linux/net/inet/arp.c	Fri Oct 21 09:39:33 1994
+++ linux/net/inet/arp.c	Wed Nov 30 21:53:47 1994
@@ -787,6 +787,10 @@
 {
 	struct arp_table *entry;
 	unsigned long hash;
+#ifdef CONFIG_IP_MULTICAST
+	unsigned long taddr;
+#endif	
+
 	switch (ip_chk_addr(paddr))
 	{
 		case IS_MYADDR:
@@ -794,6 +798,26 @@
 			memcpy(haddr, dev->dev_addr, dev->addr_len);
 			skb->arp = 1;
 			return 0;
+#ifdef CONFIG_IP_MULTICAST
+		case IS_MULTICAST:
+			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
+			{
+				haddr[0]=0x01;
+				haddr[1]=0x00;
+				haddr[2]=0x5e;
+				taddr=ntohl(paddr);
+				haddr[5]=taddr&0xff;
+				taddr=taddr>>8;
+				haddr[4]=taddr&0xff;
+				taddr=taddr>>8;
+				haddr[3]=taddr&0x7f;
+				return 0;
+			}
+		/*
+		 *	If a device does not support multicast broadcast the stuff (eg AX.25 for now)
+		 */
+#endif
+		
 		case IS_BROADCAST:
 			memcpy(haddr, dev->broadcast, dev->addr_len);
 			skb->arp = 1;
diff -u --recursive --new-file v1.1.68/linux/net/inet/dev.c linux/net/inet/dev.c
--- v1.1.68/linux/net/inet/dev.c	Sat Nov 26 18:36:07 1994
+++ linux/net/inet/dev.c	Wed Nov 30 21:53:47 1994
@@ -22,6 +22,9 @@
  *					keep the queue safe.
  *		Alan Cox	:	Fixed double lock.
  *		Alan Cox	:	Fixed promisc NULL pointer trap
+ *		????????	:	Support the full private ioctl range
+ *		Alan Cox	:	Moved ioctl permission check into drivers
+ *		Tim Kordas	:	SIOCADDMULTI/SIOCDELMULTI
  *
  *	Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have
  *	the rest as well commented in the end.
@@ -266,6 +269,12 @@
 	if (ret == 0) 
 		dev->flags |= (IFF_UP | IFF_RUNNING);
 	
+	/*
+	 *	Initialise multicasting status 
+	 */
+	 
+	dev_mc_upload(dev);
+
 	return(ret);
 }
 
@@ -305,6 +314,10 @@
 		ipxrtr_device_down(dev);
 #endif	
 		/*
+		 *	Flush the multicast chain
+		 */
+		dev_mc_discard(dev);
+		/*
 		 *	Blank the IP addresses
 		 */
 		dev->pa_addr = 0;
@@ -360,9 +373,9 @@
 		dev=dev->slave;
 	restore_flags(flags);
 #endif		
- 
+#ifdef CONFIG_SKB_CHECK 
 	IS_SKB(skb);
-    
+#endif    
 	skb->dev = dev;
 
 	/*
@@ -495,8 +508,9 @@
 	/*
 	 *	Add it to the "backlog" queue. 
 	 */
-
+#ifdef CONFIG_SKB_CHECK
 	IS_SKB(skb);
+#endif	
 	skb_queue_tail(&backlog,skb);
 	backlog_size++;
   
@@ -1070,15 +1084,21 @@
 				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 | IFF_SLAVE | IFF_MASTER);
+					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
+					| IFF_MULTICAST);
 #ifdef CONFIG_SLAVE_BALANCING				
 				if(!(dev->flags&IFF_MASTER) && dev->slave)
 				{
 					dev->slave->flags&=~IFF_SLAVE;
 					dev->slave=NULL;
 				}
-#endif				
+#endif
+				/*
+				 *	Load in the correct multicast list now the flags have changed.
+				 */				
 
+				dev_mc_upload(dev);
+#if 0
 				if( dev->set_multicast_list!=NULL)
 				{
 				
@@ -1096,7 +1116,7 @@
 					if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0))
 			  			dev->set_multicast_list(dev,-1,NULL);
 			  	}
-			  		
+#endif			  		
 			  	/*
 			  	 *	Have we downed the interface
 			  	 */
@@ -1340,6 +1360,22 @@
 		}
 		break;
 #endif			
+
+		case SIOCADDMULTI:
+			if(dev->set_multicast_list==NULL)
+				return -EINVAL;
+			if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+				return -EINVAL;
+			dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1);
+			return 0;
+
+		case SIOCDELMULTI:
+			if(dev->set_multicast_list==NULL)
+				return -EINVAL;
+			if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+				return -EINVAL;
+			dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1);
+			return 0;
 		/*
 		 *	Unknown or private ioctl
 		 */
@@ -1420,8 +1456,6 @@
 		default:
 			if((cmd >= SIOCDEVPRIVATE) &&
 			   (cmd <= (SIOCDEVPRIVATE + 15))) {
-				if (!suser())
-					return -EPERM;
 				return dev_ifsioc(arg, cmd);
 			}
 			return -EINVAL;
diff -u --recursive --new-file v1.1.68/linux/net/inet/dev_mcast.c linux/net/inet/dev_mcast.c
--- v1.1.68/linux/net/inet/dev_mcast.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/dev_mcast.c	Wed Nov 30 21:53:47 1994
@@ -0,0 +1,156 @@
+/*
+ *	Linux NET3:	Multicast List maintenance. 
+ *
+ *	Authors:
+ *		Tim Kordas <tjk@nostromo.eeap.cwru.edu> 
+ *		Richard Underwood <richard@wuzz.demon.co.uk>
+ *
+ *	Stir fried together from the IP multicast and CAP patches above
+ *		Alan Cox <Alan.Cox@linux.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 <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 <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+
+
+/*
+ *	Device multicast list maintenance. This knows about such little matters as promiscuous mode and
+ *	converting from the list to the array the drivers use. At least until I fix the drivers up.
+ *
+ *	This is used both by IP and by the user level maintenance functions. Unlike BSD we maintain a usage count
+ *	on a given multicast address so that a casual user application can add/delete multicasts used by protocols
+ *	without doing damage to the protocols when it deletes the entries. It also helps IP as it tracks overlapping
+ *	maps.
+ */
+ 
+
+/*
+ *	Update the multicast list into the physical NIC controller.
+ */
+ 
+void dev_mc_upload(struct device *dev)
+{
+	struct dev_mc_list *dmi;
+	char *data;
+	
+	/* Promiscuous is promiscuous - so no filter needed */
+	
+	if(dev->flags&IFF_PROMISC)
+		return;
+		
+	/* Devices with no set multicast don't get set */
+	if(dev->set_multicast_list==NULL)
+		return;
+	if(dev->mc_count==0)
+	{
+		dev->set_multicast_list(dev,0,NULL);
+		return;
+	}
+	
+	data=kmalloc(dev->mc_count*dev->addr_len, GFP_KERNEL);
+	if(data==NULL)
+	{
+		printk("Unable to get memory to set multicast list on %s\n",dev->name);
+		return;
+	}
+	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
+	{
+		memcpy(data,dmi->dmi_addr, dmi->dmi_addrlen);
+		data+=dev->addr_len;
+	}
+	dev->set_multicast_list(dev,dev->mc_count,data);
+	kfree_s(data,dev->mc_count*dev->addr_len);
+}
+  
+/*
+ *	Delete a device level multicast
+ */
+ 
+void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
+{
+	struct dev_mc_list **dmi;
+	for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
+	{
+		if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
+		{
+			struct dev_mc_list *tmp= *dmi;
+			if((*dmi)->dmi_users-- && !all)
+				return;
+			*dmi=(*dmi)->next;
+			dev->mc_count--;
+			kfree_s(tmp,sizeof(*tmp));
+			return;
+		}
+	}
+	dev_mc_upload(dev);
+}
+
+/*
+ *	Add a device level multicast
+ */
+ 
+void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
+{
+	struct dev_mc_list *dmi;
+	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
+	{
+		if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
+		{
+			if(!newonly)
+				dmi->dmi_users++;
+			return;
+		}
+	}
+	dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi),GFP_KERNEL);
+	if(dmi==NULL)
+		return;	/* GFP_KERNEL so can't happen anyway */
+	memcpy(dmi->dmi_addr, addr, alen);
+	dmi->dmi_addrlen=alen;
+	dmi->next=dev->mc_list;
+	dmi->dmi_users=1;
+	dev->mc_list=dmi;
+	dev->mc_count++;
+	dev_mc_upload(dev);
+}
+
+/*
+ *	Discard multicast list when a device is downed
+ */
+
+void dev_mc_discard(struct device *dev)
+{
+	while(dev->mc_list!=NULL)
+	{
+		struct dev_mc_list *tmp=dev->mc_list;
+		dev->mc_list=dev->mc_list->next;
+		kfree_s(tmp,sizeof(*tmp));
+	}
+	dev->mc_count=0;
+}
+
diff -u --recursive --new-file v1.1.68/linux/net/inet/igmp.c linux/net/inet/igmp.c
--- v1.1.68/linux/net/inet/igmp.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/igmp.c	Wed Nov 30 21:53:47 1994
@@ -0,0 +1,363 @@
+/*
+ *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
+ *
+ *	Authors:
+ *		Alan Cox <Alan.Cox@linux.org>	
+ *
+ *	WARNING:
+ *		This is a 'prelimary' implementation... on your own head
+ *	be it.
+ *
+ *	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>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "igmp.h"
+
+#ifdef CONFIG_IP_MULTICAST
+
+
+/*
+ *	Timer management
+ */
+ 
+ 
+static void igmp_stop_timer(struct ip_mc_list *im)
+{
+	del_timer(&im->timer);
+	im->tm_running=0;
+}
+
+static int random(void)
+{
+	static unsigned long seed=152L;
+	seed=seed*69069L+1;
+	return seed^jiffies;
+}
+
+
+static void igmp_start_timer(struct ip_mc_list *im)
+{
+	int tv;
+	if(im->tm_running)
+		return;
+	tv=random()%(10*HZ);		/* Pick a number any number 8) */
+	im->timer.expires=tv;
+	add_timer(&im->timer);
+}
+ 
+/*
+ *	Send an IGMP report.
+ */
+
+#define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
+
+static void igmp_send_report(struct device *dev, unsigned long address, int type)
+{
+	struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
+	int tmp;
+	struct igmphdr *igh;
+	
+	if(skb==NULL)
+		return;
+	tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
+				skb->mem_len, 0, 1);
+	if(tmp<0)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return;
+	}
+	igh=(struct igmphdr *)(skb->data+tmp);
+	skb->len=tmp+sizeof(*igh);
+	igh->csum=0;
+	igh->unused=0;
+	igh->type=type;
+	igh->group=address;
+	igh->csum=ip_compute_csum((void *)igh,sizeof(*igh));
+	ip_queue_xmit(NULL,dev,skb,1);
+}
+
+
+static void igmp_timer_expire(unsigned long data)
+{
+	struct ip_mc_list *im=(struct ip_mc_list *)data;
+	igmp_stop_timer(im);
+	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
+}
+
+static void igmp_init_timer(struct ip_mc_list *im)
+{
+	im->tm_running=0;
+	init_timer(&im->timer);
+	im->timer.data=(unsigned long)im;
+	im->timer.function=&igmp_timer_expire;
+}
+	
+
+static void igmp_heard_report(struct device *dev, unsigned long address)
+{
+	struct ip_mc_list *im;
+	for(im=dev->ip_mc_list;im!=NULL;im=im->next)
+		if(im->multiaddr==address)
+			igmp_stop_timer(im);
+}
+
+static void igmp_heard_query(struct device *dev)
+{
+	struct ip_mc_list *im;
+	for(im=dev->ip_mc_list;im!=NULL;im=im->next)
+		if(!im->tm_running && im->multiaddr!=IGMP_ALL_HOSTS)
+			igmp_start_timer(im);
+}
+
+/*
+ *	Map a multicast IP onto multicast MAC for type ethernet.
+ */
+ 
+static void ip_mc_map(unsigned long addr, char *buf)
+{
+	addr=ntohl(addr);
+	buf[0]=0x01;
+	buf[1]=0x00;
+	buf[2]=0x5e;
+	buf[5]=addr&0xFF;
+	addr>>=8;
+	buf[4]=addr&0xFF;
+	addr>>=8;
+	buf[3]=addr&0x7F;
+}
+
+/*
+ *	Add a filter to a device
+ */
+ 
+void ip_mc_filter_add(struct device *dev, unsigned long addr)
+{
+	char buf[6];
+	if(dev->type!=ARPHRD_ETHER)
+		return;	/* Only do ethernet now */
+	ip_mc_map(addr,buf);	
+	dev_mc_add(dev,buf,ETH_ALEN,0);
+}
+
+/*
+ *	Remove a filter from a device
+ */
+ 
+void ip_mc_filter_del(struct device *dev, unsigned long addr)
+{
+	char buf[6];
+	if(dev->type!=ARPHRD_ETHER)
+		return;	/* Only do ethernet now */
+	ip_mc_map(addr,buf);	
+	dev_mc_delete(dev,buf,ETH_ALEN,0);
+}
+
+static void igmp_group_dropped(struct ip_mc_list *im)
+{
+	del_timer(&im->timer);
+	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
+	ip_mc_filter_del(im->interface, im->multiaddr);
+}
+
+static void igmp_group_added(struct ip_mc_list *im)
+{
+	igmp_init_timer(im);
+	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
+	ip_mc_filter_add(im->interface, im->multiaddr);
+}
+
+int igmp_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)
+{
+	/* This basically follows the spec line by line -- see RFC1112 */
+	struct igmphdr *igh=(struct igmphdr *)skb->h.raw;
+	
+	if(skb->ip_hdr->ttl!=1 || ip_compute_csum((void *)igh,sizeof(*igh)))
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	
+	if(igh->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
+		igmp_heard_query(dev);
+	if(igh->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==igh->group)
+		igmp_heard_report(dev,igh->group);
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+/*
+ *	Multicast list managers
+ */
+ 
+ 
+/*
+ *	A socket has joined a multicast group on device dev.
+ */
+  
+static void ip_mc_inc_group(struct device *dev, unsigned long addr)
+{
+	struct ip_mc_list *i;
+	for(i=dev->ip_mc_list;i!=NULL;i=i->next)
+	{
+		if(i->multiaddr==addr)
+		{
+			i->users++;
+			return;
+		}
+	}
+	i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
+	if(!i)
+		return;
+	i->users=1;
+	i->interface=dev;
+	i->next=dev->ip_mc_list;
+	igmp_group_added(i);
+	dev->ip_mc_list=i;
+}
+
+/*
+ *	A socket has left a multicast group on device dev
+ */
+	
+static void ip_mc_dec_group(struct device *dev, unsigned long addr)
+{
+	struct ip_mc_list **i;
+	for(i=&dev->ip_mc_list;i!=NULL;i=&(*i)->next)
+	{
+		if((*i)->multiaddr==addr)
+		{
+			if(--(*i)->users)
+				return;
+			else
+			{
+				struct ip_mc_list *tmp= *i;
+				igmp_group_dropped(tmp);
+				*i=(*i)->next;
+				kfree_s(tmp,sizeof(*tmp));
+			}
+		}
+	}
+}
+
+/*
+ *	Device going down: Clean up.
+ */
+ 
+void ip_mc_drop_device(struct device *dev)
+{
+	struct ip_mc_list *i;
+	struct ip_mc_list *j;
+	for(i=dev->ip_mc_list;i!=NULL;i=j)
+	{
+		j=i->next;
+		kfree_s(i,sizeof(*i));
+	}
+	dev->ip_mc_list=NULL;
+}
+
+/*
+ *	Join a socket to a group
+ */
+ 
+int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
+{
+	int unused= -1;
+	int i;
+	if(!MULTICAST(addr))
+		return -EINVAL;
+	if(!(dev->flags&IFF_MULTICAST))
+		return -EADDRNOTAVAIL;
+	if(sk->ip_mc_list==NULL)
+	{
+		if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
+			return -ENOMEM;
+		memset(sk->ip_mc_list,'\0',sizeof(*sk->ip_mc_list));
+	}
+	for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
+	{
+		if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
+			return -EADDRINUSE;
+		if(sk->ip_mc_list->multidev[i]==NULL)
+			unused=i;
+	}
+	
+	if(unused==-1)
+		return -ENOBUFS;
+	sk->ip_mc_list->multiaddr[unused]=addr;
+	sk->ip_mc_list->multidev[unused]=dev;
+	ip_mc_inc_group(dev,addr);
+	return 0;
+}
+
+/*
+ *	Ask a socket to leave a group.
+ */
+ 
+int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
+{
+	int i;
+	if(!MULTICAST(addr))
+		return -EINVAL;
+	if(!(dev->flags&IFF_MULTICAST))
+		return -EADDRNOTAVAIL;
+	if(sk->ip_mc_list==NULL)
+		return -EADDRNOTAVAIL;
+		
+	for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
+	{
+		if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
+		{
+			sk->ip_mc_list->multidev[i]=NULL;
+			ip_mc_dec_group(dev,addr);
+			return 0;
+		}
+	}
+	return -EADDRNOTAVAIL;
+}
+
+/*
+ *	A socket is closing.
+ */
+ 
+void ip_mc_drop_socket(struct sock *sk)
+{
+	int i;
+	
+	if(sk->ip_mc_list==NULL)
+		return;
+		
+	for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
+	{
+		if(sk->ip_mc_list->multidev[i])
+		{
+			ip_mc_dec_group(sk->ip_mc_list->multidev[i], sk->ip_mc_list->multiaddr[i]);
+			sk->ip_mc_list->multidev[i]=NULL;
+		}
+	}
+	kfree_s(sk->ip_mc_list,sizeof(*sk->ip_mc_list));
+	sk->ip_mc_list=NULL;
+}
+
+#endif
diff -u --recursive --new-file v1.1.68/linux/net/inet/igmp.h linux/net/inet/igmp.h
--- v1.1.68/linux/net/inet/igmp.h	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/igmp.h	Wed Nov 30 21:53:47 1994
@@ -0,0 +1,70 @@
+/*
+ *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
+ *
+ *	Authors:
+ *		Alan Cox <Alan.Cox@linux.org>	
+ *
+ *	WARNING:
+ *		This is a 'prelimary' implementation... on your own head
+ *	be it.
+ *
+ *	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 _IGMP_H
+#define _IGMP_H
+
+/*
+ *	IGMP protocol structures
+ */
+
+struct igmphdr
+{
+	unsigned char type;
+	unsigned char unused;
+	unsigned short csum;
+	unsigned long group;
+};
+
+#define IGMP_HOST_MEMBERSHIP_QUERY	0x11	/* From RFC1112 */
+#define IGMP_HOST_MEMBERSHIP_REPORT	0x12	/* Ditto */
+#define IGMP_HOST_LEAVE_MESSAGE		0x17	/* An extra BSD seems to send */
+
+				/* 224.0.0.1 */
+#define IGMP_ALL_HOSTS		htonl(0xE0000001L)
+
+/*
+ * struct for keeping the multicast list in
+ */
+
+struct ip_mc_socklist
+{
+	unsigned long multiaddr[IP_MAX_MEMBERSHIPS];	/* This is a speed trade off */
+	struct device *multidev[IP_MAX_MEMBERSHIPS];
+};
+
+struct ip_mc_list 
+{
+	struct device *interface;
+	unsigned long multiaddr;
+	struct ip_mc_list *next;
+	struct timer_list timer;
+	int tm_running;
+	int users;
+};
+ 
+extern struct ip_mc_list *ip_mc_head;
+
+
+#ifdef __KERNEL__
+extern int igmp_rcv(struct sk_buff *, struct device *, struct options *, unsigned long, unsigned short,
+	unsigned long, int , struct inet_protocol *);
+extern void ip_mc_drop_device(struct device *dev); 
+extern int ip_mc_join_group(struct sock *sk, struct device *dev, unsigned long addr);
+extern int ip_mc_leave_group(struct sock *sk, struct device *dev,unsigned long addr);
+extern void ip_mc_drop_socket(struct sock *sk);
+#endif
+#endif
diff -u --recursive --new-file v1.1.68/linux/net/inet/ip.c linux/net/inet/ip.c
--- v1.1.68/linux/net/inet/ip.c	Sat Nov 26 18:36:07 1994
+++ linux/net/inet/ip.c	Thu Dec  1 00:40:17 1994
@@ -11,6 +11,7 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Donald Becker, <becker@super.org>
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Richard Underwood
  *
  * Fixes:
  *		Alan Cox	:	Commented a couple of minor bits of surplus code
@@ -54,6 +55,9 @@
  *		Alan Cox	: 	IP options adjust sk->priority.
  *		Pedro Roque	:	Fix mtu/length error in ip_forward.
  *		Alan Cox	:	Avoid ip_chk_addr when possible.
+ *	Richard Underwood	:	IP multicasting.
+ *		Alan Cox	:	Cleaned up multicast handlers.
+ *		Alan Cox	:	RAW sockets demultiplex in the BSD style.
  *
  * To Fix:
  *		IP option processing is mostly not needed. ip_forward needs to know about routing rules
@@ -64,6 +68,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>
@@ -86,6 +91,9 @@
 #include "sock.h"
 #include "arp.h"
 #include "icmp.h"
+#include "raw.h"
+#include "igmp.h"
+#include <linux/ip_fw.h>
 
 #define CONFIG_IP_DEFRAG
 
@@ -99,7 +107,17 @@
  *	SNMP management statistics
  */
 
+#ifdef CONFIG_IP_FORWARDING
 struct ip_mib ip_statistics={1,64,};	/* Forwarding=Yes, Default TTL=64 */
+#else
+struct ip_mib ip_statistics={1,64,};	/* Forwarding=No, Default TTL=64 */
+#endif
+
+#ifdef CONFIG_IP_MULTICAST
+
+struct ip_mc_list *ip_mc_head=NULL;
+
+#endif
 
 /*
  *	Handle the issuing of an ioctl() request
@@ -1259,6 +1277,16 @@
 	unsigned char *ptr;	/* Data pointer */
 	unsigned long raddr;	/* Router IP address */
 
+	/* 
+	 *	See if we are allowed to forward this.
+	 */
+
+#ifdef CONFIG_IP_FIREWALL
+	if(!ip_fw_chk(skb->h.iph, ip_fw_fwd_chain))
+	{
+		return;
+	}
+#endif
 	/*
 	 *	According to the RFC, we must first decrease the TTL field. If
 	 *	that reaches zero, we must reply an ICMP control message telling
@@ -1403,6 +1431,14 @@
 		}
 		else
 		{
+#ifdef CONFIG_IP_ACCT		
+			/*
+			 *	Count mapping we shortcut
+			 */
+			 
+			ip_acct_cnt(iph,ip_acct_chain,1);
+#endif			
+			
 			/*
 			 *	Map service types to priority. We lie about
 			 *	throughput being low priority, but its a good
@@ -1428,6 +1464,7 @@
 int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct iphdr *iph = skb->h.iph;
+	struct sock *raw_sk=NULL;
 	unsigned char hash;
 	unsigned char flag = 0;
 	unsigned char opts_p = 0;	/* Set iff the packet has options. */
@@ -1437,7 +1474,6 @@
 	int brd=IS_MYADDR;
 	int is_frag=0;
 
-
 	ip_statistics.IpInReceives++;
 
 	/*
@@ -1461,7 +1497,21 @@
 		kfree_skb(skb, FREE_WRITE);
 		return(0);
 	}
+	
+	/*
+	 *	See if the firewall wants to dispose of the packet. 
+	 */
+
+#ifdef	CONFIG_IP_FIREWALL
+	
+	if(!LOOPBACK(iph->daddr) && !ip_fw_chk(iph,ip_fw_blk_chain))
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return 0;	
+	}
 
+#endif
+	
 	/*
 	 *	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.
@@ -1543,6 +1593,14 @@
 	}
 
 	/*
+	 *	Account for the packet
+	 */
+	 
+#ifdef CONFIG_IP_ACCT
+	ip_acct_cnt(iph,ip_acct_chain,1);
+#endif	
+
+	/*
 	 * Reassemble IP fragments.
 	 */
 
@@ -1554,6 +1612,8 @@
 			return 0;
 		iph=skb->h.iph;
 	}
+	
+		 
 
 	/*
 	 *	Point into the IP datagram, just past the header.
@@ -1561,7 +1621,39 @@
 
 	skb->ip_hdr = iph;
 	skb->h.raw += iph->ihl*4;
-
+	
+	/*
+	 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
+	 */
+	 
+	hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
+	
+	/* If there maybe a raw socket we must check - if not we don't care less */
+	if((raw_sk=raw_prot.sock_array[hash])!=NULL)
+	{
+		struct sock *sknext=NULL;
+		struct sk_buff *skb1;
+		raw_sk=get_sock_raw(raw_sk, hash,  iph->saddr, iph->daddr);
+		if(raw_sk)	/* Any raw sockets */
+		{
+			do
+			{
+				/* Find the next */
+				sknext=get_sock_raw(raw_sk->next, hash, iph->saddr, iph->daddr);
+				if(sknext)
+					skb1=skb_clone(skb, GFP_ATOMIC);
+				else
+					break;	/* One pending raw socket left */
+				if(skb1)
+					raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);
+				raw_sk=sknext;
+			}
+			while(raw_sk!=NULL);
+			/* Here either raw_sk is the last raw socket, or NULL if none */
+			/* We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy */
+		}
+	}
+	
 	/*
 	 *	skb->h.raw now points at the protocol beyond the IP header.
 	 */
@@ -1576,14 +1668,10 @@
        /*
 	* 	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.
+	* 	and then not for the last one. If there is a pending
+	*	raw delivery wait for that
 	*/
-		if (ipprot->copy)
+		if (ipprot->copy || raw_sk)
 		{
 			skb2 = skb_clone(skb, GFP_ATOMIC);
 			if(skb2==NULL)
@@ -1613,7 +1701,9 @@
 	 * ICMP reply messages get queued up for transmission...)
 	 */
 
-	if (!flag)
+	if(raw_sk!=NULL)	/* Shift to last raw user */
+		raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);
+	else if (!flag)		/* Free and report errors */
 	{
 		if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
@@ -1768,6 +1858,9 @@
 	 *	If the indicated interface is up and running, send the packet.
 	 */
 	ip_statistics.IpOutRequests++;
+#ifdef CONFIG_IP_ACCT
+	ip_acct_cnt(iph,ip_acct_chain,1);
+#endif	
 
 	if (dev->flags & IFF_UP)
 	{
@@ -1916,6 +2009,50 @@
 	reset_timer(sk, TIME_WRITE, sk->rto);
 }
 
+#ifdef CONFIG_IP_MULTICAST
+
+/*
+ *	Write an multicast group list table for the IGMP daemon to
+ *	read.
+ */
+ 
+int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length)
+{
+	off_t pos=0, begin=0;
+	struct ip_mc_list *im;
+	unsigned long flags;
+	int len=0;
+	
+	
+	len=sprintf(buffer,"Device    : Multicast\n");  
+	save_flags(flags);
+	cli();
+	
+	im=ip_mc_head;
+	
+	while(im!=NULL)
+	{
+		len+=sprintf(buffer+len,"%-10s: %08lX\n", im->interface->name, im->multiaddr);
+		pos=begin+len;
+		if(pos<offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos>offset+length)
+			break;
+		im=im->next;
+	}
+	restore_flags(flags);
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;	
+	return len;
+}
+
+
+#endif	
 /*
  *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
  *	an IP socket.
@@ -1928,7 +2065,9 @@
 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 {
 	int val,err;
-
+#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
+	struct ip_fw tmp_fw;
+#endif	
 	if (optval == NULL)
 		return(-EINVAL);
 
@@ -1957,6 +2096,227 @@
 				return -EINVAL;
 			sk->ip_ttl=val;
 			return 0;
+#ifdef CONFIG_IP_MULTICAST
+		case IP_MULTICAST_TTL: 
+		{
+			unsigned char ucval;
+
+			ucval=get_fs_byte((unsigned char *)optval);
+			printk("MC TTL %d\n", ucval);
+			if(ucval<1||ucval>255)
+                                return -EINVAL;
+			sk->ip_mc_ttl=(int)ucval;
+	                return 0;
+		}
+
+		case IP_MULTICAST_IF: 
+		{
+			/* Not fully tested */
+			struct in_addr addr;
+			struct device *dev=NULL;
+			
+			/*
+			 *	Check the arguments are allowable
+			 */
+
+			err=verify_area(VERIFY_READ, optval, sizeof(addr));
+			if(err)
+				return err;
+				
+			memcpy_fromfs(&addr,optval,sizeof(addr));
+			
+			printk("MC bind %s\n", in_ntoa(addr.s_addr));
+			
+			/*
+			 *	What address has been requested
+			 */
+			
+			if(addr.s_addr==INADDR_ANY)	/* Default */
+			{
+				sk->ip_mc_name[0]=0;
+				return 0;
+			}
+			
+			/*
+			 *	Find the device
+			 */
+			 
+			for(dev = dev_base; dev; dev = dev->next)
+			{
+				if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
+					(dev->pa_addr==addr.s_addr))
+					break;
+			}
+			
+			/*
+			 *	Did we find one
+			 */
+			 
+			if(dev) 
+			{
+				strcpy(sk->ip_mc_name,dev->name);
+				return 0;
+			}
+			return -EADDRNOTAVAIL;
+		}
+		
+		case IP_ADD_MEMBERSHIP: 
+		{
+		
+/*
+ *	FIXME: Add/Del membership should have a semaphore protecting them from re-entry
+ */
+			struct ip_mreq mreq;
+			static struct options optmem;
+			unsigned long route_src;
+			struct rtable *rt;
+			struct ip_mc_list *l=NULL;
+			struct device *dev=NULL;
+			int ct=0;
+			
+			/*
+			 *	Check the arguments.
+			 */
+
+			err=verify_area(VERIFY_READ, optval, sizeof(mreq));
+			if(err)
+				return err;
+
+			memcpy_fromfs(&mreq,optval,sizeof(mreq));
+
+			/* 
+			 *	Get device for use later
+			 */
+
+			if(mreq.imr_interface.s_addr==INADDR_ANY) 
+			{
+				/*
+				 *	Not set so scan.
+				 */
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,&optmem, &route_src))!=NULL)
+				{
+					dev=rt->rt_dev;
+					rt->rt_use--;
+				}
+			}
+			else
+			{
+				/*
+				 *	Find a suitable device.
+				 */
+				for(dev = dev_base; dev; dev = dev->next)
+				{
+					if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
+						(dev->pa_addr==mreq.imr_interface.s_addr))
+						break;
+				}
+			}
+			
+			/*
+			 *	No device, no cookies.
+			 */
+			 
+			if(!dev)
+				return -ENODEV;
+				
+			/*
+			 *	Join group.
+			 */
+			 
+			return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
+		
+		case IP_DROP_MEMBERSHIP: 
+		{
+			struct ip_mreq mreq;
+			struct rtable *rt;
+			static struct options optmem;
+                        unsigned long route_src;
+			struct device *dev=NULL;
+
+			/*
+			 *	Check the arguments
+			 */
+			 
+			err=verify_area(VERIFY_READ, optval, sizeof(mreq));
+			if(err)
+				return err;
+
+			memcpy_fromfs(&mreq,optval,sizeof(mreq));
+
+			/*
+			 *	Get device for use later 
+			 */
+ 
+			if(mreq.imr_interface.s_addr==INADDR_ANY) 
+			{
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,&optmem, &route_src))!=NULL)
+			        {
+					dev=rt->rt_dev;
+					rt->rt_use--;
+				}
+			}
+			else 
+			{
+				for(dev = dev_base; dev; dev = dev->next)
+				{
+					if((dev->flags&IFF_UP)&& (dev->flags&IFF_MULTICAST)&&
+							(dev->pa_addr==mreq.imr_interface.s_addr))
+						break;
+				}
+			}
+			
+			/*
+			 *	Did we find a suitable device.
+			 */
+			 
+			if(!dev)
+				return -ENODEV;
+				
+			/*
+			 *	Leave group
+			 */
+			 
+			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
+#endif			
+#ifdef CONFIG_IP_FIREWALL
+		case IP_FW_ADD_BLK:
+		case IP_FW_DEL_BLK:
+		case IP_FW_ADD_FWD:
+		case IP_FW_DEL_FWD:
+		case IP_FW_CHK_BLK:
+		case IP_FW_CHK_FWD:
+		case IP_FW_FLUSH:
+		case IP_FW_POLICY:
+			if(!suser())
+				return -EPERM;
+			if(optlen>sizeof(tmp_fw) || optlen<1)
+				return -EINVAL;
+			err=verify_area(VERIFY_READ,optval,optlen);
+			if(err)
+				return err;
+			memcpy_fromfs(&tmp_fw,optval,optlen);
+			err=ip_fw_ctl(optname, &tmp_fw,optlen);
+			return -err;	/* -0 is 0 after all */
+			
+#endif
+#ifdef CONFIG_IP_ACCT
+		case IP_ACCT_DEL:
+		case IP_ACCT_ADD:
+		case IP_ACCT_FLUSH:
+		case IP_ACCT_ZERO:
+			if(!suser())
+				return -EPERM;
+			if(optlen>sizeof(tmp_fw) || optlen<1)
+				return -EINVAL;
+			err=verify_area(VERIFY_READ,optval,optlen);
+			if(err)
+				return err;
+			memcpy_fromfs(&tmp_fw, optval,optlen);
+			err=ip_acct_ctl(optname, &tmp_fw,optlen);
+			return -err;	/* -0 is 0 after all */
+#endif
 		/* IP_OPTIONS and friends go here eventually */
 		default:
 			return(-ENOPROTOOPT);
@@ -1971,7 +2331,10 @@
 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 {
 	int val,err;
-
+#ifdef CONFIG_IP_MULTICAST
+	int len;
+#endif
+	
 	if(level!=SOL_IP)
 		return -EOPNOTSUPP;
 
@@ -1983,6 +2346,22 @@
 		case IP_TTL:
 			val=sk->ip_ttl;
 			break;
+#ifdef CONFIG_IP_MULTICAST			
+		case IP_MULTICAST_TTL:
+			val=sk->ip_mc_ttl;
+			break;
+		case IP_MULTICAST_IF:
+			err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
+			if(err)
+  				return err;
+  			len=strlen(sk->ip_mc_name);
+  			err=verify_area(VERIFY_WRITE, optval, len);
+		  	if(err)
+  				return err;
+  			put_fs_long(len,(unsigned long *) optlen);
+			memcpy_tofs((void *)optval,sk->ip_mc_name, len);
+			return 0;
+#endif
 		default:
 			return(-ENOPROTOOPT);
 	}
diff -u --recursive --new-file v1.1.68/linux/net/inet/ip.h linux/net/inet/ip.h
--- v1.1.68/linux/net/inet/ip.h	Mon Apr 18 11:38:48 1994
+++ linux/net/inet/ip.h	Wed Nov 30 21:53:47 1994
@@ -36,6 +36,13 @@
 
 #define IP_FRAG_TIME	(30 * HZ)		/* fragment lifetime	*/
 
+#ifdef CONFIG_IP_MULTICAST
+extern void		ip_mc_dropsocket(struct sock *);
+extern void		ip_mc_dropdevice(struct device *dev);
+extern int		ip_mc_procinfo(char *, char **, off_t, int);
+#define MULTICAST(x)	(IN_MULTICAST(htonl(x)))
+#endif
+ 
 
 /* Describe an IP fragment. */
 struct ipfrag {
diff -u --recursive --new-file v1.1.68/linux/net/inet/ip_fw.c linux/net/inet/ip_fw.c
--- v1.1.68/linux/net/inet/ip_fw.c	Thu Jan  1 02:00:00 1970
+++ linux/net/inet/ip_fw.c	Thu Dec  1 00:57:31 1994
@@ -0,0 +1,907 @@
+/*
+ *	IP firewalling code. This is taken from 4.4BSD. Please note the 
+ *	copyright message below. As per the GPL it must be maintained
+ *	and the licenses thus do not conflict. While this port is subject
+ *	to the GPL I also place my modifications under the original 
+ *	license in recognition of the original copyright. 
+ *
+ *	Ported from BSD to Linux,
+ *		Alan Cox 22/Nov/1994.
+ *
+ *	All the real work was done by .....
+ */
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "icmp.h"
+#include <linux/ip_fw.h>
+
+/*
+ *	Implement IP packet firewall
+ */
+
+#ifdef CONFIG_IP_FIREWALL
+struct ip_fw *ip_fw_fwd_chain;
+struct ip_fw *ip_fw_blk_chain;
+static int ip_fw_policy=1;
+#endif
+#ifdef CONFIG_IP_ACCT
+struct ip_fw *ip_acct_chain;
+#endif
+
+
+extern inline void print_ip(unsigned long xaddr)
+{
+	unsigned long addr = ntohl(xaddr);
+	printk("%ld.%ld.%ld.%ld",(addr>>24) & 0xff,
+                         (addr>>16)&0xff,
+                         (addr>>8)&0xff,
+                         addr&0xFF);
+}                  
+
+
+/*
+ *	Returns 1 if the port is matched by the vector, 0 otherwise
+ */
+
+extern inline int port_match(unsigned short *portptr,int nports,unsigned short port,int range_flag)
+{
+	if (!nports)
+		return 1;
+	if ( range_flag ) 
+	{
+		if ( portptr[0] <= port && port <= portptr[1] ) 
+		{
+			return( 1 );
+		}
+		nports -= 2;
+		portptr += 2;
+	}
+	while ( nports-- > 0 ) 
+	{
+		if ( *portptr++ == port ) 
+		{
+			return( 1 );
+		}
+	}
+	return(0);
+}
+
+
+/*
+ *	Returns 0 if packet should be dropped, 1 or more if it should be accepted
+ */
+
+#ifdef CONFIG_IP_FIREWALL
+
+int ip_fw_chk(struct iphdr *ip, struct ip_fw *chain)
+{
+	unsigned long src, dst;
+	char got_proto=0;
+	int frwl_proto, proto=0;
+	struct ip_fw *f;
+	unsigned short src_port=0, dst_port=0;
+	unsigned short *portptr=(unsigned short *)&(((u_int *)ip)[ip->ihl]);
+
+	if (!chain) 
+		return(1);     /* If no chain , always say Ok to packet */
+
+	src = ip->saddr;
+	dst = ip->daddr;
+
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+	{
+		printk("packet ");
+		switch(ip->protocol) 
+		{
+			case IPPROTO_TCP:
+				printf("TCP ");
+				break;
+			case IPPROTO_UDP:
+				printf("UDP ");
+				break;
+			case IPPROTO_ICMP:
+				printf("ICMP:%d ",((char *)portptr)[0]&0xff);
+				break;
+			default:
+				printf("p=%d ",ip->protocol);
+				break;
+		}
+		print_ip(ip->saddr);
+		if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) 
+		{
+			printf(":%d ",ntohs(portptr[0]));
+		}
+		print_ip(ip->daddr);
+		if ( ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) 
+		{
+			printf(":%d ",ntohs(portptr[1]));
+		}
+		printf("\n");
+	}
+#endif
+
+	for (f=chain;f;f=f->next) 
+	{
+		if ((src&f->src_mask.s_addr)==f->src.s_addr
+			&&  (dst&f->dst_mask.s_addr)==f->dst.s_addr) 
+		{
+			frwl_proto=f->flags&IP_FW_F_KIND;
+			if (frwl_proto==IP_FW_F_ALL) 
+			{
+				/* Universal frwl - we've got a match! */
+
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+				printf("universal frwl match\n");
+#endif
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+				if (!(f->flags & IP_FW_F_ACCEPT))
+					goto bad_packet;
+				return 1;
+#else
+				return( f->flags & IP_FW_F_ACCEPT );
+#endif
+			}
+			else
+			{
+				/*
+				 *	Specific firewall - packet's protocol must match firewall's
+				 */
+				if (!got_proto) 
+				{
+					/*
+	 				 * We still had not determined the protocol
+					 * of this packet,now the time to do so.
+					 */
+					switch(ip->protocol) 
+					{
+						case IPPROTO_TCP:
+							/*
+							 * 	First two shorts in TCP are src/dst ports
+							 */
+							proto=IP_FW_F_TCP;
+							src_port=ntohs(portptr[0]);
+							dst_port=ntohs(portptr[1]);
+							break;
+						case IPPROTO_UDP:
+							/*
+							 *	First two shorts in UDP are src/dst ports
+			 				 */
+							proto = IP_FW_F_UDP;
+							src_port = ntohs(portptr[0]);
+							dst_port = ntohs(portptr[1]);
+							break;
+						case IPPROTO_ICMP:
+							proto=IP_FW_F_ICMP;
+							break;
+						default:
+							proto=IP_FW_F_ALL;
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+							printf("non TCP/UDP packet\n");
+#endif
+					}
+					got_proto=1;
+				} 
+				/*
+				 * At this moment we surely know the protocol of this
+				 * packet and we'll check if it matches,then proceed futher..
+				 */
+				if (proto==frwl_proto) 
+				{
+	
+					if (proto==IP_FW_F_ICMP || (port_match(&f->ports[0],f->n_src_p,src_port,
+						f->flags&IP_FW_F_SRNG) &&
+					        port_match(&f->ports[f->n_src_p],f->n_dst_p,dst_port,
+						f->flags&IP_FW_F_DRNG))) 
+					{
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+						if (!(f->flags & IP_FW_F_ACCEPT))
+							goto bad_packet;
+						return 1;
+#else
+						return( f->flags & IP_FW_F_ACCEPT);
+#endif
+					} /* Ports match */
+				} /* Proto matches */
+			}  /* ALL/Specific */
+		} /* IP addr/mask matches */
+	} /* Loop */
+	
+	/*
+	 * If we get here then none of the firewalls matched.
+	 * So now we relay on policy defined by user-unmatched packet can
+	 * be ever accepted or rejected...
+	 */
+
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+	if (!(ip_fw_policy))
+		goto bad_packet;
+	return 1;
+#else
+	return(ip_fw_policy);
+#endif
+
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+bad_packet:
+	/*
+	 * VERY ugly piece of code which actually
+	 * makes kernel printf for denied packets...
+	 */
+	if (f->flags&IP_FW_F_PRN) 
+	{
+		printf("ip_fw_chk says no to ");
+		switch(ip->protocol) 
+		{
+			case IPPROTO_TCP:
+				printf("TCP ");
+				break;
+			case IPPROTO_UDP:
+				printf("UDP ");
+				break;
+			case IPPROTO_ICMP:
+				printf("ICMP:%d ",((char *)portptr)[0]&0xff);
+				break;
+			default:
+				printf("p=%d ",ip->protocol);
+				break;
+		}
+		print_ip(ip->saddr);
+		if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP) 
+		{
+			printf(":%d ",ntohs(portptr[0]));
+		}
+		else
+		{
+			printf("\n");
+		}
+		print_ip(ip->daddr);
+		if ( ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP ) 
+		{
+			printf(":%d ",ntohs(portptr[1]));
+		}
+		printf("\n");
+	}
+	return(0);
+#endif
+}
+#endif /* CONFIG_IP_FIREWALL */
+
+
+
+
+#ifdef CONFIG_IP_ACCT
+void ip_acct_cnt(struct iphdr *ip,struct ip_fw *chain,int nh_conv)
+{
+	unsigned long src, dst;
+	char got_proto=0,rev=0;
+	int frwl_proto, proto=0;
+	struct ip_fw *f;
+	unsigned short src_port=0, dst_port=0;
+	unsigned short *portptr=(unsigned short *)&(((u_int *)ip)[ip->ihl]);
+
+	if (!chain) 
+		return;     
+
+	src = ip->saddr;
+	dst = ip->daddr;
+
+	for (f=chain;f;f=f->next) 
+	{
+		if ((src&f->src_mask.s_addr)==f->src.s_addr
+			&&  (dst&f->dst_mask.s_addr)==f->dst.s_addr) 
+		{
+			rev=0;
+			goto addr_match;
+		}
+	 	if  ((f->flags&IP_FW_F_BIDIR) &&
+		    ((src&f->src_mask.s_addr)==f->dst.s_addr
+		&&  (dst&f->dst_mask.s_addr)==f->src.s_addr)) 
+		{ 
+			rev=1;
+			goto addr_match;
+		}
+		continue;
+addr_match:
+		frwl_proto=f->flags&IP_FW_F_KIND;
+		if (frwl_proto==IP_FW_F_ALL) 
+		{
+			/*	Universal frwl - we've got a match! */
+     			f->p_cnt++;	/*	Rise packet count */
+
+			/*
+			 *	Rise byte count, if need to convert from host to network byte order,do it.
+		     	 */
+		     	 
+			if (nh_conv)		    
+				f->b_cnt+=ntohs(ip->tot_len);
+			else
+				f->b_cnt+=ip->tot_len;
+		}
+		else
+		{
+			/*
+			 *	Specific firewall - packet's protocol must match firewall's
+			 */
+			 
+			if (!got_proto) 
+			{
+				/*
+ 				 *	We still had not determined the protocol
+				 *	of this packet,now the time to do so.
+				 */
+				switch(ip->protocol) 
+				{
+				    	case IPPROTO_TCP:
+						/*
+						 *	First two shorts in TCP are src/dst ports
+						 */
+						proto=IP_FW_F_TCP;
+						src_port=ntohs(portptr[0]);
+						dst_port=ntohs(portptr[1]);
+						break;
+	    				case IPPROTO_UDP:
+						/*
+						 * First two shorts in UDP are src/dst ports
+						 */
+						proto = IP_FW_F_UDP;
+						src_port = ntohs(portptr[0]);
+						dst_port = ntohs(portptr[1]);
+						break;
+	    				case IPPROTO_ICMP:
+						proto=IP_FW_F_ICMP;
+						break;
+					default:
+						proto=IP_FW_F_ALL;
+				}
+				got_proto=1;
+			} 
+			/*
+			 * At this moment we surely know the protocol of this
+			 * packet and we'll check if it matches,then proceed futher..
+			 */
+			if (proto==frwl_proto) 
+			{
+
+				if ((proto==IP_FW_F_ICMP ||
+					(port_match(&f->ports[0],f->n_src_p,src_port,
+					f->flags&IP_FW_F_SRNG) &&
+					port_match(&f->ports[f->n_src_p],f->n_dst_p,dst_port,
+					f->flags&IP_FW_F_DRNG)))
+					|| ((rev)   
+						&& (port_match(&f->ports[0],f->n_src_p,dst_port,
+                        	                f->flags&IP_FW_F_SRNG)
+						&& port_match(&f->ports[f->n_src_p],f->n_dst_p,src_port,
+	                       	                f->flags&IP_FW_F_DRNG))))
+				{
+					f->p_cnt++;                   /* Rise packet count */
+					/*
+					 * Rise byte count, if need to convert from host to network byte order,do it.
+					 */
+					if (nh_conv)		    
+						f->b_cnt+=ntohs(ip->tot_len);
+					else
+						f->b_cnt+=ip->tot_len;
+				} /* Ports match */
+			} /* Proto matches */
+		}  /* ALL/Specific */
+	} /* IP addr/mask matches */
+} /* End of whole function */
+#endif /* CONFIG_IP_ACCT */
+
+#ifdef CONFIG_IP_ACCT
+
+static void zero_fw_chain(struct ip_fw *chainptr)
+{
+	struct ip_fw *ctmp=chainptr;
+	while(ctmp) 
+	{
+		ctmp->p_cnt=0l;
+		ctmp->b_cnt=0l;
+		ctmp=ctmp->next;
+	}
+}
+
+#endif
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+static void free_fw_chain(struct ip_fw **chainptr)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	while ( *chainptr != NULL ) 
+	{
+		struct ip_fw *ftmp;
+		ftmp = *chainptr;
+		*chainptr = ftmp->next;
+		kfree_s(ftmp,sizeof(*ftmp));
+	}
+	restore_flags(flags);
+}
+
+#endif  /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
+
+#ifdef CONFIG_IP_FIREWALL
+
+static int add_to_chain(struct ip_fw **chainptr, struct ip_fw *frwl)
+{
+	struct ip_fw *ftmp;
+	struct ip_fw *chtmp=NULL;
+	struct ip_fw *chtmp_prev=NULL;
+	unsigned long flags;
+	unsigned long m_src_mask,m_dst_mask;
+	unsigned long n_sa,n_da,o_sa,o_da,o_sm,o_dm,n_sm,n_dm;
+	unsigned short n_sr,n_dr,o_sr,o_dr; 
+	unsigned short oldkind,newkind;
+	int addb4=0;
+	int n_o,n_n;
+	
+	save_flags(flags);
+	
+	ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+	if ( ftmp == NULL ) 
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl:  malloc said no\n");
+#endif
+		return( ENOSPC );
+	}
+
+	memcpy(ftmp, frwl, sizeof( struct ip_fw ) );
+	ftmp->p_cnt=0L;
+	ftmp->b_cnt=0L;
+
+	ftmp->next = NULL;
+
+	cli();
+	
+	if (*chainptr==NULL)
+	{
+		*chainptr=ftmp;
+	}
+	else
+	{
+		chtmp_prev=NULL;
+		for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->next) 
+		{
+			addb4=0;
+			newkind=ftmp->flags & IP_FW_F_KIND;
+			oldkind=chtmp->flags & IP_FW_F_KIND;
+	
+			if (newkind!=IP_FW_F_ALL 
+				&&  oldkind!=IP_FW_F_ALL
+				&&  oldkind!=newkind) 
+			{
+				chtmp_prev=chtmp;
+				continue;
+			}
+
+			/*
+			 *	Very very *UGLY* code...
+			 *	Sorry,but i had to do this....
+			 */
+
+			n_sa=ntohl(ftmp->src.s_addr);
+			n_da=ntohl(ftmp->dst.s_addr);
+			n_sm=ntohl(ftmp->src_mask.s_addr);
+			n_dm=ntohl(ftmp->dst_mask.s_addr);
+
+			o_sa=ntohl(chtmp->src.s_addr);
+			o_da=ntohl(chtmp->dst.s_addr);
+			o_sm=ntohl(chtmp->src_mask.s_addr);
+			o_dm=ntohl(chtmp->dst_mask.s_addr);
+
+			m_src_mask = o_sm & n_sm;
+			m_dst_mask = o_dm & n_dm;
+
+			if ((o_sa & m_src_mask) == (n_sa & m_src_mask)) 
+			{
+				if (n_sm > o_sm) 
+					addb4++;
+				if (n_sm < o_sm) 
+					addb4--;
+			}
+		
+			if ((o_da & m_dst_mask) == (n_da & m_dst_mask)) 
+			{
+				if (n_dm > o_dm)
+					addb4++;
+				if (n_dm < o_dm)
+					addb4--;
+			}
+
+			if (((o_da & o_dm) == (n_da & n_dm))
+               			&&((o_sa & o_sm) == (n_sa & n_sm)))
+			{
+				if (newkind!=IP_FW_F_ALL &&
+					oldkind==IP_FW_F_ALL)
+					addb4++;
+				if (newkind==oldkind && (oldkind==IP_FW_F_TCP
+					||  oldkind==IP_FW_F_UDP)) 
+				{
+	
+					/*
+					 * 	Here the main idea is to check the size
+					 * 	of port range which the frwl covers
+					 * 	We actually don't check their values but
+					 *	just the wideness of range they have
+					 *	so that less wide ranges or single ports
+					 *	go first and wide ranges go later. No ports
+					 *	at all treated as a range of maximum number
+					 *	of ports.
+					 */
+
+					if (ftmp->flags & IP_FW_F_SRNG) 
+						n_sr=ftmp->ports[1]-ftmp->ports[0];
+					else 
+						n_sr=(ftmp->n_src_p)?ftmp->n_src_p : 0xFFFF;
+						
+					if (chtmp->flags & IP_FW_F_SRNG) 
+						o_sr=chtmp->ports[1]-chtmp->ports[0];
+					else 
+						o_sr=(chtmp->n_src_p)?chtmp->n_src_p : 0xFFFF;
+
+					if (n_sr<o_sr)
+						addb4++;
+					if (n_sr>o_sr)
+						addb4--;
+					
+					n_n=ftmp->n_src_p;
+					n_o=chtmp->n_src_p;
+	
+					/*
+					 * Actually this cannot happen as the frwl control
+					 * procedure checks for number of ports in source and
+					 * destination range but we will try to be more safe.
+					 */
+					 
+					if ((n_n>(IP_FW_MAX_PORTS-2)) ||
+						(n_o>(IP_FW_MAX_PORTS-2)))
+						goto skip_check;
+
+					if (ftmp->flags & IP_FW_F_DRNG) 
+					       n_dr=ftmp->ports[n_n+1]-ftmp->ports[n_n];
+					else 
+					       n_dr=(ftmp->n_dst_p)? ftmp->n_dst_p : 0xFFFF;
+
+					if (chtmp->flags & IP_FW_F_DRNG) 
+						o_dr=chtmp->ports[n_o+1]-chtmp->ports[n_o];
+					else 
+						o_dr=(chtmp->n_dst_p)? chtmp->n_dst_p : 0xFFFF;
+					if (n_dr<o_dr)
+						addb4++;
+					if (n_dr>o_dr)
+						addb4--;
+skip_check:
+				}
+			}
+			if (addb4>0) 
+			{
+				if (chtmp_prev) 
+				{
+					chtmp_prev->next=ftmp; 
+					ftmp->next=chtmp;
+				} 
+				else 
+				{
+					*chainptr=ftmp;
+					ftmp->next=chtmp;
+				}
+				restore_flags(flags);
+				return 0;
+			}
+			chtmp_prev=chtmp;
+		}
+	}
+	
+	if (chtmp_prev)
+		chtmp_prev->next=ftmp;
+	else
+	{
+        	*chainptr=ftmp;
+		printk("ip_fw: add_to_chain: Can't happen");
+	}
+	restore_flags(flags);
+	return(0);
+}
+
+static int del_from_chain(struct ip_fw **chainptr, struct ip_fw *frwl)
+{
+	struct ip_fw 	*ftmp,*ltmp;
+	unsigned short	tport1,tport2,tmpnum;
+	char		matches,was_found;
+	unsigned long 	flags;
+
+	save_flags(flags);
+	cli();
+	
+	ftmp=*chainptr;
+
+	if ( ftmp == NULL ) 
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl:  chain is empty\n");
+#endif
+		restore_flags(flags);
+		return( EINVAL );
+	}
+
+	ltmp=NULL;
+	was_found=0;
+
+	while( ftmp != NULL )
+	{
+		matches=1;
+		if ((memcmp(&ftmp->src,&frwl->src,sizeof(struct in_addr))) 
+     			|| (memcmp(&ftmp->src_mask,&frwl->src_mask,sizeof(struct in_addr)))
+     			|| (memcmp(&ftmp->dst,&frwl->dst,sizeof(struct in_addr)))
+     			|| (memcmp(&ftmp->dst_mask,&frwl->dst_mask,sizeof(struct in_addr)))
+     			|| (ftmp->flags!=frwl->flags))
+        		matches=0;
+
+		tport1=ftmp->n_src_p+ftmp->n_dst_p;
+		tport2=frwl->n_src_p+frwl->n_dst_p;
+		if (tport1!=tport2)
+		        matches=0;
+		else if (tport1!=0)
+		{
+			for (tmpnum=0;tmpnum < tport1 && tmpnum < IP_FW_MAX_PORTS;tmpnum++)
+        		if (ftmp->ports[tmpnum]!=frwl->ports[tmpnum])
+        			matches=0;
+		}
+		if(matches)
+		{
+			was_found=1;
+			if (ltmp)
+			{
+				ltmp->next=ftmp->next;
+				kfree_s(ftmp,sizeof(*ftmp));
+				ftmp=ltmp->next;
+        		}
+      			else
+      			{
+      				*chainptr=ftmp->next; 
+	 			kfree_s(ftmp,sizeof(*ftmp));
+				ftmp=*chainptr;
+			}       
+		}
+		else
+		{
+			ltmp = ftmp;
+			ftmp = ftmp->next;
+		 }
+	}
+	restore_flags(flags);
+	if (was_found)
+		return 0;
+	else
+		return(EINVAL);
+}
+
+#endif /* CONFIG_IP_FIREWALL */
+
+struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
+{
+
+	if ( len != sizeof(struct ip_fw) )
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl: len=%d, want %d\n",m->m_len,
+					sizeof(struct ip_fw));
+#endif
+		return(NULL);
+	}
+
+	if ( (frwl->flags & ~IP_FW_F_MASK) != 0 )
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl: undefined flag bits set (flags=%x)\n",
+			frwl->flags);
+#endif
+		return(NULL);
+	}
+
+	if ( (frwl->flags & IP_FW_F_SRNG) && frwl->n_src_p < 2 ) 
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl: src range set but n_src_p=%d\n",
+			frwl->n_src_p);
+#endif
+		return(NULL);
+	}
+
+	if ( (frwl->flags & IP_FW_F_DRNG) && frwl->n_dst_p < 2 ) 
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl: dst range set but n_dst_p=%d\n",
+			frwl->n_dst_p);
+#endif
+		return(NULL);
+	}
+
+	if ( frwl->n_src_p + frwl->n_dst_p > IP_FW_MAX_PORTS ) 
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printf("ip_fw_ctl: too many ports (%d+%d)\n",
+			frwl->n_src_p,frwl->n_dst_p);
+#endif
+		return(NULL);
+	}
+
+	return frwl;
+}
+
+
+
+
+#ifdef CONFIG_IP_ACCT
+int ip_acct_ctl(int stage, void *m, int len)
+{
+	if ( stage == IP_ACCT_FLUSH )
+	{
+		free_fw_chain(&ip_acct_chain);
+		return(0);
+	}  
+	if ( stage == IP_ACCT_ZERO )
+	{
+		zero_fw_chain(ip_acct_chain);
+		return(0);
+	}
+	if ( stage == IP_ACCT_ADD
+	  || stage == IP_ACCT_DEL
+	   )
+	{
+		struct ip_fw *frwl;
+
+		if (!(frwl=check_ipfw_struct(m,len)))
+			return (EINVAL);
+
+		switch (stage) 
+		{
+			case IP_ACCT_ADD:
+				return( add_to_chain(&ip_acct_chain,frwl));
+		    	case IP_ACCT_DEL:
+				return( del_from_chain(&ip_acct_chain,frwl));
+			default:
+				/*
+ 				 *	Should be panic but... (Why ??? - AC)
+				 */
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+				printf("ip_acct_ctl:  unknown request %d\n",stage);
+#endif
+				return(EINVAL);
+		}
+	}
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+	printf("ip_acct_ctl:  unknown request %d\n",stage);
+#endif
+	return(EINVAL);
+}
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+int ip_fw_ctl(int stage, void *m, int len)
+{
+	if ( stage == IP_FW_FLUSH )
+	{
+		free_fw_chain(&ip_fw_blk_chain);
+		free_fw_chain(&ip_fw_fwd_chain);
+		return(0);
+	}  
+
+	if ( stage == IP_FW_POLICY )
+	{
+		int *tmp_policy_ptr;
+		tmp_policy_ptr=(int *)m;
+		if ((*tmp_policy_ptr)!=1 && (*tmp_policy_ptr)!=0)
+			return (EINVAL);
+		ip_fw_policy=*tmp_policy_ptr;
+		return 0;
+	}
+
+	if ( stage == IP_FW_CHK_BLK 
+		|| stage == IP_FW_CHK_FWD )
+	{
+		struct iphdr *ip;
+
+		if ( len < sizeof(struct iphdr) + 2 * sizeof(unsigned short) )
+		{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+			printf("ip_fw_ctl: len=%d, want at least %d\n",
+				len,sizeof(struct ip) + 2 * sizeof(unsigned short));
+#endif
+			return( EINVAL );
+		}
+
+	 	ip = (struct iphdr *)m;
+
+		if ( ip->ihl != sizeof(struct iphdr) / sizeof(int))
+		{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+			printf("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
+					sizeof(struct ip)/sizeof(int));
+#endif
+			return(EINVAL);
+		}
+
+		if ( ip_fw_chk(ip,
+			stage == IP_FW_CHK_BLK ?
+	                ip_fw_blk_chain : ip_fw_fwd_chain )
+		       ) 
+			return(0);
+	    	else	
+			return(EACCES);
+	}
+
+/*
+ *	Here we really working hard-adding new elements
+ *	to blocking/forwarding chains or deleting'em
+ */
+
+	if ( stage == IP_FW_ADD_BLK || stage == IP_FW_ADD_FWD
+		|| stage == IP_FW_DEL_BLK || stage == IP_FW_DEL_FWD
+		)
+	{
+		struct ip_fw *frwl;
+		frwl=check_ipfw_struct(m,len);
+		if (frwl==NULL)
+			return (EINVAL);
+		
+		switch (stage) 
+		{
+			case IP_FW_ADD_BLK:
+				return(add_to_chain(&ip_fw_blk_chain,frwl));
+			case IP_FW_ADD_FWD:
+				return(add_to_chain(&ip_fw_fwd_chain,frwl));
+			case IP_FW_DEL_BLK:
+				return(del_from_chain(&ip_fw_blk_chain,frwl));
+			case IP_FW_DEL_FWD: 
+				return(del_from_chain(&ip_fw_fwd_chain,frwl));
+			default:
+			/*
+	 		 *	Should be panic but... (Why are BSD people panic obsessed ??)
+			 */
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+				printf("ip_fw_ctl:  unknown request %d\n",stage);
+#endif
+				return(EINVAL);
+		}
+	} 
+
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+	printf("ip_fw_ctl:  unknown request %d\n",stage);
+#endif
+	return(EINVAL);
+}
+#endif /* CONFIG_IP_FIREWALL */
diff -u --recursive --new-file v1.1.68/linux/net/inet/protocol.c linux/net/inet/protocol.c
--- v1.1.68/linux/net/inet/protocol.c	Wed Aug 10 19:26:50 1994
+++ linux/net/inet/protocol.c	Wed Nov 30 21:53:47 1994
@@ -31,6 +31,7 @@
 #include <linux/in.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <linux/timer.h>
 #include "ip.h"
 #include "protocol.h"
 #include "tcp.h"
@@ -38,6 +39,7 @@
 #include "sock.h"
 #include "icmp.h"
 #include "udp.h"
+#include "igmp.h"
 
 
 static struct inet_protocol tcp_protocol = {
@@ -75,8 +77,23 @@
   "ICMP"		/* name			*/
 };
 
-
+#ifndef CONFIG_IP_MULTICAST
 struct inet_protocol *inet_protocol_base = &icmp_protocol;
+#else
+static struct inet_protocol igmp_protocol = {
+  igmp_rcv,		/* IGMP handler		*/
+  NULL,			/* IGMP never fragments anyway */
+  NULL,			/* IGMP error control	*/
+  &icmp_protocol,	/* next			*/
+  IPPROTO_IGMP,		/* protocol ID		*/
+  0,			/* copy			*/
+  NULL,			/* data			*/
+  "IGMP"		/* name			*/
+};
+
+struct inet_protocol *inet_protocol_base = &igmp_protocol;
+#endif
+
 struct inet_protocol *inet_protos[MAX_INET_PROTOS] = {
   NULL
 };
diff -u --recursive --new-file v1.1.68/linux/net/inet/raw.c linux/net/inet/raw.c
--- v1.1.68/linux/net/inet/raw.c	Tue Nov 22 15:41:15 1994
+++ linux/net/inet/raw.c	Wed Nov 30 21:53:47 1994
@@ -26,6 +26,7 @@
  *		Alan Cox	:	Cleaned up old debugging
  *		Alan Cox	:	Use new kernel side addresses
  *	Arnt Gulbrandsen	:	Fixed MSG_DONTROUTE in raw sockets.
+ *		Alan Cox	:	BSD style RAW socket demultiplexing.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -89,35 +90,15 @@
 
 /*
  *	This should be the easiest of all, all we do is
- *	copy it into a buffer.
+ *	copy it into a buffer. All demultiplexing is done
+ *	in ip.c
  */
 
-int raw_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)
+int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, long saddr, long daddr)
 {
-	struct sock *sk;
-
-	if (skb == NULL)
-		return(0);
-  	
-	if (protocol == NULL) 
-	{
-		kfree_skb(skb, FREE_READ);
-		return(0);
-	}
-  
-	sk = (struct sock *) protocol->data;
-	if (sk == NULL) 
-	{
-		kfree_skb(skb, FREE_READ);
-		return(0);
-	}
-
 	/* Now we need to copy this into memory. */
-
 	skb->sk = sk;
-	skb->len = len + skb->ip_hdr->ihl*sizeof(long);
+	skb->len = ntohs(skb->ip_hdr->tot_len);
 	skb->h.raw = (unsigned char *) skb->ip_hdr;
 	skb->dev = dev;
 	skb->saddr = daddr;
@@ -154,7 +135,10 @@
 	/*
 	 *	Check the flags. Only MSG_DONTROUTE is permitted.
 	 */
-	 
+
+	if (flags & MSG_OOB)		/* Mirror BSD error message compatibility */
+		return -EOPNOTSUPP;
+			 
 	if (flags & ~MSG_DONTROUTE)
 		return(-EINVAL);
 	/*
@@ -240,35 +224,12 @@
 
 static void raw_close(struct sock *sk, int timeout)
 {
-	sk->inuse = 1;
 	sk->state = TCP_CLOSE;
-
-	inet_del_protocol((struct inet_protocol *)sk->pair);
-	kfree_s((void *)sk->pair, sizeof (struct inet_protocol));
-	sk->pair = NULL;
-	release_sock(sk);
 }
 
 
 static int raw_init(struct sock *sk)
 {
-	struct inet_protocol *p;
-
-	p = (struct inet_protocol *) kmalloc(sizeof (*p), GFP_KERNEL);
-	if (p == NULL)
-		return(-ENOMEM);
-
-	p->handler = raw_rcv;
-	p->protocol = sk->protocol;
-	p->data = (void *)sk;
-	p->err_handler = raw_err;
-	p->name="USER";
-	p->frag_handler = NULL;	/* For now */
-	inet_add_protocol(p);
-   
-	/* We need to remember this somewhere. */
-	sk->pair = (struct sock *)p;
-
 	return(0);
 }
 
@@ -287,6 +248,9 @@
 	int err;
 	int truesize;
 
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+		
 	if (sk->shutdown & RCV_SHUTDOWN) 
 		return(0);
 
@@ -340,7 +304,7 @@
 	ip_retransmit,
 	NULL,
 	NULL,
-	raw_rcv,
+	NULL,
 	datagram_select,
 	NULL,
 	raw_init,
diff -u --recursive --new-file v1.1.68/linux/net/inet/raw.h linux/net/inet/raw.h
--- v1.1.68/linux/net/inet/raw.h	Wed Dec  1 14:44:15 1993
+++ linux/net/inet/raw.h	Wed Nov 30 21:53:47 1994
@@ -23,14 +23,12 @@
 
 extern void	raw_err(int err, unsigned char *header, unsigned long daddr,
 			unsigned long saddr, struct inet_protocol *protocol);
-extern int	raw_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);
 extern int	raw_recvfrom(struct sock *sk, unsigned char *to,
-			     int len, int noblock, unsigned flags,
-			     struct sockaddr_in *sin, int *addr_len);
+			int len, int noblock, unsigned flags,
+			struct sockaddr_in *sin, int *addr_len);
 extern int	raw_read(struct sock *sk, unsigned char *buff,
-			 int len, int noblock, unsigned flags);
+			int len, int noblock, unsigned flags);
+extern int 	raw_rcv(struct sock *, struct sk_buff *, struct device *, 
+			long, long);
 
 #endif	/* _RAW_H */
diff -u --recursive --new-file v1.1.68/linux/net/inet/skbuff.c linux/net/inet/skbuff.c
--- v1.1.68/linux/net/inet/skbuff.c	Fri Oct 21 09:39:34 1994
+++ linux/net/inet/skbuff.c	Wed Nov 30 21:53:47 1994
@@ -149,13 +149,12 @@
 #endif
 
 
+#ifdef CONFIG_SKB_CHECK
 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
 }
 
 
@@ -170,12 +169,10 @@
 	save_flags(flags);
 	cli();
 
-#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->next;
 	newsk->prev = list;
@@ -197,12 +194,10 @@
 	save_flags(flags);
 	cli();
 
-#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;
@@ -254,7 +249,6 @@
 {
 	unsigned long flags;
 
-#if CONFIG_SKB_CHECK
 	IS_SKB(old);
 	IS_SKB(newsk);
 
@@ -262,7 +256,6 @@
 		printk("insert before unlisted item!\n");
 	if(newsk->next || newsk->prev)
 		printk("inserted item is already on a list.\n");
-#endif
 
 	save_flags(flags);
 	cli();
@@ -281,7 +274,6 @@
 {
 	unsigned long flags;
 
-#if CONFIG_SKB_CHECK
 	IS_SKB(old);
 	IS_SKB(newsk);
 
@@ -289,7 +281,6 @@
 		printk("append before unlisted item!\n");
 	if(newsk->next || newsk->prev)
 		printk("append item is already on a list.\n");
-#endif
 
 	save_flags(flags);
 	cli();
@@ -331,6 +322,8 @@
 	restore_flags(flags);
 }
 
+#endif
+
 /*
  *	Free an sk_buff. This still knows about things it should
  *	not need to like protocols and sockets.
@@ -344,7 +337,9 @@
 			__builtin_return_address(0));
 		return;
   	}
+#ifdef CONFIG_SKB_CHECK
 	IS_SKB(skb);
+#endif
 	if (skb->lock)
 	{
 		skb->free = 3;    /* Free when unlocked */
@@ -456,6 +451,7 @@
 		skb->dev->pkt_queue--;
 	restore_flags(flags);
 #endif
+#ifdef CONFIG_SKB_CHECK
 	IS_SKB(skb);
 	if(size!=skb->truesize)
 		printk("kfree_skbmem: size mismatch.\n");
@@ -473,6 +469,14 @@
 	}
 	else
 		printk("kfree_skbmem: bad magic cookie\n");
+#else
+	save_flags(flags);
+	cli();
+	kfree_s((void *)skb,size);
+	net_skbcount--;
+	net_memory -= size;
+	restore_flags(flags);
+#endif
 }
 
 /*
diff -u --recursive --new-file v1.1.68/linux/net/inet/sock.c linux/net/inet/sock.c
--- v1.1.68/linux/net/inet/sock.c	Tue Nov 29 18:51:29 1994
+++ linux/net/inet/sock.c	Wed Nov 30 21:53:47 1994
@@ -383,7 +383,9 @@
 
 void sock_wfree(struct sock *sk, struct sk_buff *skb, unsigned long size)
 {
+#ifdef CONFIG_SKB_CHECK
 	IS_SKB(skb);
+#endif
 	kfree_skbmem(skb, size);
 	if (sk) 
 	{
@@ -398,7 +400,9 @@
 
 void sock_rfree(struct sock *sk, struct sk_buff *skb, unsigned long size)
 {
+#ifdef CONFIG_SKB_CHECK
 	IS_SKB(skb);
+#endif	
 	kfree_skbmem(skb, size);
 	if (sk) 
 	{
diff -u --recursive --new-file v1.1.68/linux/net/inet/sock.h linux/net/inet/sock.h
--- v1.1.68/linux/net/inet/sock.h	Tue Nov 22 15:41:15 1994
+++ linux/net/inet/sock.h	Wed Nov 30 21:53:47 1994
@@ -43,7 +43,9 @@
 #include "ipx.h"
 #endif
 
-#define SOCK_ARRAY_SIZE	64
+#include "igmp.h"
+
+#define SOCK_ARRAY_SIZE	256		/* Think big (also on some systems a byte is faster */
 
 
 /*
@@ -157,6 +159,12 @@
   int				ip_ttl;		/* TTL setting */
   int				ip_tos;		/* TOS */
   struct tcphdr			dummy_th;
+#ifdef CONFIG_IP_MULTICAST  
+  int				ip_mc_ttl;			/* Multicasting TTL */
+  int				ip_mc_loop;			/* Loopback (not implemented yet) */
+  char				ip_mc_name[MAX_ADDR_LEN];	/* Multicast device name */
+  struct ip_mc_socklist		*ip_mc_list;			/* Group array */
+#endif  
 
   /* This part is used for the timeout functions (timer.c). */
   int				timeout;	/* What are we waiting for? */
@@ -256,7 +264,12 @@
 extern struct sock		*get_sock(struct proto *, unsigned short,
 					  unsigned long, unsigned short,
 					  unsigned long);
-extern void			print_sk(struct sock *);
+extern struct sock		*get_sock_mcast(struct sock *, unsigned short,
+					  unsigned long, unsigned short,
+					  unsigned long);
+extern struct sock		*get_sock_raw(struct sock *, unsigned short,
+					  unsigned long, unsigned long);
+
 extern struct sk_buff		*sock_wmalloc(struct sock *sk,
 					      unsigned long size, int force,
 					      int priority);
diff -u --recursive --new-file v1.1.68/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.68/linux/net/inet/tcp.c	Mon Nov 28 14:21:47 1994
+++ linux/net/inet/tcp.c	Wed Nov 30 21:53:48 1994
@@ -925,7 +925,7 @@
  *	This routine builds a generic TCP header. 
  */
  
-static int tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
+extern __inline int tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
 {
 
 	/* FIXME: want to get rid of this. */
@@ -3041,8 +3041,13 @@
 	       (((flag&2) && sk->retransmits) ||
 	       (sk->send_head->when + sk->rto < jiffies))) 
 	{
-		ip_do_retransmit(sk, 1);
-		reset_timer(sk, TIME_WRITE, sk->rto);
+		if(sk->send_head->when + sk->rto < jiffies)
+			tcp_retransmit(sk,0);	
+		else
+		{
+			ip_do_retransmit(sk, 1);
+			reset_timer(sk, TIME_WRITE, sk->rto);
+		}
 	}
 
 	return(1);
@@ -3352,7 +3357,6 @@
 	}
 	else
 	{
-		/* We missed a packet.  Send an ack to try to resync things. */
 		tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
 	}
 
@@ -3508,7 +3512,7 @@
 			reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
 			return(0);
 	}
-	sk->ack_backlog++;
+/*	sk->ack_backlog++;     tcp_data has already dealt with ACK's */
 
 	return(0);
 }
@@ -3866,7 +3870,6 @@
 		}
 
 		skb->len = len;
-		skb->sk = sk;
 		skb->acked = 0;
 		skb->used = 0;
 		skb->free = 0;
@@ -3905,12 +3908,12 @@
 	 
 	if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) 
 	{
-		skb->sk = NULL;
 		kfree_skb(skb, FREE_READ);
 		release_sock(sk);
 		return(0);
 	}
 
+	skb->sk=sk;
 	sk->rmem_alloc += skb->mem_len;
 
 #ifdef TCP_FASTPATH
@@ -4042,24 +4045,22 @@
 
 			if (th->rst) 
 			{
-				tcp_statistics.TcpEstabResets++;
-				sk->zapped=1;
-				/* This means the thing should really be closed. */
-				sk->err = ECONNRESET;
-				if (sk->state == TCP_CLOSE_WAIT) 
+				if(sk->state!=TCP_TIME_WAIT)	/* RFC 1337 recommendation re RST in time wait */
 				{
-					sk->err = EPIPE;
-				}
-	
-				/*
-				 * A reset with a fin just means that
-				 * the data was not all read.
-				 */
-				tcp_set_state(sk,TCP_CLOSE);
-				sk->shutdown = SHUTDOWN_MASK;
-				if (!sk->dead) 
-				{
-					sk->state_change(sk);
+					tcp_statistics.TcpEstabResets++;
+					sk->zapped=1;
+					/* This means the thing should really be closed. */
+					sk->err = ECONNRESET;
+					if (sk->state == TCP_CLOSE_WAIT) 
+					{
+						sk->err = EPIPE;
+					}
+					tcp_set_state(sk,TCP_CLOSE);
+					sk->shutdown = SHUTDOWN_MASK;
+					if (!sk->dead) 
+					{
+						sk->state_change(sk);
+					}
 				}
 				kfree_skb(skb, FREE_READ);
 				release_sock(sk);
diff -u --recursive --new-file v1.1.68/linux/net/inet/tcp.h linux/net/inet/tcp.h
--- v1.1.68/linux/net/inet/tcp.h	Wed Aug 10 19:26:51 1994
+++ linux/net/inet/tcp.h	Wed Nov 30 21:53:48 1994
@@ -24,7 +24,7 @@
 #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 MAX_WINDOW	16384
 #define MIN_WINDOW	2048
 #define MAX_ACK_BACKLOG	2
 #define MIN_WRITE_SPACE	2048
@@ -77,19 +77,20 @@
  * The next routines deal with comparing 32 bit unsigned ints
  * and worry about wraparound (automatic with unsigned arithmetic).
  */
-static inline int before(unsigned long seq1, unsigned long seq2)
+
+extern __inline int before(unsigned long seq1, unsigned long seq2)
 {
         return (long)(seq1-seq2) < 0;
 }
 
-static inline int after(unsigned long seq1, unsigned long seq2)
+extern __inline int after(unsigned long seq1, unsigned long seq2)
 {
 	return (long)(seq1-seq2) > 0;
 }
 
 
 /* is s2<=s1<=s3 ? */
-static inline int between(unsigned long seq1, unsigned long seq2, unsigned long seq3)
+extern __inline int between(unsigned long seq1, unsigned long seq2, unsigned long seq3)
 {
 	return (after(seq1+1, seq2) && before(seq1, seq3+1));
 }
@@ -101,7 +102,7 @@
  * convinced that this is the solution for the 'getpeername(2)'
  * problem. Thanks to Stephen A. Wood <saw@cebaf.gov>  -FvK
  */
-static inline const int
+extern __inline const int
 tcp_connected(const int state)
 {
   return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT ||
diff -u --recursive --new-file v1.1.68/linux/net/inet/udp.c linux/net/inet/udp.c
--- v1.1.68/linux/net/inet/udp.c	Tue Nov 22 15:41:15 1994
+++ linux/net/inet/udp.c	Thu Dec  1 00:51:03 1994
@@ -78,7 +78,7 @@
 struct udp_mib		udp_statistics;
 
 
-
+static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb, struct device *dev, long saddr, long daddr, int len);
 
 #define min(a,b)	((a)<(b)?(a):(b))
 
@@ -255,6 +255,7 @@
 	unsigned char *buff;
 	unsigned long saddr;
 	int size, tmp;
+	int ttl;
   
 	/* 
 	 *	Allocate an sk_buff copy of the packet.
@@ -278,8 +279,14 @@
 	buff = skb->data;
 	saddr = sk->saddr;
 	dev = NULL;
+	ttl = sk->ip_ttl;
+#ifdef CONFIG_IP_MULTICAST
+	if (MULTICAST(sin->sin_addr.s_addr))
+		ttl = sk->ip_mc_ttl;
+#endif
 	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);
+			&dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl);
+
 	skb->sk=sk;	/* So memory is freed correctly */
 	
 	/*
@@ -556,6 +563,10 @@
   	struct sock *sk;
   	struct udphdr *uh;
 	unsigned short ulen;
+	int addr_type=IS_MYADDR;
+	
+	if(skb->dev->pa_addr!=daddr)
+		addr_type=ip_chk_addr(daddr);
 		
 	/*
 	 *	Get the header.
@@ -577,13 +588,54 @@
 		kfree_skb(skb, FREE_WRITE);
 		return(0);
 	}
+
+	if (uh->check && udp_check(uh, len, saddr, daddr)) 
+	{
+		printk("UDP: bad checksum.\n");
+		udp_statistics.UdpInErrors++;
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+	}
+
+
 	len=ulen;
 
+#ifdef CONFIG_IP_MULTICAST
+	if (addr_type!=IS_MYADDR)
+	{
+		/*
+		 *	Multicasts and broadcasts go to each listener.
+		 */
+		struct sock *sknext=NULL;
+		sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
+				saddr, uh->source, daddr);
+		if(sk)
+		{		
+			do
+			{
+				struct sk_buff *skb1;
+
+				sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
+				if(sknext)
+					skb1=skb_clone(skb,GFP_ATOMIC);
+				else
+					skb1=skb;
+				if(skb1)
+					udp_deliver(sk, uh, skb1,skb->dev,saddr,daddr,len);
+				sk=sknext;
+			}
+			while(sknext!=NULL);
+		}
+		else
+			kfree_skb(skb, FREE_READ);
+		return 0;
+	}	
+#endif
   	sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
 	if (sk == NULL) 
   	{
   		udp_statistics.UdpNoPorts++;
-		if (ip_chk_addr(daddr) == IS_MYADDR) 
+		if (addr_type == IS_MYADDR) 
 		{
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
 		}
@@ -596,14 +648,11 @@
 		return(0);
   	}
 
-	if (uh->check && udp_check(uh, len, saddr, daddr)) 
-	{
-		printk("UDP: bad checksum.\n");
-		udp_statistics.UdpInErrors++;
-		kfree_skb(skb, FREE_WRITE);
-		return(0);
-	}
+	return udp_deliver(sk,uh,skb,skb->dev, saddr, daddr, len);
+}
 
+static int udp_deliver(struct sock *sk, struct udphdr *uh, struct sk_buff *skb, struct device *dev, long saddr, long daddr, int len)
+{
 	skb->sk = sk;
 	skb->dev = dev;
 	skb->len = len;
