*** linux-2.0.28/Makefile	Tue Jan 14 04:21:28 1997
--- KERNEL/Makefile	Fri Aug  8 16:15:05 1997
***************
*** 210,219 ****
  	fi
  
  include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion
! 	@if [ -f .name ]; then \
! 	   echo \#define UTS_VERSION \"\#`cat .version`-`cat .name` `date`\"; \
  	 else \
! 	   echo \#define UTS_VERSION \"\#`cat .version` `date`\";  \
  	 fi >> .ver
  	@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> .ver
  	@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> .ver
--- 210,221 ----
  	fi
  
  include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion
! 	@if [ -f VERSION ]; then \
! 	     echo \#define UTS_VERSION \"\#`cat VERSION` `date`\"; \
! 	 elif [ -f .name ]; then \
! 	     echo \#define UTS_VERSION \"\#`cat .version`-`cat .name` `date`\";\
  	 else \
! 	     echo \#define UTS_VERSION \"\#`cat .version` `date`\";  \
  	 fi >> .ver
  	@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> .ver
  	@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> .ver
***************
*** 267,272 ****
--- 269,275 ----
  endif
  
  modules: include/linux/version.h
+ 	@-mkdir modules
  	@set -e; \
  	for i in $(SUBDIRS); \
  	do $(MAKE) -C $$i CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules; \
***************
*** 313,319 ****
  	rm -f core `find . -type f -name 'core' -print`
  	rm -f vmlinux System.map
  	rm -f .tmp* drivers/sound/configure
! 	rm -fr modules/*
  	rm -f submenu*
  
  mrproper: clean
--- 316,322 ----
  	rm -f core `find . -type f -name 'core' -print`
  	rm -f vmlinux System.map
  	rm -f .tmp* drivers/sound/configure
! 	rm -fr modules
  	rm -f submenu*
  
  mrproper: clean
*** linux-2.0.28/arch/i386/kernel/bios32.c	Sat Jun  8 01:10:49 1996
--- KERNEL/arch/i386/kernel/bios32.c	Thu Jul 31 12:03:22 1997
***************
*** 56,61 ****
--- 56,62 ----
  #include <linux/pci.h>
  
  #include <asm/segment.h>
+ #include <asm/io.h>
  
  #define PCIBIOS_PCI_FUNCTION_ID 	0xb1XX
  #define PCIBIOS_PCI_BIOS_PRESENT 	0xb101
***************
*** 110,452 ****
  
  static unsigned long bios32_entry = 0;
  static struct {
! 	unsigned long address;
! 	unsigned short segment;
! } bios32_indirect = { 0, KERNEL_CS };
  
  #ifdef CONFIG_PCI
  /*
   * Returns the entry point for the given service, NULL on error
   */
  
  static unsigned long bios32_service(unsigned long service)
  {
! 	unsigned char return_code;	/* %al */
! 	unsigned long address;		/* %ebx */
! 	unsigned long length;		/* %ecx */
! 	unsigned long entry;		/* %edx */
! 
! 	__asm__("lcall (%%edi)"
! 		: "=a" (return_code),
! 		  "=b" (address),
! 		  "=c" (length),
! 		  "=d" (entry)
! 		: "0" (service),
! 		  "1" (0),
! 		  "D" (&bios32_indirect));
! 
! 	switch (return_code) {
! 		case 0:
! 			return address + entry;
! 		case 0x80:	/* Not present */
! 			printk("bios32_service(%ld) : not present\n", service);
! 			return 0;
! 		default: /* Shouldn't happen */
! 			printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
! 				service, return_code);
! 			return 0;
! 	}
  }
  
  static long pcibios_entry = 0;
  static struct {
! 	unsigned long address;
! 	unsigned short segment;
! } pci_indirect = { 0, KERNEL_CS };
  
  
  extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
  {
! 	unsigned long signature;
! 	unsigned char present_status;
! 	unsigned char major_revision;
! 	unsigned char minor_revision;
! 	int pack;
! 
! 	if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
! 		pci_indirect.address = pcibios_entry;
! 
! 		__asm__("lcall (%%edi)\n\t"
! 			"jc 1f\n\t"
! 			"xor %%ah, %%ah\n"
! 			"1:\tshl $8, %%eax\n\t"
! 			"movw %%bx, %%ax"
! 			: "=d" (signature),
! 			  "=a" (pack)
! 			: "1" (PCIBIOS_PCI_BIOS_PRESENT),
! 			  "D" (&pci_indirect)
! 			: "bx", "cx");
! 
! 		present_status = (pack >> 16) & 0xff;
! 		major_revision = (pack >> 8) & 0xff;
! 		minor_revision = pack & 0xff;
! 		if (present_status || (signature != PCI_SIGNATURE)) {
! 			printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
! 				"	but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
! 				"	and signature of 0x%08lx (%c%c%c%c).  mail drew@Colorado.EDU\n",
! 				(signature == PCI_SIGNATURE) ?  "WARNING" : "ERROR",
! 				present_status, signature,
! 				(char) (signature >>  0), (char) (signature >>  8),
! 				(char) (signature >> 16), (char) (signature >> 24));
! 
! 			if (signature != PCI_SIGNATURE)
! 				pcibios_entry = 0;
! 		}
! 		if (pcibios_entry) {
! 			printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
! 				major_revision, minor_revision, pcibios_entry);
! 		}
  	}
! 	return memory_start;
  }
  
! int pcibios_present(void)
  {
! 	return pcibios_entry ? 1 : 0;
  }
  
! int pcibios_find_class (unsigned int class_code, unsigned short index,
! 	unsigned char *bus, unsigned char *device_fn)
  {
! 	unsigned long bx;
! 	unsigned long ret;
  
! 	__asm__ ("lcall (%%edi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=b" (bx),
! 		  "=a" (ret)
! 		: "1" (PCIBIOS_FIND_PCI_CLASS_CODE),
! 		  "c" (class_code),
! 		  "S" ((int) index),
! 		  "D" (&pci_indirect));
! 	*bus = (bx >> 8) & 0xff;
! 	*device_fn = bx & 0xff;
! 	return (int) (ret & 0xff00) >> 8;
  }
  
  
! int pcibios_find_device (unsigned short vendor, unsigned short device_id,
! 	unsigned short index, unsigned char *bus, unsigned char *device_fn)
  {
! 	unsigned short bx;
! 	unsigned short ret;
  
! 	__asm__("lcall (%%edi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=b" (bx),
! 		  "=a" (ret)
! 		: "1" (PCIBIOS_FIND_PCI_DEVICE),
! 		  "c" (device_id),
! 		  "d" (vendor),
! 		  "S" ((int) index),
! 		  "D" (&pci_indirect));
! 	*bus = (bx >> 8) & 0xff;
! 	*device_fn = bx & 0xff;
! 	return (int) (ret & 0xff00) >> 8;
  }
  
  int pcibios_read_config_byte(unsigned char bus,
! 	unsigned char device_fn, unsigned char where, unsigned char *value)
  {
! 	unsigned long ret;
! 	unsigned long bx = (bus << 8) | device_fn;
  
! 	__asm__("lcall (%%esi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=c" (*value),
! 		  "=a" (ret)
! 		: "1" (PCIBIOS_READ_CONFIG_BYTE),
! 		  "b" (bx),
! 		  "D" ((long) where),
! 		  "S" (&pci_indirect));
! 	return (int) (ret & 0xff00) >> 8;
  }
  
! int pcibios_read_config_word (unsigned char bus,
! 	unsigned char device_fn, unsigned char where, unsigned short *value)
  {
! 	unsigned long ret;
! 	unsigned long bx = (bus << 8) | device_fn;
  
! 	__asm__("lcall (%%esi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=c" (*value),
! 		  "=a" (ret)
! 		: "1" (PCIBIOS_READ_CONFIG_WORD),
! 		  "b" (bx),
! 		  "D" ((long) where),
! 		  "S" (&pci_indirect));
! 	return (int) (ret & 0xff00) >> 8;
  }
  
! int pcibios_read_config_dword (unsigned char bus,
! 	unsigned char device_fn, unsigned char where, unsigned int *value)
  {
! 	unsigned long ret;
! 	unsigned long bx = (bus << 8) | device_fn;
  
! 	__asm__("lcall (%%esi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=c" (*value),
! 		  "=a" (ret)
! 		: "1" (PCIBIOS_READ_CONFIG_DWORD),
! 		  "b" (bx),
! 		  "D" ((long) where),
! 		  "S" (&pci_indirect));
! 	return (int) (ret & 0xff00) >> 8;
  }
  
! int pcibios_write_config_byte (unsigned char bus,
! 	unsigned char device_fn, unsigned char where, unsigned char value)
  {
! 	unsigned long ret;
! 	unsigned long bx = (bus << 8) | device_fn;
  
! 	__asm__("lcall (%%esi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=a" (ret)
! 		: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
! 		  "c" (value),
! 		  "b" (bx),
! 		  "D" ((long) where),
! 		  "S" (&pci_indirect));
! 	return (int) (ret & 0xff00) >> 8;
  }
  
! int pcibios_write_config_word (unsigned char bus,
! 	unsigned char device_fn, unsigned char where, unsigned short value)
  {
! 	unsigned long ret;
! 	unsigned long bx = (bus << 8) | device_fn;
  
! 	__asm__("lcall (%%esi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=a" (ret)
! 		: "0" (PCIBIOS_WRITE_CONFIG_WORD),
! 		  "c" (value),
! 		  "b" (bx),
! 		  "D" ((long) where),
! 		  "S" (&pci_indirect));
! 	return (int) (ret & 0xff00) >> 8;
  }
  
! int pcibios_write_config_dword (unsigned char bus,
  	unsigned char device_fn, unsigned char where, unsigned int value)
  {
! 	unsigned long ret;
! 	unsigned long bx = (bus << 8) | device_fn;
  
! 	__asm__("lcall (%%esi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:"
! 		: "=a" (ret)
! 		: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
! 		  "c" (value),
! 		  "b" (bx),
! 		  "D" ((long) where),
! 		  "S" (&pci_indirect));
! 	return (int) (ret & 0xff00) >> 8;
  }
  
! const char *pcibios_strerror (int error)
  {
! 	static char buf[80];
  
! 	switch (error) {
! 		case PCIBIOS_SUCCESSFUL:
! 			return "SUCCESSFUL";
  
! 		case PCIBIOS_FUNC_NOT_SUPPORTED:
! 			return "FUNC_NOT_SUPPORTED";
  
! 		case PCIBIOS_BAD_VENDOR_ID:
! 			return "SUCCESSFUL";
  
! 		case PCIBIOS_DEVICE_NOT_FOUND:
! 			return "DEVICE_NOT_FOUND";
  
! 		case PCIBIOS_BAD_REGISTER_NUMBER:
! 			return "BAD_REGISTER_NUMBER";
  
!                 case PCIBIOS_SET_FAILED:          
! 			return "SET_FAILED";
  
!                 case PCIBIOS_BUFFER_TOO_SMALL:    
! 			return "BUFFER_TOO_SMALL";
  
! 		default:
! 			sprintf (buf, "UNKNOWN RETURN 0x%x", error);
! 			return buf;
! 	}
  }
  
  
  unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
  {
! return mem_start;
  }
  
  
- #endif
- 
  unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
  {
! 	union bios32 *check;
! 	unsigned char sum;
! 	int i, length;
! 
! 	/*
! 	 * Follow the standard procedure for locating the BIOS32 Service
! 	 * directory by scanning the permissible address range from
! 	 * 0xe0000 through 0xfffff for a valid BIOS32 structure.
! 	 *
! 	 */
! 
! 	for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
! 		if (check->fields.signature != BIOS32_SIGNATURE)
! 			continue;
! 		length = check->fields.length * 16;
! 		if (!length)
! 			continue;
! 		sum = 0;
! 		for (i = 0; i < length ; ++i)
! 			sum += check->chars[i];
! 		if (sum != 0)
! 			continue;
! 		if (check->fields.revision != 0) {
! 			printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
! 				check->fields.revision, check);
! 			continue;
! 		}
! 		printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
! 		if (!bios32_entry) {
! 			if (check->fields.entry >= 0x100000) {
! 				printk("pcibios_init: entry in high memory, unable to access\n");
! 			} else {
! 				bios32_indirect.address = bios32_entry = check->fields.entry;
! 				printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
! 			}
! 		}
  	}
! #ifdef CONFIG_PCI
! 	if (bios32_entry) {
! 		memory_start = check_pcibios (memory_start, memory_end);
  	}
  #endif
! 	return memory_start;
  }
--- 111,833 ----
  
  static unsigned long bios32_entry = 0;
  static struct {
!     unsigned long address;
!     unsigned short segment;
! } bios32_indirect = {
!     0, KERNEL_CS
! };
  
  #ifdef CONFIG_PCI
+ 
+ /*
+  * function table for accessing PCI configuration space
+  */
+ struct pci_access {
+     int (*find_device) (unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
+     int (*find_class) (unsigned int, unsigned short, unsigned char *, unsigned char *);
+     int (*read_config_byte) (unsigned char, unsigned char, unsigned char, unsigned char *);
+     int (*read_config_word) (unsigned char, unsigned char, unsigned char, unsigned short *);
+     int (*read_config_dword) (unsigned char, unsigned char, unsigned char, unsigned int *);
+     int (*write_config_byte) (unsigned char, unsigned char, unsigned char, unsigned char);
+     int (*write_config_word) (unsigned char, unsigned char, unsigned char, unsigned short);
+     int (*write_config_dword) (unsigned char, unsigned char, unsigned char, unsigned int);
+ };
+ 
+ /*
+  * pointer to selected PCI access function table
+  */
+ struct pci_access *access_pci = NULL;
+ 
+ 
+ 
  /*
   * Returns the entry point for the given service, NULL on error
   */
  
  static unsigned long bios32_service(unsigned long service)
  {
!     unsigned char return_code;	/* %al */
!     unsigned long address;	/* %ebx */
!     unsigned long length;	/* %ecx */
!     unsigned long entry;	/* %edx */
! 
!     __asm__("lcall (%%edi)"
!   :	    "=a"(return_code),
! 	    "=b"(address),
! 	    "=c"(length),
! 	    "=d"(entry)
!   :	    "0"(service),
! 	    "1"(0),
! 	    "D"(&bios32_indirect));
! 
!     switch (return_code) {
!     case 0:
! 	return address + entry;
!     case 0x80:			/* Not present */
! 	printk("bios32_service(%ld) : not present\n", service);
! 	return 0;
!     default:			/* Shouldn't happen */
! 	printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
! 	       service, return_code);
! 	return 0;
!     }
  }
  
  static long pcibios_entry = 0;
  static struct {
!     unsigned long address;
!     unsigned short segment;
! } pci_indirect = {
! 
!     0, KERNEL_CS
! };
  
  
  extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
  {
!     unsigned long signature;
!     unsigned char present_status;
!     unsigned char major_revision;
!     unsigned char minor_revision;
!     int pack;
! 
!     if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
! 	pci_indirect.address = pcibios_entry;
! 
! 	__asm__("lcall (%%edi)\n\t"
! 		"jc 1f\n\t"
! 		"xor %%ah, %%ah\n"
! 		"1:\tshl $8, %%eax\n\t"
! 		"movw %%bx, %%ax"
!       :	"=d"(signature),
! 		"=a"(pack)
!       :	"1"(PCIBIOS_PCI_BIOS_PRESENT),
! 		"D"(&pci_indirect)
!       :	"bx", "cx");
! 
! 	present_status = (pack >> 16) & 0xff;
! 	major_revision = (pack >> 8) & 0xff;
! 	minor_revision = pack & 0xff;
! 	if (present_status || (signature != PCI_SIGNATURE)) {
! 	    printk("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
! 		   "	but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
! 		   "	and signature of 0x%08lx (%c%c%c%c).  mail drew@Colorado.EDU\n",
! 		   (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR",
! 		   present_status, signature,
! 		   (char) (signature >> 0), (char) (signature >> 8),
! 		   (char) (signature >> 16), (char) (signature >> 24));
! 
! 	    if (signature != PCI_SIGNATURE)
! 		pcibios_entry = 0;
! 	}
! 	if (pcibios_entry) {
! 	    printk("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
! 		   major_revision, minor_revision, pcibios_entry);
  	}
!     }
!     return memory_start;
  }
  
! 
! int pci_bios_find_class(unsigned int class_code, unsigned short index,
! 			unsigned char *bus, unsigned char *device_fn)
  {
!     unsigned long bx;
!     unsigned long ret;
! 
!     __asm__("lcall (%%edi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=b"(bx),
! 	    "=a"(ret)
!   :	    "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
! 	    "c"(class_code),
! 	    "S"((int) index),
! 	    "D"(&pci_indirect));
!     *bus = (bx >> 8) & 0xff;
!     *device_fn = bx & 0xff;
!     return (int) (ret & 0xff00) >> 8;
! }
! 
! 
! int pci_bios_find_device(unsigned short vendor, unsigned short device_id,
!       unsigned short index, unsigned char *bus, unsigned char *device_fn)
! {
!     unsigned short bx;
!     unsigned short ret;
! 
!     __asm__("lcall (%%edi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=b"(bx),
! 	    "=a"(ret)
!   :	    "1"(PCIBIOS_FIND_PCI_DEVICE),
! 	    "c"(device_id),
! 	    "d"(vendor),
! 	    "S"((int) index),
! 	    "D"(&pci_indirect));
!     *bus = (bx >> 8) & 0xff;
!     *device_fn = bx & 0xff;
!     return (int) (ret & 0xff00) >> 8;
! }
! 
! int pci_bios_read_config_byte(unsigned char bus,
!       unsigned char device_fn, unsigned char where, unsigned char *value)
! {
!     unsigned long ret;
!     unsigned long bx = (bus << 8) | device_fn;
! 
!     __asm__("lcall (%%esi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=c"(*value),
! 	    "=a"(ret)
!   :	    "1"(PCIBIOS_READ_CONFIG_BYTE),
! 	    "b"(bx),
! 	    "D"((long) where),
! 	    "S"(&pci_indirect));
!     return (int) (ret & 0xff00) >> 8;
! }
! 
! int pci_bios_read_config_word(unsigned char bus,
!      unsigned char device_fn, unsigned char where, unsigned short *value)
! {
!     unsigned long ret;
!     unsigned long bx = (bus << 8) | device_fn;
! 
!     __asm__("lcall (%%esi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=c"(*value),
! 	    "=a"(ret)
!   :	    "1"(PCIBIOS_READ_CONFIG_WORD),
! 	    "b"(bx),
! 	    "D"((long) where),
! 	    "S"(&pci_indirect));
!     return (int) (ret & 0xff00) >> 8;
! }
! 
! int pci_bios_read_config_dword(unsigned char bus,
!        unsigned char device_fn, unsigned char where, unsigned int *value)
! {
!     unsigned long ret;
!     unsigned long bx = (bus << 8) | device_fn;
! 
!     __asm__("lcall (%%esi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=c"(*value),
! 	    "=a"(ret)
!   :	    "1"(PCIBIOS_READ_CONFIG_DWORD),
! 	    "b"(bx),
! 	    "D"((long) where),
! 	    "S"(&pci_indirect));
!     return (int) (ret & 0xff00) >> 8;
! }
! 
! int pci_bios_write_config_byte(unsigned char bus,
!        unsigned char device_fn, unsigned char where, unsigned char value)
! {
!     unsigned long ret;
!     unsigned long bx = (bus << 8) | device_fn;
! 
!     __asm__("lcall (%%esi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=a"(ret)
!   :	    "0"(PCIBIOS_WRITE_CONFIG_BYTE),
! 	    "c"(value),
! 	    "b"(bx),
! 	    "D"((long) where),
! 	    "S"(&pci_indirect));
!     return (int) (ret & 0xff00) >> 8;
! }
! 
! int pci_bios_write_config_word(unsigned char bus,
!       unsigned char device_fn, unsigned char where, unsigned short value)
! {
!     unsigned long ret;
!     unsigned long bx = (bus << 8) | device_fn;
! 
!     __asm__("lcall (%%esi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=a"(ret)
!   :	    "0"(PCIBIOS_WRITE_CONFIG_WORD),
! 	    "c"(value),
! 	    "b"(bx),
! 	    "D"((long) where),
! 	    "S"(&pci_indirect));
!     return (int) (ret & 0xff00) >> 8;
  }
  
! int pci_bios_write_config_dword(unsigned char bus,
! 	unsigned char device_fn, unsigned char where, unsigned int value)
  {
!     unsigned long ret;
!     unsigned long bx = (bus << 8) | device_fn;
  
!     __asm__("lcall (%%esi)\n\t"
! 	    "jc 1f\n\t"
! 	    "xor %%ah, %%ah\n"
! 	    "1:"
!   :	    "=a"(ret)
!   :	    "0"(PCIBIOS_WRITE_CONFIG_DWORD),
! 	    "c"(value),
! 	    "b"(bx),
! 	    "D"((long) where),
! 	    "S"(&pci_indirect));
!     return (int) (ret & 0xff00) >> 8;
  }
  
+ /*
+  * function table for BIOS32 access
+  */
+ struct pci_access pci_bios_access =
+ {
+     pci_bios_find_device,
+     pci_bios_find_class,
+     pci_bios_read_config_byte,
+     pci_bios_read_config_word,
+     pci_bios_read_config_dword,
+     pci_bios_write_config_byte,
+     pci_bios_write_config_word,
+     pci_bios_write_config_dword
+ };
+ 
  
! 
! /*
!  * Given the vendor and device ids, find the n'th instance of that device
!  * in the system.  
!  */
! int pci_direct_find_device(unsigned short vendor, unsigned short device_id,
! 			   unsigned short index, unsigned char *bus,
! 			   unsigned char *devfn)
! {
!     unsigned int curr = 0;
!     struct pci_dev *dev;
! 
!     for (dev = pci_devices; dev; dev = dev->next) {
! 	if (dev->vendor == vendor && dev->device == device_id) {
! 	    if (curr == index) {
! 		*devfn = dev->devfn;
! 		*bus = dev->bus->number;
! 		return PCIBIOS_SUCCESSFUL;
! 	    }
! 	    ++curr;
! 	}
!     }
!     return PCIBIOS_DEVICE_NOT_FOUND;
! }
! 
! 
! /*
!  * Given the class, find the n'th instance of that device
!  * in the system.
!  */
! int pci_direct_find_class(unsigned int class_code, unsigned short index,
! 			  unsigned char *bus, unsigned char *devfn)
  {
!     unsigned int curr = 0;
!     struct pci_dev *dev;
  
!     for (dev = pci_devices; dev; dev = dev->next) {
! 	if (dev->class == class_code) {
! 	    if (curr == index) {
! 		*devfn = dev->devfn;
! 		*bus = dev->bus->number;
! 		return PCIBIOS_SUCCESSFUL;
! 	    }
! 	    ++curr;
! 	}
!     }
!     return PCIBIOS_DEVICE_NOT_FOUND;
! }
! 
! /*
!  * Functions for accessing PCI configuration space with type 1 accesses
!  */
! #define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
! 
! int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
! 			       unsigned char where, unsigned char *value)
! {
!     outl(CONFIG_CMD(bus, device_fn, where), 0xCF8);
!     switch (where & 3) {
!     case 0:
! 	*value = inb(0xCFC);
! 	break;
!     case 1:
! 	*value = inb(0xCFD);
! 	break;
!     case 2:
! 	*value = inb(0xCFE);
! 	break;
!     case 3:
! 	*value = inb(0xCFF);
! 	break;
!     }
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf1_read_config_word(unsigned char bus,
!      unsigned char device_fn, unsigned char where, unsigned short *value)
! {
!     outl(CONFIG_CMD(bus, device_fn, where), 0xCF8);
!     if (where & 2)
! 	*value = inw(0xCFE);
!     else
! 	*value = inw(0xCFC);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf1_read_config_dword(unsigned char bus, unsigned char device_fn,
! 				unsigned char where, unsigned int *value)
! {
!     outl(CONFIG_CMD(bus, device_fn, where), 0xCF8);
!     *value = inl(0xCFC);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf1_write_config_byte(unsigned char bus, unsigned char device_fn,
! 				unsigned char where, unsigned char value)
! {
!     outl(CONFIG_CMD(bus, device_fn, where), 0xCF8);
!     outb(value, 0xCFC);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf1_write_config_word(unsigned char bus, unsigned char device_fn,
! 				unsigned char where, unsigned short value)
! {
!     outl(CONFIG_CMD(bus, device_fn, where), 0xCF8);
!     outw(value, 0xCFC);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf1_write_config_dword(unsigned char bus, unsigned char device_fn,
! 				 unsigned char where, unsigned int value)
! {
!     outl(CONFIG_CMD(bus, device_fn, where), 0xCF8);
!     outl(value, 0xCFC);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! #undef CONFIG_CMD
! 
! /*
!  * functiontable for type 1
!  */
! struct pci_access pci_direct_conf1 =
! {
!     pci_direct_find_device,
!     pci_direct_find_class,
!     pci_conf1_read_config_byte,
!     pci_conf1_read_config_word,
!     pci_conf1_read_config_dword,
!     pci_conf1_write_config_byte,
!     pci_conf1_write_config_word,
!     pci_conf1_write_config_dword
! };
! 
! /*
!  * Functions for accessing PCI configuration space with type 2 accesses
!  */
! #define IOADDR(devfn, where)   ((0xC000 | ((devfn & 0x78) << 5)) + where)
! #define FUNC(devfn)            (((devfn & 7) << 1) | 0xf0)
! 
! int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn,
! 			       unsigned char where, unsigned char *value)
! {
!     if (device_fn & 0x80)
! 	return PCIBIOS_DEVICE_NOT_FOUND;
!     outb(FUNC(device_fn), 0xCF8);
!     outb(bus, 0xCFA);
!     *value = inb(IOADDR(device_fn, where));
!     outb(0, 0xCF8);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf2_read_config_word(unsigned char bus, unsigned char device_fn,
! 			       unsigned char where, unsigned short *value)
! {
!     if (device_fn & 0x80)
! 	return PCIBIOS_DEVICE_NOT_FOUND;
!     outb(FUNC(device_fn), 0xCF8);
!     outb(bus, 0xCFA);
!     *value = inw(IOADDR(device_fn, where));
!     outb(0, 0xCF8);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf2_read_config_dword(unsigned char bus, unsigned char device_fn,
! 				unsigned char where, unsigned int *value)
! {
!     if (device_fn & 0x80)
! 	return PCIBIOS_DEVICE_NOT_FOUND;
!     outb(FUNC(device_fn), 0xCF8);
!     outb(bus, 0xCFA);
!     *value = inl(IOADDR(device_fn, where));
!     outb(0, 0xCF8);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf2_write_config_byte(unsigned char bus, unsigned char device_fn,
! 				unsigned char where, unsigned char value)
! {
!     outb(FUNC(device_fn), 0xCF8);
!     outb(bus, 0xCFA);
!     outb(value, IOADDR(device_fn, where));
!     outb(0, 0xCF8);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf2_write_config_word(unsigned char bus, unsigned char device_fn,
! 				unsigned char where, unsigned short value)
! {
!     outb(FUNC(device_fn), 0xCF8);
!     outb(bus, 0xCFA);
!     outw(value, IOADDR(device_fn, where));
!     outb(0, 0xCF8);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! int pci_conf2_write_config_dword(unsigned char bus, unsigned char device_fn,
! 				 unsigned char where, unsigned int value)
! {
!     outb(FUNC(device_fn), 0xCF8);
!     outb(bus, 0xCFA);
!     outl(value, IOADDR(device_fn, where));
!     outb(0, 0xCF8);
!     return PCIBIOS_SUCCESSFUL;
! }
! 
! #undef IOADDR
! #undef FUNC
! 
! /*
!  * functiontable for type 2
!  */
! struct pci_access pci_direct_conf2 =
! {
!     pci_direct_find_device,
!     pci_direct_find_class,
!     pci_conf2_read_config_byte,
!     pci_conf2_read_config_word,
!     pci_conf2_read_config_dword,
!     pci_conf2_write_config_byte,
!     pci_conf2_write_config_word,
!     pci_conf2_write_config_dword
! };
! 
! #endif
! 
! struct pci_access *check_direct_pci(void)
! {
!     unsigned int tmp;
! 
!     /*
!      * check if configuration type 1 works
!      */
!     outb(0x01, 0xCFB);
!     tmp = inl(0xCF8);
!     outl(0x80000000, 0xCF8);
!     if (inl(0xCF8) == 0x80000000) {
! 	outl(tmp, 0xCF8);
! 	printk("pcibios_init: Using configuration type 1\n");
! 	return &pci_direct_conf1;
!     }
!     outl(tmp, 0xCF8);
! 
!     /*
!      * check if configuration type 2 works
!      */
!     outb(0x00, 0xCFB);
!     outb(0x00, 0xCF8);
!     outb(0x00, 0xCFA);
!     if (inb(0xCF8) == 0x00 && inb(0xCFC) == 0x00) {
! 	printk("pcibios_init: Using configuration type 2\n");
! 	return &pci_direct_conf2;
!     }
!     printk("pcibios_init: Not supported chipset for direct PCI access !\n");
!     return NULL;
! }
! 
! 
! /*
!  * access defined pcibios functions via
!  * the function table
!  */
! 
! int pcibios_present(void)
! {
!     return access_pci ? 1 : 0;
! }
! 
! int pcibios_find_class(unsigned int class_code, unsigned short index,
! 		       unsigned char *bus, unsigned char *device_fn)
! {
!     if (access_pci && access_pci->find_class)
! 	return access_pci->find_class(class_code, index, bus, device_fn);
! 
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
! }
! 
! int pcibios_find_device(unsigned short vendor, unsigned short device_id,
!       unsigned short index, unsigned char *bus, unsigned char *device_fn)
! {
!     if (access_pci && access_pci->find_device)
! 	return access_pci->find_device(vendor, device_id, index, bus, device_fn);
! 
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
  int pcibios_read_config_byte(unsigned char bus,
!       unsigned char device_fn, unsigned char where, unsigned char *value)
  {
!     if (access_pci && access_pci->read_config_byte)
! 	return access_pci->read_config_byte(bus, device_fn, where, value);
  
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
! int pcibios_read_config_word(unsigned char bus,
!      unsigned char device_fn, unsigned char where, unsigned short *value)
  {
!     if (access_pci && access_pci->read_config_word)
! 	return access_pci->read_config_word(bus, device_fn, where, value);
  
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
! int pcibios_read_config_dword(unsigned char bus,
!        unsigned char device_fn, unsigned char where, unsigned int *value)
  {
!     if (access_pci && access_pci->read_config_dword)
! 	return access_pci->read_config_dword(bus, device_fn, where, value);
  
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
! int pcibios_write_config_byte(unsigned char bus,
!        unsigned char device_fn, unsigned char where, unsigned char value)
  {
!     if (access_pci && access_pci->write_config_byte)
! 	return access_pci->write_config_byte(bus, device_fn, where, value);
  
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
! int pcibios_write_config_word(unsigned char bus,
!       unsigned char device_fn, unsigned char where, unsigned short value)
  {
!     if (access_pci && access_pci->write_config_word)
! 	return access_pci->write_config_word(bus, device_fn, where, value);
  
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
! int pcibios_write_config_dword(unsigned char bus,
  	unsigned char device_fn, unsigned char where, unsigned int value)
  {
!     if (access_pci && access_pci->write_config_dword)
! 	return access_pci->write_config_dword(bus, device_fn, where, value);
  
!     return PCIBIOS_FUNC_NOT_SUPPORTED;
  }
  
! const char *pcibios_strerror(int error)
  {
!     static char buf[80];
  
!     switch (error) {
!     case PCIBIOS_SUCCESSFUL:
! 	return "SUCCESSFUL";
  
!     case PCIBIOS_FUNC_NOT_SUPPORTED:
! 	return "FUNC_NOT_SUPPORTED";
  
!     case PCIBIOS_BAD_VENDOR_ID:
! 	return "SUCCESSFUL";
  
!     case PCIBIOS_DEVICE_NOT_FOUND:
! 	return "DEVICE_NOT_FOUND";
  
!     case PCIBIOS_BAD_REGISTER_NUMBER:
! 	return "BAD_REGISTER_NUMBER";
  
!     case PCIBIOS_SET_FAILED:
! 	return "SET_FAILED";
  
!     case PCIBIOS_BUFFER_TOO_SMALL:
! 	return "BUFFER_TOO_SMALL";
  
!     default:
! 	sprintf(buf, "UNKNOWN RETURN 0x%x", error);
! 	return buf;
!     }
  }
  
  
  unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
  {
!     return mem_start;
  }
  
  
  unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
  {
!     union bios32 *check;
!     unsigned char sum;
!     int i, length;
! 
!     /*
!      * Follow the standard procedure for locating the BIOS32 Service
!      * directory by scanning the permissible address range from
!      * 0xe0000 through 0xfffff for a valid BIOS32 structure.
!      *
!      */
! 
!     for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
! 	if (check->fields.signature != BIOS32_SIGNATURE)
! 	    continue;
! 	length = check->fields.length * 16;
! 	if (!length)
! 	    continue;
! 	sum = 0;
! 	for (i = 0; i < length; ++i)
! 	    sum += check->chars[i];
! 	if (sum != 0)
! 	    continue;
! 	if (check->fields.revision != 0) {
! 	    printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
! 		   check->fields.revision, check);
! 	    continue;
  	}
! 	printk("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
! 	if (!bios32_entry) {
! 	    if (check->fields.entry >= 0x100000) {
! 		printk("pcibios_init: entry in high memory, trying direct PCI access\n");
! 		access_pci = check_direct_pci();
! 	    } else {
! 		bios32_indirect.address = bios32_entry = check->fields.entry;
! 		printk("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
! 		access_pci = &pci_bios_access;
! 	    }
  	}
+     }
+ #ifdef CONFIG_PCI
+     if (bios32_entry) {
+ 	memory_start = check_pcibios(memory_start, memory_end);
+     }
  #endif
!     return memory_start;
  }
*** linux-2.0.28/drivers/block/ide-cd.c	Fri Sep 20 07:00:34 1996
--- KERNEL/drivers/block/ide-cd.c	Thu Jul 31 12:02:58 1997
***************
*** 707,713 ****
  			} else if (sense_key == UNIT_ATTENTION) {
  				/* Check for media change. */
  				cdrom_saw_media_change (drive);
! 				printk ("%s: media changed\n", drive->name);
  			} else {
  				/* Otherwise, print an error. */
  				ide_dump_status (drive, "packet command error",
--- 707,713 ----
  			} else if (sense_key == UNIT_ATTENTION) {
  				/* Check for media change. */
  				cdrom_saw_media_change (drive);
! 				printk (KERN_DEBUG "%s: media changed\n", drive->name);
  			} else {
  				/* Otherwise, print an error. */
  				ide_dump_status (drive, "packet command error",
*** linux-2.0.28/drivers/char/console.c	Thu Nov  7 01:25:21 1996
--- KERNEL/drivers/char/console.c	Thu Jul 31 12:02:59 1997
***************
*** 308,314 ****
  	if (i >= MAX_NR_CONSOLES)
  	  return -ENXIO;
  	if (!vc_cons[i].d) {
! 	    long p, q;
  
  	    /* prevent users from taking too much memory */
  	    if (i >= MAX_NR_USER_CONSOLES && !suser())
--- 308,314 ----
  	if (i >= MAX_NR_CONSOLES)
  	  return -ENXIO;
  	if (!vc_cons[i].d) {
! 	    long p, q, b;
  
  	    /* prevent users from taking too much memory */
  	    if (i >= MAX_NR_USER_CONSOLES && !suser())
***************
*** 325,334 ****
--- 325,342 ----
  		kfree_s((char *) q, video_screen_size);
  		return -ENOMEM;
  	    }
+ 	    b = get_free_page(GFP_KERNEL);
+ 	    if (!b) {
+ 		kfree_s((char *) p, structsize);
+ 		kfree_s((char *) q, video_screen_size);
+ 		return -ENOMEM;
+ 	    }
  
  	    vc_cons[i].d = (struct vc_data *) p;
  	    p += sizeof(struct vc_data);
  	    vt_cons[i] = (struct vt_struct *) p;
+ 	    vt_cons[i]->xmit_buf = (unsigned char *)b;
+ 	    vt_cons[i]->xmit_cnt = 0;
  	    vc_scrbuf[i] = (unsigned short *) q;
  	    vc_cons[i].d->vc_kmalloced = 1;
  	    vc_cons[i].d->vc_screenbuf_size = video_screen_size;
***************
*** 1831,1846 ****
  	return n;
  }
  
  static int con_write_room(struct tty_struct *tty)
  {
! 	if (tty->stopped)
! 		return 0;
! 	return 4096;		/* No limit, really; we're not buffering */
  }
  
  static int con_chars_in_buffer(struct tty_struct *tty)
  {
! 	return 0;		/* we're not buffering */
  }
  
  void poke_blanked_console(void)
--- 1839,1889 ----
  	return n;
  }
  
+ static void con_put_char(struct tty_struct * tty, unsigned char ch)
+ {
+ 	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+ 	unsigned long flags;
+ 
+ 	if (!tty || !vt || !vt->xmit_buf)
+ 		return;
+ 
+ 	save_flags(flags); cli();
+ 	vt->xmit_buf[vt->xmit_cnt++] = ch;
+ 	if (vt->xmit_cnt >= CON_XMIT_SIZE) {
+ 		con_write(tty, 0, vt->xmit_buf, vt->xmit_cnt);
+ 		vt->xmit_cnt = 0;
+ 	}
+ 
+ 	restore_flags(flags);
+ }
+ 
+ static void con_flush_chars(struct tty_struct *tty)
+ {
+ 	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+ 	unsigned long flags;
+ 
+ 	if (vt->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ 	    !vt->xmit_buf)
+ 		return;
+ 
+ 	save_flags(flags); cli();
+ 	con_write(tty, 0, vt->xmit_buf, vt->xmit_cnt);
+ 	vt->xmit_cnt = 0;
+ 	restore_flags(flags);
+ }
+ 
  static int con_write_room(struct tty_struct *tty)
  {
! 	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
! 
! 	return CON_XMIT_SIZE - vt->xmit_cnt;
  }
  
  static int con_chars_in_buffer(struct tty_struct *tty)
  {
! 	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
! 
! 	return vt->xmit_cnt;
  }
  
  void poke_blanked_console(void)
***************
*** 2016,2021 ****
--- 2059,2066 ----
  
  	console_driver.open = con_open;
  	console_driver.write = con_write;
+ 	console_driver.put_char = con_put_char;
+ 	console_driver.flush_chars = con_flush_chars;
  	console_driver.write_room = con_write_room;
  	console_driver.chars_in_buffer = con_chars_in_buffer;
  	console_driver.ioctl = vt_ioctl;
***************
*** 2053,2058 ****
--- 2098,2106 ----
  		kmem_start += sizeof(struct vc_data);
  		vt_cons[currcons] = (struct vt_struct *) kmem_start;
  		kmem_start += sizeof(struct vt_struct);
+ 		vt_cons[currcons]->xmit_buf = (unsigned char *) kmem_start;
+ 		vt_cons[currcons]->xmit_cnt = 0;
+ 		kmem_start += CON_XMIT_SIZE;
  		vc_scrbuf[currcons] = (unsigned short *) kmem_start;
  		kmem_start += video_screen_size;
  		kmalloced = 0;
*** linux-2.0.28/drivers/char/vt_kern.h	Sun Mar 24 03:35:08 1996
--- KERNEL/drivers/char/vt_kern.h	Thu Jul 31 12:02:59 1997
***************
*** 18,23 ****
--- 18,28 ----
   */
  #define BROKEN_GRAPHICS_PROGRAMS 1
  
+ /*
+  * The console transmit buffer is set to one page (is this always 4096?)
+  */
+ #define CON_XMIT_SIZE	PAGE_SIZE
+ 
  extern struct vt_struct {
  	int vc_num;				/* The console number */
  	unsigned char	vc_mode;		/* KD_TEXT, ... */
***************
*** 28,33 ****
--- 33,41 ----
  	int		vt_pid;
  	int		vt_newvt;
  	struct wait_queue *paste_wait;
+ 
+ 	unsigned char	*xmit_buf;
+ 	int		xmit_cnt;
  } *vt_cons[MAX_NR_CONSOLES];
  
  void (*kd_mksound)(unsigned int hz, unsigned int ticks);
*** linux-2.0.28/fs/ext2/super.c	Sun Jul  7 01:06:53 1996
--- KERNEL/fs/ext2/super.c	Thu Jul 31 12:02:41 1997
***************
*** 282,287 ****
--- 282,288 ----
  		else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
  			printk ("EXT2-fs warning: mounting fs with errors, "
  				"running e2fsck is recommended\n");
+ #ifdef OVERLYVERBOSE
  		else if (es->s_max_mnt_count >= 0 &&
  		         es->s_mnt_count >= (unsigned short) es->s_max_mnt_count)
  			printk ("EXT2-fs warning: maximal mount count reached, "
***************
*** 290,295 ****
--- 291,297 ----
  			(es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME))
  			printk ("EXT2-fs warning: checktime reached, "
  				"running e2fsck is recommended\n");
+ #endif
  		es->s_state &= ~EXT2_VALID_FS;
  		if (!es->s_max_mnt_count)
  			es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
*** linux-2.0.28/fs/locks.c	Fri Sep 20 07:00:35 1996
--- KERNEL/fs/locks.c	Thu Jul 31 12:02:40 1997
***************
*** 308,314 ****
  		break;
  	case F_SHLCK :
  	case F_EXLCK :
! #if 1
  /* warn a bit for now, but don't overdo it */
  {
  	static int count = 0;
--- 308,314 ----
  		break;
  	case F_SHLCK :
  	case F_EXLCK :
! #if 0
  /* warn a bit for now, but don't overdo it */
  {
  	static int count = 0;
*** linux-2.0.28/include/asm-i386/segment.h	Tue Apr  9 00:35:29 1996
--- KERNEL/include/asm-i386/segment.h	Thu Jul 31 12:02:47 1997
***************
*** 1,8 ****
  #ifndef _ASM_SEGMENT_H
  #define _ASM_SEGMENT_H
  
! #define KERNEL_CS	0x10
! #define KERNEL_DS	0x18
  
  #define USER_CS		0x23
  #define USER_DS		0x2B
--- 1,9 ----
  #ifndef _ASM_SEGMENT_H
  #define _ASM_SEGMENT_H
  
! #define	BIOS_CS		0x08	/* selector 1 */
! #define KERNEL_CS	0x10	/* selector 2 */
! #define KERNEL_DS	0x18	/* selector 3 */
  
  #define USER_CS		0x23
  #define USER_DS		0x2B
*** linux-2.0.28/include/linux/proc_fs.h	Sun Dec  1 09:59:28 1996
--- KERNEL/include/linux/proc_fs.h	Mon Nov 24 22:59:24 1997
***************
*** 104,109 ****
--- 104,113 ----
  	PROC_NET_IP_MASQ_APP,
  	PROC_NET_STRIP_STATUS,
  	PROC_NET_STRIP_TRACE,
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 	PROC_NET_WSSOCKLOG,
+ 	PROC_NET_WSBRIDGELOG,
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
  	PROC_NET_LAST
  };
  
*** linux-2.0.28/include/linux/skbuff.h	Sun Dec  1 09:59:28 1996
--- KERNEL/include/linux/skbuff.h	Thu Jul 31 12:02:46 1997
***************
*** 112,117 ****
--- 112,119 ----
  	unsigned char 	*end;			/* End pointer					*/
  	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
  	__u16		redirport;		/* Redirect port				*/
+ 	unsigned char	ipcatch;		/* scannable packet */
+ 	struct device	*indev;			/* incoming device */
  };
  
  #ifdef CONFIG_SKB_LARGE
*** linux-2.0.28/include/linux/sockios.h	Mon May 13 13:23:07 1996
--- KERNEL/include/linux/sockios.h	Thu Jul 31 12:02:46 1997
***************
*** 53,60 ****
--- 53,73 ----
  #define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
  #define SIOCDELMULTI	0x8932
  
+ #if 0
+ /* BRYAN: turn them off for now */
  #define SIOCGIFBR	0x8940		/* Bridging support		*/
  #define SIOCSIFBR	0x8941		/* Set bridging options 	*/
+ #endif
+ 
+ /* Bridge scanning support */
+ #define SIOCGIFBR	0x8945		/* Bridging support		*/
+ #define SIOCSIFBR	0x8946		/* Set bridging options 	*/
+ #define SIOCSLISTEN	0x8947		/* Set global listening socket	*/
+ #define SIOCSCANPAIR	0x8948		/* get partner in scanned pair	*/
+ #define SIOCSDONTCARE	0x8949		/* dont scan this connection	*/
+ #define SIOCSINTERNAL	0x894a		/* internally initiated		*/
+ #define SIOCBRDEBUG	0x894b		/* enable bridge debugging	*/
+ #define SIOCBRNODEBUG	0x894c		/* disable bridge debugging	*/
  
  /* ARP cache control calls. */
  #define OLD_SIOCDARP	0x8950		/* old delete ARP table entry	*/
*** linux-2.0.28/include/linux/uio.h	Thu Jun  6 11:22:24 1996
--- KERNEL/include/linux/uio.h	Thu Jul 31 12:02:46 1997
***************
*** 14,24 ****
--- 14,27 ----
  /* A word of warning: Our uio structure will clash with the C library one (which is now obsolete). Remove the C
     library one from sys/uio.h if you have a very old library set */
  
+ #ifndef IOVEC_DEFINED
+ #define IOVEC_DEFINED
  struct iovec
  {
  	void *iov_base;		/* BSD uses caddr_t (same thing in effect) */
  	int iov_len;
  };
+ #endif
  
  #define UIO_MAXIOV	16	/* Maximum iovec's in one operation 
  				   16 matches BSD */
*** linux-2.0.28/include/net/sock.h	Sun Dec  1 10:01:21 1996
--- KERNEL/include/net/sock.h	Tue Aug  5 22:38:28 1997
***************
*** 196,201 ****
--- 196,209 ----
  	struct sock		*next;
  	struct sock		*prev; /* Doubly linked chain.. */
  	struct sock		*pair;
+ 	struct sock		*scanpair; /* bridged sock's partner */
+ 	unsigned char		scanaccepted; /* bridge socks creation state */
+ 	unsigned char		dontcare; /* bridge application does not care */
+ 	unsigned char		scanclient; /* client side of scan pair */
+ 	unsigned char		finflag; /* consumed the fin */
+ 	struct device		*scandev; /* bound to this device */
+ 	struct device		*indev; /* peer bound to this device */
+ 	unsigned char		hw_addr[6]; /* internal dest MAC (ETH_ALEN) */
  	struct sk_buff		* volatile send_head;
  	struct sk_buff		* volatile send_next;
  	struct sk_buff		* volatile send_tail;
***************
*** 385,390 ****
--- 393,399 ----
  #define TIME_DESTROY	4
  #define TIME_DONE	5	/* Used to absorb those last few packets */
  #define TIME_PROBE0	6
+ #define TIME_NEWSCAN	7	/* bridged connection starting up */
  
  /*
   *	About 10 seconds 
*** linux-2.0.28/include/net/tcp.h	Sun Dec  1 10:01:21 1996
--- KERNEL/include/net/tcp.h	Mon Nov 24 22:43:31 1997
***************
*** 56,62 ****
  				 */
  
  #define TCP_TIMEOUT_LEN	(15*60*HZ) /* should be about 15 mins		*/
! #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully 
  				  * close the socket, about 60 seconds	*/
  #define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */				  
  #define TCP_ACK_TIME	(3*HZ)	/* time to delay before sending an ACK	*/
--- 56,62 ----
  				 */
  
  #define TCP_TIMEOUT_LEN	(15*60*HZ) /* should be about 15 mins		*/
! #define TCP_TIMEWAIT_LEN (10*HZ) /* how long to wait to successfully 
  				  * close the socket, about 60 seconds	*/
  #define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */				  
  #define TCP_ACK_TIME	(3*HZ)	/* time to delay before sending an ACK	*/
***************
*** 146,151 ****
--- 146,158 ----
  extern void tcp_send_check(struct tcphdr *th, unsigned long saddr, 
  		unsigned long daddr, int len, struct sk_buff *skb);
  
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ extern void log_sockevent(struct sock *sp, int action,
+ 		unsigned long seq1, unsigned long seq2);
+ #else
+ #define log_sockevent(sp, action, seq1, seq2)
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
+ 
  /* tcp_output.c */
  
  extern void tcp_send_probe0(struct sock *);
***************
*** 156,163 ****
  extern void tcp_send_skb(struct sock *, struct sk_buff *);
  extern void tcp_send_ack(struct sock *sk);
  extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout, unsigned long timeout);
! extern void tcp_send_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
! 	  struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl);
  
  extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
  extern struct sk_buff * tcp_dequeue_partial(struct sock *);
--- 163,171 ----
  extern void tcp_send_skb(struct sock *, struct sk_buff *);
  extern void tcp_send_ack(struct sock *sk);
  extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout, unsigned long timeout);
! extern void tcp_send_reset(unsigned long saddr, unsigned long daddr,
! 	struct tcphdr *th, struct proto *prot, struct options *opt,
! 	struct device *dev, int tos, int ttl, struct sk_buff *);
  
  extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
  extern struct sk_buff * tcp_dequeue_partial(struct sock *);
***************
*** 249,254 ****
--- 257,263 ----
  
  	sk->state = state;
  
+ 	log_sockevent(sk, 11, oldstate, state);
  #ifdef STATE_TRACE
  	if(sk->debug)
  		printk("TCP sk=%p, State %s -> %s\n",sk, statename[oldstate],statename[state]);
***************
*** 265,270 ****
--- 274,280 ----
  		tcp_cache_zap();
  		/* Should be about 2 rtt's */
  		reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
+ 		log_sockevent(sk, 12, sk->write_seq, sk->copied_seq);
  		/* fall through */
  	default:
  		if (oldstate==TCP_ESTABLISHED)
*** linux-2.0.28/init/version.c	Sun Nov 19 07:45:02 1995
--- KERNEL/init/version.c	Thu Jul 31 12:02:43 1997
***************
*** 26,30 ****
--- 26,34 ----
  };
  
  const char *linux_banner = 
+ #if 0
  	"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
  	LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
+ #else
+ 	"Linux version " UTS_RELEASE " [ WEBSHIELD " UTS_VERSION " ]\n";
+ #endif
*** linux-2.0.28/kernel/sched.c	Sun Sep 15 00:34:18 1996
--- KERNEL/kernel/sched.c	Mon Nov 24 22:44:37 1997
***************
*** 402,409 ****
  	return;
  
  scheduling_in_interrupt:
! 	printk("Aiee: scheduling in interrupt %p\n",
! 		__builtin_return_address(0));
  }
  
  #ifndef __alpha__
--- 402,413 ----
  	return;
  
  scheduling_in_interrupt:
! 	printk("Aiee: scheduling in interrupt %p, %p, %p, %p, %p\n",
! 		__builtin_return_address(0),
! 		__builtin_return_address(1),
! 		__builtin_return_address(2),
! 		__builtin_return_address(3),
! 		__builtin_return_address(4));
  }
  
  #ifndef __alpha__
***************
*** 450,457 ****
  	}
  	return;
  bad:
! 	printk("wait_queue is bad (eip = %p)\n",
! 		__builtin_return_address(0));
  	printk("        q = %p\n",q);
  	printk("       *q = %p\n",*q);
  }
--- 454,464 ----
  	}
  	return;
  bad:
! 	printk("wait_queue is bad (eip = %p, %p, %p, %p)\n",
! 		__builtin_return_address(0),
! 		__builtin_return_address(1),
! 		__builtin_return_address(2),
! 		__builtin_return_address(3));
  	printk("        q = %p\n",q);
  	printk("       *q = %p\n",*q);
  }
***************
*** 476,483 ****
  	}
  	return;
  bad:
! 	printk("wait_queue is bad (eip = %p)\n",
! 		__builtin_return_address(0));
  	printk("        q = %p\n",q);
  	printk("       *q = %p\n",*q);
  }
--- 483,493 ----
  	}
  	return;
  bad:
! 	printk("wait_queue is bad (eip = %p, %p, %p, %p)\n",
! 		__builtin_return_address(0),
! 		__builtin_return_address(1),
! 		__builtin_return_address(2),
! 		__builtin_return_address(3));
  	printk("        q = %p\n",q);
  	printk("       *q = %p\n",*q);
  }
***************
*** 601,630 ****
   * and the sorting routine counts on this..
   */
  static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
! #define SLOW_BUT_DEBUGGING_TIMERS 0
  
  void add_timer(struct timer_list * timer)
  {
  	unsigned long flags;
  	struct timer_list *p;
  
! #if SLOW_BUT_DEBUGGING_TIMERS
  	if (timer->next || timer->prev) {
! 		printk("add_timer() called with non-zero list from %p\n",
! 			__builtin_return_address(0));
  		return;
  	}
! #endif
  	p = &timer_head;
  	save_flags(flags);
  	cli();
  	do {
  		p = p->next;
  	} while (timer->expires > p->expires);
  	timer->next = p;
  	timer->prev = p->prev;
  	p->prev = timer;
  	timer->prev->next = timer;
  	restore_flags(flags);
  }
  
--- 611,889 ----
   * and the sorting routine counts on this..
   */
  static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
! 
! /*
!  * Get rid of all the timer debugging overhead.
!  * Bring it all back in by turning on CONFIG_WEBSHIELD_DEBUG
!  * in the configuration file.
!  */
! #ifdef CONFIG_WEBSHIELD_DEBUG
! 
! #else /* !CONFIG_WEBSHIELD_DEBUG */
! 
! #define scan_timer_log(str, p)
! #define log_timer(action, timer, prev, next)
! #define find_timer(timer)
! 
! #endif /* CONFIG_WEBSHIELD_DEBUG */
! 
! /*
!  * Count how many timers are on the list.  This is for debugging with
!  * Shift-ScrollLock only.
!  */
! int count_timer(void)
! {
! 	unsigned long flags;
! 	struct timer_list *p;
! 	int count = 0;
! 
! 	p = &timer_head;
! 	save_flags(flags);
! 	cli();
! 	while (p->next != &timer_head) {
! 		p = p->next;
! 		count++;
! 	}
! 	restore_flags(flags);
! 	return (count);
! }
! 
! #ifdef CONFIG_WEBSHIELD_DEBUG
! /*
!  * The timer_records array is a circular queue of records that
!  * log timer-related actions.
!  * Next_record points to the next timer_record to use.
!  * Records_looped flags whether or not we have used all the records.
!  * #5000*28 = 140,000 bytes.
!  * 50000*28 = 1,400,000 bytes.	... this causes spontaneous reboots on machines.
!  */
! #define NR_TIMER_LOG 5000
! struct timer_record {
! 	struct timer_list *timer;
! 	int action;
! 	struct timer_list *next;
! 	struct timer_list *prev;
! 	unsigned int jiffies;
! 	void *bt1;
! 	void *bt2;
! 	void *bt3;
! };
! static struct timer_record timer_records[NR_TIMER_LOG];
! static int records_looped = 0;
! static int next_record = 0;
! 
! /*
!  * Log a transaction on this timer.
!  * Actions are: 1. Add
!  *		2. Delete
!  *		3. Run (effectively deletes a timer)
!  *
!  * Interrupts are already cleared, so cli() is not needed.
!  */
! void log_timer(int action, struct timer_list *timer,
! 		struct timer_list *prev, struct timer_list *next)
! {
! 	struct timer_record *rp = &timer_records[next_record];
! 
! 	if (++next_record > NR_TIMER_LOG) {
! 		next_record = 0;
! 		records_looped++;
! 	}
! 	rp->timer = timer;
! 	rp->action = action;
! 	rp->next = next;
! 	rp->prev = prev;
! 	rp->jiffies = jiffies;
! 	rp->bt1 = __builtin_return_address(1);
! 	rp->bt2 = __builtin_return_address(2);
! 	/* run_timer_list does not have a long enough stack behind it */
! 	rp->bt3 = (action == 3) ? 0 : __builtin_return_address(3);
! }
! 
! /*
!  * Report recent transactions on this timer.
!  */
! void scan_timer_log(char *str, struct timer_list *timer)
! {
! 	int restart;
! 	struct timer_record *rp, *trigger;
! 	char *action_str;
! 	int nfound = 0;
! 	unsigned int oldest = 0;
! 
! 	/*
! 	 * Search backwards from next_record-1 to 0, and then from
! 	 * NR_TIMER_LOG-1 to next_record (which is the oldest record in
! 	 * the log, and thus the next to be reused).  Do the second part
! 	 * only if we have looped through the array at least once.
! 	 */
! 	trigger = &timer_records[0];
! 	rp = &timer_records[next_record - 1]; /* most recent record */
! 	for (restart = records_looped ? 1 : 0; restart >= 0; restart--) {
! 		oldest = trigger->jiffies;
! 		while (rp > trigger) {
! 			if (rp->timer == timer) {
! 				if (!nfound)
! 					printk("%s %p history:\n", str, timer);
! 				nfound++;
! 			    if (nfound < 6) { /* reduce output */
! 				switch (rp->action) {
! 					case 1: action_str = "ADD"; break;
! 					case 2: action_str = "DELETE"; break;
! 					case 3: action_str = "RUN"; break;
! 					default: action_str = "unknown"; break;
! 				}
! 				printk("  %s at %u, %p<-p->%p from %p %p %p\n",
! 					action_str, rp->jiffies, rp->prev,
! 					rp->next, rp->bt1, rp->bt2, rp->bt3);
! 			    }
! 			}
! 			rp--;
! 		}
! 		rp = &timer_records[NR_TIMER_LOG - 1];
! 		trigger = &timer_records[next_record]; /* oldest record */
! 	}
! 	if (!nfound)
! 		printk("no history found for %s %p\n", str, timer);
! 	printk("oldest record is %u, now it is %u\n", oldest, (int) jiffies);
! }
! 
! /*
!  * Determine if a given timer is already on the timer list.
!  * Return the timer just before it to match with timer->prev.
!  *
!  * This is used as a sanity check for timers that should or
!  * should not already be on the list.
!  * Also, attempt to fix things up if I hit a null p->next. (Yikes!)
!  */
! struct timer_list *find_timer(struct timer_list * timer)
! {
! 	unsigned long flags;
! 	struct timer_list *p, *pp;
! 	int count = 0;
! 
! 	if (!timer)
! 		return (struct timer_list *)0;
! 
! 	pp = p = &timer_head;
! 	save_flags(flags);
! 	cli();
! 	do {
! 		if (p->next == timer) {
! 			if (p != timer->prev) {
! 				printk("find_timer(): timer %p found on list\n",
! 					timer);
! 				printk("  but its prev %p!=%p, %p, %p, %p\n",
! 					timer->prev, p,
! 					__builtin_return_address(0),
! 					__builtin_return_address(1),
! 					__builtin_return_address(2));
! 				scan_timer_log("lost timer", timer);
! 				scan_timer_log("lost timer prev", p);
! 			}
! 			restore_flags(flags);
! 			return p; /* found the timer, return the prev */
! 		}
! 
! 		count++;
! 		/* diagnose a panic caused by a (usually) null p->next */
! 		if (!p->next) {
! 			printk("find_timer(): %p has null p->next as #%d, "
! 				"%u > %u: at %p, %p, %p, %p\n",
! 				p, count, (int)timer->expires, (int)p->expires,
! 				__builtin_return_address(0),
! 				__builtin_return_address(1),
! 				__builtin_return_address(2),
! 				__builtin_return_address(3));
! 			printk("  prev p is %p, expires=%u jiffies=%d\n",
! 				pp, (int)pp->expires, (int)jiffies);
! 			/*
! 			 * DEBUG:
! 			 *   report my most recent transactions,
! 			 *   report who was previously responsible
! 			 *     for pointing to me,
! 			 *   include backtraces in those reports.
! 			 */
! 			scan_timer_log("null timer", p);
! 			scan_timer_log("prev timer", pp);
! 
! 			/*
! 			 * Try to recover by making a new timer list.
! 			 * The front of the list (before p) is probably
! 			 * still good.  Everything after p is lost.
! 			 * I think p is bad too because I see reports of
! 			 * p->expires=0.  Use pp (previous p) if possible.
! 			 *
! 			 * I guess I could follow timer_head.prev for the
! 			 * list tail, but that is even riskier than this.
! 			 */
! 			p = pp;
! 			timer_head.prev = p;
! 			p->next = &timer_head;
! 		}
! 		pp = p;
! 		p = p->next;
! 	} while (p != &timer_head);
! 
! 	restore_flags(flags);
! 	return (struct timer_list *)0; /* did not find timer */
! }
! #endif /* CONFIG_WEBSHIELD_DEBUG */
  
  void add_timer(struct timer_list * timer)
  {
  	unsigned long flags;
  	struct timer_list *p;
  
! #ifdef CONFIG_WEBSHIELD_DEBUG
  	if (timer->next || timer->prev) {
! 		printk("add_timer() called with non-zero list from %p, %p, %p\n",
! 			__builtin_return_address(0),
! 			__builtin_return_address(1),
! 			__builtin_return_address(2));
! 		scan_timer_log("non-zero timer", timer);
  		return;
  	}
! 
! 	/* Make sure no timer entries point to this timer (even though
! 	 * I know from above that this timer points to no others).
! 	 */
! 	if (find_timer(timer)) {
! 		printk("add_timer(): timer on list from %p, %p, %p\n",
! 			__builtin_return_address(0),
! 			__builtin_return_address(1),
! 			__builtin_return_address(2));
! 		scan_timer_log("2nd non-zero timer", timer);
! 		return;
! 	}
! #endif /* CONFIG_WEBSHIELD_DEBUG */
! 
  	p = &timer_head;
  	save_flags(flags);
  	cli();
  	do {
+ 		if (!p->next) {
+ 			printk("add_timer(): null p->next from %p, %p, %p\n",
+ 				__builtin_return_address(0),
+ 				__builtin_return_address(1),
+ 				__builtin_return_address(2));
+ 			/*
+ 			 * DEBUG:
+ 			 *   report my most recent transactions,
+ 			 *   report who was previously responsible
+ 			 *     for pointing to me,
+ 			 *   include backtraces in those reports.
+ 			 */
+ 			scan_timer_log("null timer", p);
+ 			return;
+ 		}
  		p = p->next;
  	} while (timer->expires > p->expires);
  	timer->next = p;
  	timer->prev = p->prev;
  	p->prev = timer;
  	timer->prev->next = timer;
+ 	log_timer(1, timer, timer->prev, timer->next);
  	restore_flags(flags);
  }
  
***************
*** 637,642 ****
--- 896,936 ----
  		save_flags(flags);
  		cli();
  		if ((next = timer->next) != NULL) {
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 			if (!find_timer(timer)) {
+ 				int tcount;
+ 				char cbuf[30];
+ 				struct timer_list *t, *told;
+ 
+ 				printk("del_timer: deleting timer not really on list at %p, %p, %p\n",
+ 					__builtin_return_address(0),
+ 					__builtin_return_address(1),
+ 					__builtin_return_address(2));
+ 				scan_timer_log("lost timer", timer);
+ /* also print the history of the timer that likely caused the problem */
+ 				t = timer->prev;
+ 				told = timer;
+ 				tcount = 0;
+ 				while (t && t != &timer_head && tcount < 200) {
+ 					if (t->next != told) {
+ 					    sprintf(cbuf, "lost timer prev[%d]",
+ 						tcount + 1);
+ 					    scan_timer_log(cbuf, t);
+ 					    break;
+ 					}
+ 					told = t;
+ 					t = t->prev;
+ 					tcount++;
+ 				}
+ 				if (!t) {
+ 					sprintf(cbuf, "prev timer[%d] null",
+ 					    tcount);
+ 					scan_timer_log(cbuf, told);
+ 				} else if (t != &timer_head && t->next == told)
+ 					printk("del_timer:tcount=%d\n", tcount);
+ 			}
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
+ 			log_timer(2, timer, timer->prev, timer->next);
  			(next->prev = timer->prev)->next = next;
  			timer->next = timer->prev = NULL;
  			ret = 1;
***************
*** 654,659 ****
--- 948,954 ----
  	while ((timer = timer_head.next) != &timer_head && timer->expires <= jiffies) {
  		void (*fn)(unsigned long) = timer->function;
  		unsigned long data = timer->data;
+ 		log_timer(3, timer, timer->prev, timer->next);
  		timer->next->prev = timer->prev;
  		timer->prev->next = timer->next;
  		timer->next = timer->prev = NULL;
*** linux-2.0.28/net/Config.in	Mon Jul  8 00:21:46 1996
--- KERNEL/net/Config.in	Mon Nov 24 22:40:45 1997
***************
*** 3,8 ****
--- 3,10 ----
  #
  mainmenu_option next_comment
  comment 'Networking options'
+ comment 'WebShield debugging will affect performance'
+ bool 'WebShield debugging' CONFIG_WEBSHIELD_DEBUG
  bool 'Network firewalls' CONFIG_FIREWALL
  bool 'Network aliasing'  CONFIG_NET_ALIAS
  bool 'TCP/IP networking' CONFIG_INET
*** linux-2.0.28/net/bridge/br.c	Thu Nov  7 22:44:27 1996
--- KERNEL/net/bridge/br.c	Mon Nov 24 22:51:05 1997
***************
*** 39,47 ****
  #include <asm/segment.h>
  #include <asm/system.h>
  #include <net/br.h>
  
  static int br_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
- static void br_tick(unsigned long arg);
  int br_forward(struct sk_buff *skb, int port);	/* 3.7 */
  int br_port_cost(struct device *dev);	/* 4.10.2 */
  void br_bpdu(struct sk_buff *skb); /* consumes skb */
--- 39,72 ----
  #include <asm/segment.h>
  #include <asm/system.h>
  #include <net/br.h>
+ #include <net/ip.h>
+ 
+ /*
+  * WebShield bridging removes most of the BPDU standards overhead and
+  * uses the key algorithms and structures.  Turn off the removed
+  * portions with '#ifdef WS_BRIDGE_NOTDEF'.
+  */
+ #ifdef WS_BRIDGE_NOTDEF
+ #undefine WS_BRIDGE_NOTDEF
+ #endif
+ 
+ /*
+  * Turn off the diagnostic counters and remove the WSbridgelog file
+  * unless CONFIG_WEBSHIELD_DEBUG is turned on in the config file.
+  */
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ /* Diagnostic counters for /proc/net/WSbridgelog. */
+ int br_found_cnt = 0, br_notfound_cnt = 0, br_learned_cnt = 0;
+ int br_dropped_cnt = 0, br_devdropped_cnt = 0;
+ int br_tx_cnt = 0, br_queuexmit_cnt = 0, br_forwarded_cnt = 0, br_receive_cnt=0;
+ int br_scancheck_cnt = 0, br_host_cnt = 0, br_scanproto_cnt = 0;
+ int br_scantcp_cnt = 0, br_noscan_cnt = 0;
+ #define DEBUG_INCR(x)	x++
+ #else
+ #define DEBUG_INCR(x)
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
  
  static int br_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
  int br_forward(struct sk_buff *skb, int port);	/* 3.7 */
  int br_port_cost(struct device *dev);	/* 4.10.2 */
  void br_bpdu(struct sk_buff *skb); /* consumes skb */
***************
*** 66,73 ****
  
  struct br_stat br_stats;
  
- static struct timer_list tl; /* for 1 second timer... */
- 
  /*
   * the following structure is required so that we receive
   * event notifications when network devices are enabled and
--- 91,96 ----
***************
*** 79,84 ****
--- 102,108 ----
  	0
  };
  
+ #ifdef WS_BRIDGE_NOTDEF
  /** Elements of Procedure (4.6) **/
  
  /*
***************
*** 441,452 ****
--- 465,478 ----
  		stop_forward_delay_timer(port_no);/* (4.6.13.3.3) */
  	}
  }
+ #endif /* WS_BRIDGE_NOTDEF */
  
  void set_port_state(int port_no, int state)
  {
  	port_info[port_no].state = state;
  }
  
+ #ifdef WS_BRIDGE_NOTDEF
  void received_config_bpdu(int port_no, Config_bpdu *config)		  /* (4.7.1)	 */
  {
  	int         root;
***************
*** 574,584 ****
--- 600,612 ----
  		transmit_config(port_no);	  /* (4.7.8.1)	 */
  	}					  /* (4.6.1.2.3)	 */
  }
+ #endif /* WS_BRIDGE_NOTDEF */
  
  void br_init(void)
  {						  /* (4.8.1)	 */
  	int             port_no;
  
+ #ifdef WS_BRIDGE_NOTDEF
  	printk(KERN_INFO "Ethernet Bridge 002 for NET3.035 (Linux 2.0)\n");
  	bridge_info.designated_root = bridge_info.bridge_id;	/* (4.8.1.1)	 */
  	bridge_info.root_path_cost = Zero;
***************
*** 597,606 ****
  	bridge_info.top_change = 0;
  	stop_tcn_timer();
  	stop_topology_change_timer();
  	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.1.4) */
  		br_init_port(port_no);
- 		disable_port(port_no);
  	}
  	port_state_selection();			  /* (4.8.1.5)	 */
  	config_bpdu_generation();		  /* (4.8.1.6)	 */
  	
--- 625,635 ----
  	bridge_info.top_change = 0;
  	stop_tcn_timer();
  	stop_topology_change_timer();
+ #endif /* WS_BRIDGE_NOTDEF */
  	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.1.4) */
  		br_init_port(port_no);
  	}
+ #ifdef WS_BRIDGE_NOTDEF
  	port_state_selection();			  /* (4.8.1.5)	 */
  	config_bpdu_generation();		  /* (4.8.1.6)	 */
  	
***************
*** 608,613 ****
--- 637,643 ----
  	tl.expires = jiffies+HZ;	/* 1 second */
  	tl.function = br_tick;
  	add_timer(&tl);
+ #endif /* WS_BRIDGE_NOTDEF */
  
  	register_netdevice_notifier(&br_dev_notifier);
  	br_stats.flags = 0; /*BR_UP | BR_DEBUG*/;	/* enable bridge */
***************
*** 616,621 ****
--- 646,653 ----
  
  void br_init_port(int port_no)
  {
+ 	set_port_state(port_no, Blocking);
+ #ifdef WS_BRIDGE_NOTDEF
  	become_designated_port(port_no);	  /* (4.8.1.4.1) */
  	set_port_state(port_no, Blocking);	  /* (4.8.1.4.2)    */
  	port_info[port_no].top_change_ack = 0;
***************
*** 623,640 ****
  	stop_message_age_timer(port_no);	  /* (4.8.1.4.5)	 */
  	stop_forward_delay_timer(port_no);	  /* (4.8.1.4.6)	 */
  	stop_hold_timer(port_no);		  /* (4.8.1.4.7)	 */
  }
  
  void enable_port(int port_no)				  /* (4.8.2)	 */
  {
! 	br_init_port(port_no);
! 	port_state_selection();			  /* (4.8.2.7)	 */
  }						  /* */
  
  void disable_port(int port_no)				  /* (4.8.3)	 */
  {
! 	int         root;
! 
  	root = root_bridge();
  	become_designated_port(port_no);	  /* (4.8.3.1)	 */
  	set_port_state(port_no, Disabled);	  /* (4.8.3.2)	 */
--- 655,673 ----
  	stop_message_age_timer(port_no);	  /* (4.8.1.4.5)	 */
  	stop_forward_delay_timer(port_no);	  /* (4.8.1.4.6)	 */
  	stop_hold_timer(port_no);		  /* (4.8.1.4.7)	 */
+ #endif /* WS_BRIDGE_NOTDEF */
  }
  
  void enable_port(int port_no)				  /* (4.8.2)	 */
  {
! 	set_port_state(port_no, Forwarding);
  }						  /* */
  
  void disable_port(int port_no)				  /* (4.8.3)	 */
  {
! 	set_port_state(port_no, Disabled);	  /* (4.8.3.2)	 */
! 	port_info[port_no].dev->internal = 0;
! #ifdef WS_BRIDGE_NOTDEF
  	root = root_bridge();
  	become_designated_port(port_no);	  /* (4.8.3.1)	 */
  	set_port_state(port_no, Disabled);	  /* (4.8.3.2)	 */
***************
*** 653,661 ****
--- 686,696 ----
  		config_bpdu_generation();	  /* (4.8.3.8.4)    */
  		start_hello_timer();
  	}
+ #endif /* WS_BRIDGE_NOTDEF */
  }
  
  
+ #ifdef WS_BRIDGE_NOTDEF
  void set_bridge_priority(bridge_id_t *new_bridge_id)		  /* (4.8.4)	 */
  {
  
***************
*** 983,988 ****
--- 1018,1024 ----
  	restore_flags(flags);
  	return(0);
  }
+ #endif /* WS_BRIDGE_NOTDEF */
  
  static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
  {
***************
*** 1017,1023 ****
  			if ((port_info[i].dev == (struct device *)0) ||
  				(port_info[i].dev == dev)) {
  				port_info[i].dev = dev;
! 				enable_port(i);
  				set_path_cost(i, br_port_cost(dev));
  				set_port_priority(i, 128); 
  				port_info[i].port_id = i;
--- 1053,1061 ----
  			if ((port_info[i].dev == (struct device *)0) ||
  				(port_info[i].dev == dev)) {
  				port_info[i].dev = dev;
! /* Let br_ioctl() do this part. */
! /*				enable_port(i); */
! #ifdef WS_BRIDGE_NOTDEF
  				set_path_cost(i, br_port_cost(dev));
  				set_port_priority(i, 128); 
  				port_info[i].port_id = i;
***************
*** 1029,1034 ****
--- 1067,1073 ----
  					set_bridge_priority(&bridge_info.bridge_id);
  				}	
  				make_forwarding(i);
+ #endif /* WS_BRIDGE_NOTDEF */
  				return NOTIFY_DONE;
  				break;
  			}
***************
*** 1044,1049 ****
--- 1083,1195 ----
  }
  
  /*
+  * Peek at the network and transport protocols to see if this packet
+  * is scanable.
+  */
+ int brscancheck(struct sk_buff *skb, int port)
+ {
+ 	struct fdb *f;
+ 	struct ethhdr *eth = skb->h.eth;
+ 
+ 	DEBUG_INCR(br_scancheck_cnt);
+ 	/* check for a broadcast */
+ 	if (eth->h_dest[0] & 0x01) 
+ 	{
+ 		/* group address */
+ 		br_flood(skb, port);
+ 		return(0);
+ 	}
+ 
+ 	/* check for packet directed at this interface */
+ 	if (memcmp(eth->h_dest, port_info[port].dev->dev_addr, ETH_ALEN) == 0)
+ 	{
+ 		/* Packet is for us */
+ 		DEBUG_INCR(br_host_cnt);
+ 		skb->pkt_type = PACKET_HOST;
+ 		return(0);	/* pass frame up our stack (this will */
+ 				/* happen in net_bh() in dev.c) */
+ 	}
+ 
+ 	/* check for packet directed at the internal interface */
+ 	/* (only if this external interface has an IP address) */
+ 	if (!port_info[port].dev->internal && port_info[port].dev->pa_addr) {
+ 		int aport;
+ 		for (aport = One; aport <= No_of_ports; aport++)
+ 		{
+ 			if (port_info[aport].state == Forwarding &&
+ 				port_info[aport].dev->internal)
+ 			{
+ 				if (memcmp(eth->h_dest,
+ 					port_info[aport].dev->dev_addr,
+ 					ETH_ALEN) == 0)
+ 				{
+ 					skb->pkt_type = PACKET_HOST;
+ 					DEBUG_INCR(br_host_cnt);
+ 					return(0);
+ 				}
+ 			}
+ 		}
+ 	}
+ 	/* locate port to forward to */
+ 	f = br_avl_find_addr(skb->h.eth->h_dest);
+ 	if (f && f->flags & FDB_ENT_VALID)
+ 	{
+ 		if (f->timer + fdb_aging_time > CURRENT_TIME) {
+ 			/*
+ 			 * The fdb entry still valid, so check its port.
+ 			 * If that port's state is not Forwarding, then
+ 			 * we drop the packet because the destination
+ 			 * LAN is not being scanned.
+ 			 * If that port matches the incoming port, then
+ 			 * we drop the packet because the destination is
+ 			 * on the same LAN as the source.
+ 			 */
+ 			if (port_info[f->port].state != Forwarding)
+ 				return(br_drop(skb));
+ 			if (f->port==port)	/* same port as arrival */
+ 				return(br_drop(skb));	/* no need to scan */
+ 			/* we have a valid and non-source port fdb entry */
+ 		} else {
+ 			/* timer expired, invalidate entry */
+ 			f->flags &= ~FDB_ENT_VALID;
+ 			if (br_stats.flags & BR_DEBUG)
+ 				printk("fdb entry expired...\n");
+ 		}
+ 	}
+ 
+ 	DEBUG_INCR(br_scanproto_cnt);
+ 	/*
+ 	 * This interface is being bridged.
+ 	 *
+ 	 * We still have not decided to bridge it or queue it.
+ 	 * This requires knowledge of Network & Transport protocols.
+ 	 * Now the drivers set skb->protocol with eth_type_trans(),
+ 	 * and net_bh() sets skb->ip_hdr before sending us the packet.
+ 	 */
+ 	switch(ntohs(skb->protocol)) {
+ 	case ETH_P_IP: {
+ 		struct iphdr *iph = skb->ip_hdr;
+ 
+ 		switch(iph->protocol) {
+ 		case IPPROTO_TCP:	/* catch TCP packets */
+ 			skb->ipcatch = 1;
+ 			DEBUG_INCR(br_scantcp_cnt);
+ 			return(0);	/* pass it up the stack */
+ 			break;
+ 		case IPPROTO_UDP:	/* bridge UDP (for now) */
+ 		default:		/* bridge other IP protocols */
+ 			break;
+ 		}
+ 		break;
+ 	}
+ 	default:	/* bridge all other ether types */
+ 		break;
+ 	}
+ 	DEBUG_INCR(br_noscan_cnt);
+ 	return(br_forward(skb, port));
+ }
+ 
+ /*
   * following routine is called when a frame is received
   * from an interface, it returns 1 when it consumes the
   * frame, 0 when it does not
***************
*** 1052,1058 ****
  int br_receive_frame(struct sk_buff *skb)	/* 3.5 */
  {
  	int port;
- 	int i;
  	
  	if (br_stats.flags & BR_DEBUG)
  		printk("br_receive_frame: ");
--- 1198,1203 ----
***************
*** 1095,1102 ****
--- 1240,1249 ----
  		return(0);
  	}
  
+ 	DEBUG_INCR(br_receive_cnt);
  	switch (port_info[port].state) 
  	{
+ #ifdef WS_BRIDGE_NOTDEF
  		case Learning:
  			(void) br_learn(skb, port);	/* 3.8 */
  			/* fall through */
***************
*** 1109,1114 ****
--- 1256,1262 ----
  			/* fall through */
  		case Blocking:
  			/* fall through */
+ #endif /* WS_BRIDGE_NOTDEF */
  		case Disabled:
  			/* should drop frames, but for now, we let
  			 * them get passed up to the next higher layer
***************
*** 1118,1123 ****
--- 1266,1272 ----
  			break;
  		case Forwarding:
  			(void) br_learn(skb, port);	/* 3.8 */
+ #ifdef WS_BRIDGE_NOTDEF
  			/* process BPDUs */
  			if (memcmp(skb->h.eth->h_dest, bridge_ula, 
  					ETH_ALEN) == 0) 
***************
*** 1138,1143 ****
--- 1287,1294 ----
  			}
  			/* ok, forward this frame... */
  			return(br_forward(skb, port));
+ #endif /* WS_BRIDGE_NOTDEF */
+ 			return(brscancheck(skb, port));
  		default:
  			printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n",
  				port, port_info[port].state);
***************
*** 1172,1179 ****
  	if (skb->dev->flags & IFF_LOOPBACK)
  		return(0);
  
  	skb->h.raw = skb->data;
! 	port = 0;	/* an impossible port */	
  	if (br_stats.flags & BR_DEBUG)
  		printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\
  	  		dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
--- 1323,1361 ----
  	if (skb->dev->flags & IFF_LOOPBACK)
  		return(0);
  
+ 	/*
+ 	 * As in bridge_xmit(), check for a panic condition reported
+ 	 * at a customer site.  Dump the skb and free the packet.
+ 	 */
+ 	if (skb->data < skb->head || skb->data > skb->end) {
+ 		unsigned int *wp;
+ 		int i;
+ 		printk("br_tx_frame: bad head/data/end %p/%p/%p\n",
+ 			skb->head, skb->data, skb->end);
+ 		printk("  skb=%p, h.raw=%p, mac.raw=%p, tail=%p, len=%d\n",
+ 			skb, skb->h.raw, skb->mac.raw, skb->tail,(int)skb->len);
+ 		/*
+ 		 * alloc_skb() disassembly shows 0x98 bytes or 38 words
+ 		 * in an skbuff struct.  Dump it.
+ 		 * This dumps 6 lines of 6 words and one line of 2 words.
+ 		 */
+ 		for (i=0, wp=(unsigned int *)skb; i<36; i+=6, wp+=6) {
+ 			printk("  %08x %08x %08x %08x %08x %08x\n",
+ 				wp[0], wp[1], wp[2], wp[3], wp[4], wp[5]);
+ 		}
+ 		printk("  %08x %08x\n", wp[0], wp[1]);
+ 		printk("Dropping it\n");
+ 		/* kfree_skb(skb, 0); Don't... this may panic. */
+ 		return(1);
+ 	}
+ 	DEBUG_INCR(br_tx_cnt);
  	skb->h.raw = skb->data;
! 	if (skb->indev)
! 		/* Must not xmit on incoming port */
! 		/* or else sender will see its own packet. */
! 		port = find_port(skb->indev);
! 	else
! 		port = 0;	/* an impossible port */	
  	if (br_stats.flags & BR_DEBUG)
  		printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\
  	  		dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
***************
*** 1203,1208 ****
--- 1385,1391 ----
  {
  	struct fdb *f;
  
+ #ifdef WS_BRIDGE_NOTDEF
  	switch (port_info[port].state) {
  		case Listening:
  		case Blocking:
***************
*** 1212,1221 ****
--- 1395,1415 ----
  			/* break; */
  		case Learning:
  		case Forwarding:
+ #endif /* WS_BRIDGE_NOTDEF */
  			/* don't keep group addresses in the tree */
  			if (skb->h.eth->h_source[0] & 0x01)
  				return(-1);
  			
+ 			f = br_avl_find_addr(skb->h.eth->h_source);
+ 			if (f) { /* common case */
+ 				f->timer = CURRENT_TIME;
+ 				f->flags = FDB_ENT_VALID;
+ 				f->port = port;
+ 				DEBUG_INCR(br_found_cnt);
+ 				return (0);
+ 			}
+ 			DEBUG_INCR(br_notfound_cnt);
+ 
  			f = (struct fdb *)kmalloc(sizeof(struct fdb), 
  				GFP_ATOMIC);
  
***************
*** 1239,1247 ****
--- 1433,1444 ----
  			/* add to head of port chain */
  			f->fdb_next = port_info[port].fdb;
  			port_info[port].fdb = f;
+ 			DEBUG_INCR(br_learned_cnt);
  			return(0);
  			/* break */
+ #ifdef WS_BRIDGE_NOTDEF
  	}
+ #endif /* WS_BRIDGE_NOTDEF */
  }
  
  /*
***************
*** 1250,1255 ****
--- 1447,1453 ----
  
  int br_drop(struct sk_buff *skb)
  {
+ 	DEBUG_INCR(br_dropped_cnt);
  	kfree_skb(skb, 0);
  	return(1);
  }
***************
*** 1260,1265 ****
--- 1458,1464 ----
  
  int br_dev_drop(struct sk_buff *skb)
  {
+ 	DEBUG_INCR(br_devdropped_cnt);
  	dev_kfree_skb(skb, 0);
  	return(1);
  }
***************
*** 1273,1278 ****
--- 1472,1478 ----
  {
  	struct fdb *f;
  	
+ 	DEBUG_INCR(br_forwarded_cnt);
  	/*
     	 * flood all ports with frames destined for a group
  	 * address.  If frame came from above, drop it,
***************
*** 1306,1312 ****
  		/*
  		 *	Sending
  		 */
! 		if (f->port!=port && port_info[f->port].state == Forwarding) {
  			/* has entry expired? */
  			if (f->timer + fdb_aging_time < CURRENT_TIME) {
  				/* timer expired, invalidate entry */
--- 1506,1512 ----
  		/*
  		 *	Sending
  		 */
! 		if (f->port!=port) {
  			/* has entry expired? */
  			if (f->timer + fdb_aging_time < CURRENT_TIME) {
  				/* timer expired, invalidate entry */
***************
*** 1330,1335 ****
--- 1530,1536 ----
  			 */
  			 
  			skb->dev=port_info[f->port].dev;
+ 			DEBUG_INCR(br_queuexmit_cnt);
  			 
  			/*
  			 *	We send this still locked
***************
*** 1360,1366 ****
  	{
  		if (i == port)
  			continue;
! 		if (port_info[i].state == Forwarding) 
  		{
  			nskb = skb_clone(skb, GFP_ATOMIC);
  			if(nskb==NULL)
--- 1561,1567 ----
  	{
  		if (i == port)
  			continue;
! 		if (port_info[i].dev)
  		{
  			nskb = skb_clone(skb, GFP_ATOMIC);
  			if(nskb==NULL)
***************
*** 1386,1397 ****
  	int i;
  
  	for (i = One; i <= No_of_ports; i++)
! 		if ((port_info[i].dev == dev) && 
! 			(port_info[i].state != Disabled))
  			return(i);
  	return(0);
  }
  
  int br_port_cost(struct device *dev)	/* 4.10.2 */
  {
  	if (strncmp(dev->name, "eth", 3) == 0)	/* ethernet */
--- 1587,1598 ----
  	int i;
  
  	for (i = One; i <= No_of_ports; i++)
! 		if (port_info[i].dev == dev)
  			return(i);
  	return(0);
  }
  
+ #ifdef WS_BRIDGE_NOTDEF
  int br_port_cost(struct device *dev)	/* 4.10.2 */
  {
  	if (strncmp(dev->name, "eth", 3) == 0)	/* ethernet */
***************
*** 1523,1529 ****
--- 1724,1793 ----
  	/*NOTREACHED*/
  	return 0;
  }
+ #endif /* WS_BRIDGE_NOTDEF */
  
+ /* This is largely copied from dev_ifsioc(). */
+ int br_ioctl(unsigned int cmd, void *arg)
+ {
+ 	struct ifreq ifr;
+ 	struct device *dev, *ext;
+ 	int inport, outport;
+ 
+ 	/* Fetch the caller's info block into kernel space. */
+ 	int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
+ 	if(err)
+ 		return err;
+ 	memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
+ 
+ 	/* See which interface the caller is talking about. */
+ 	if ((dev = dev_get(ifr.ifr_name)) == NULL) 	
+ 		return(-ENODEV);
+ 	if ((inport=find_port(dev)) == 0)
+ 		return(-ENODEV);
+ 
+ 	switch(cmd) {
+ 	case SIOCGIFBR:
+ 		/* This ioctl is currently unused, skip it. */
+ 		return(0);
+ 		break;
+ 
+ 	case SIOCSIFBR:
+ 		ext = dev_get(ifr.ifr_slave);
+ 
+ 		if(ext) { /* enable bridging */
+ 			if (br_stats.flags & BR_UP)
+ 				return(-EADDRINUSE);
+ 			if ((outport=find_port(ext)) == 0)
+ 				return(-ENODEV);
+ 			br_stats.flags |= BR_UP;	/* enable bridge */
+ 			enable_port(inport);
+ 			enable_port(outport);
+ 			port_info[inport].dev->internal = 1;
+ 			return(0);
+ 		} else { /* disable bridging */
+ 			if (!(br_stats.flags & BR_UP))
+ 				return(-ENODEV);	/* already disabled */
+ 			for (inport = One; inport <= No_of_ports; inport++)
+ 				if (port_info[inport].state == Forwarding) {
+ 					disable_port(inport);
+ 				}
+ 			br_stats.flags &= ~BR_UP;	/* disable bridge */
+ 			return(0);
+ 		}
+ 		break;
+ 	case SIOCBRNODEBUG:
+ 		br_stats.flags &= ~BR_DEBUG;
+ 		break;
+ 	case SIOCBRDEBUG:
+ 		br_stats.flags |= BR_DEBUG;
+ 		break;
+ 	default:
+ 		return(-EINVAL);
+ 	}
+ 	return(0);
+ }
+ 
+ #ifdef WS_BRIDGE_NOTDEF
  int br_cmp(unsigned int *a, unsigned int *b)
  {
  	int i;	
***************
*** 1538,1541 ****
  	}
  	return(0);
  }
! 		
--- 1802,1940 ----
  	}
  	return(0);
  }
! #endif /* WS_BRIDGE_NOTDEF */
! 
! /*
!  * Provide a hook for ARP to change an external device's source hardware
!  * address to the internal device's hardware address.
!  */
! char *a2b_src_hw(struct device *dev, char *src_hw)
! {
! 	int port_no, internal_port=0;
! 	int found_port=0;
! 	struct device *dev2;
! 
! 	for (port_no = One; port_no <= No_of_ports; port_no++) {
! 		dev2 = port_info[port_no].dev;
! 		if (!dev2)
! 			continue;
! 		if (dev2 == dev) {
! 			if (port_info[port_no].state != Forwarding)
! 				return (src_hw);
! 			if (dev2->internal)
! 				return (src_hw);
! 			found_port++;
! 		}
! 		if (dev2->internal)
! 			internal_port = port_no;
! 	}
! 	/*
! 	 * If the specified device is bridged and we found an internal
! 	 * interface, use the internal interface's hardware address.
! 	 */
! 	if (found_port && internal_port)
! 		return (port_info[internal_port].dev->dev_addr);
! 	return (src_hw);
! }
! 
! #ifdef CONFIG_WEBSHIELD_DEBUG
! /*
!  * Provide the contents of /proc/net/WSbridgelog.
!  */
! extern void proc_usedata(char *buffer, char *tmpbuf,
! 	int *pos, int offset, int *len);
! 
! int WSbridgelog_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
! {
! 	int len = 0; /* offset from the start of the given buffer */
! 	int pos = 0; /* offset from the start of the file */
! 	int i, port;
! 	char tmpbuf[200]; /* Good enough? */
! 	unsigned char *p;
! 	struct fdb *f;
! 
! 	*start = buffer;
! 
!     for (port = 0, i=0; port < 3; port++) {
! 	f = port_info[port].fdb;
! 	for (; i < 1000 && f != NULL; f = f->fdb_next) {
! 		p = f->ula;
! 		sprintf(tmpbuf, "fdb %d: %02x:%02x:%02x:%02x:%02x:%02x (%d)",
! 			i, p[0], p[1], p[2], p[3], p[4], p[5], f->port);
! 		/* get 3 MAC addresses per line: 23*3+6=75 chars */
! 		if (f->fdb_next != NULL) {
! 			f = f->fdb_next;
! 			i++;
! 			p = f->ula;
! 			sprintf(tmpbuf + strlen(tmpbuf),
! 				", %02x:%02x:%02x:%02x:%02x:%02x (%d)",
! 				p[0], p[1], p[2], p[3], p[4], p[5], f->port);
! 		}
! 		if (f->fdb_next != NULL) {
! 			f = f->fdb_next;
! 			i++;
! 			p = f->ula;
! 			sprintf(tmpbuf + strlen(tmpbuf),
! 				", %02x:%02x:%02x:%02x:%02x:%02x (%d)",
! 				p[0], p[1], p[2], p[3], p[4], p[5], f->port);
! 		}
! 		sprintf(tmpbuf + strlen(tmpbuf), "\n");
! 		proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 		if (len >= length)
! 			return length;
! 	}
!     }
! 	sprintf(tmpbuf, "Found %d forwarding database records\n\n", i);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	if (len >= length)
! 		return length;
! 
! 	sprintf(tmpbuf, "Learning: found/notfound/learned = %d/%d/%d\n",
! 		br_found_cnt, br_notfound_cnt, br_learned_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Dropped: dropped/devdropped = %d/%d\n",
! 		br_dropped_cnt, br_devdropped_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	if (len >= length)
! 		return length;
! 
! 	sprintf(tmpbuf, "Out: tx (from system to bridge): %d\n", br_tx_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Out: queuexmit (from bridge to device): %d\n",
! 		br_queuexmit_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Out: forward (from system or bridge to bridge): %d\n",
! 		br_forwarded_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	if (len >= length)
! 		return length;
! 
! 	sprintf(tmpbuf, "In: receive_frame (from device to bridge): %d\n",
! 		br_receive_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	if (len >= length)
! 		return length;
! 
! 	sprintf(tmpbuf, "Scan: checked for scanning: %d\n", br_scancheck_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Scan: target at me: %d\n", br_host_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Scan: checked protocol in header: %d\n",
! 		br_scanproto_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Scan: found TCP in header: %d\n", br_scantcp_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	sprintf(tmpbuf, "Scan: not scanned: %d\n", br_noscan_cnt);
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 	if (len >= length)
! 		return length;
! 
! 	sprintf(tmpbuf, "\nBridge is %s.\n",
! 		(br_stats.flags & BR_UP) ? "up" : "down");
! 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
! 
! 	if (len > length)
! 		len = length;
! 	return len;
! }
! #endif /* CONFIG_WEBSHIELD_DEBUG */
*** linux-2.0.28/net/bridge/br_tree.c	Mon May 13 02:15:24 1996
--- KERNEL/net/bridge/br_tree.c	Thu Jul 31 12:02:54 1997
***************
*** 217,222 ****
--- 217,223 ----
  		if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */
  			node->flags = new_node->flags;
  			node->timer = new_node->timer;	
+ 			node->port = new_node->port;	
  			return(0);
  		}
  		if (addr_cmp(new_node->ula, node->ula) < 0) {
*** linux-2.0.28/net/core/dev.c	Wed Nov  6 04:39:47 1996
--- KERNEL/net/core/dev.c	Thu Jul 31 12:02:52 1997
***************
*** 256,261 ****
--- 256,262 ----
  	if (ret == 0) 
  	{
  		dev->flags |= (IFF_UP | IFF_RUNNING);
+ 		dev->internal = 0;
  		/*
  		 *	Initialise multicasting status 
  		 */
***************
*** 308,313 ****
--- 309,318 ----
  				kfree_skb(skb,FREE_WRITE);
  		ct++;
  	}
+ 	/*
+ 	 *	Also disconnect from scanned device pair.
+ 	 *	This turns bridging off in both devices.
+ 	 */
  	return(0);
  }
  
***************
*** 328,333 ****
--- 333,378 ----
  }
  
  /*
+  *	Bridging code has decided to transmit.  Set the packet up
+  *	correctly, first.  Caller must:
+  *	1. make sure sock accounting for this packet is FREE_WRITE,
+  *		because that is what the consuming driver will use.
+  *		In kfree_skb(), skbuffs associated with socks are
+  *		accounted for in the socks' read and write memory
+  *		allocation counters (rmem_alloc and wmem_alloc).
+  *		Failure to do this will cause a sock leak because
+  *		destroy_sock will not free the sock until these
+  *		counts are 0.
+  *	2. The caller should no longer muck with skb->len.  This is
+  *		because enough mechanisms muck with skb->len along
+  *		with the now-variable skb->data that bridge_xmit()
+  *		will fix them both.  This routine can only be called
+  *		for packets which have had skb->mac initialized as
+  *		is done by individual drivers calling eth_type_trans().
+  */
+ void bridge_xmit(struct sk_buff *skb, struct device *dev, int pri)
+ {
+ 	skb->arp = 1;
+ /*
+  * One customer (at least) reports a panic apparently caused by mac.raw
+  * getting set to time in seconds.  This is an early report of data and
+  * len when a bad mac.raw is detected.  A more thorough dump is made in
+  * br_tx_frame().
+  */
+ 	if (skb->mac.raw < skb->head || skb->mac.raw > skb->end)
+ 		printk("bridge_xmit: bad head/mac.raw/end %p/%p/%p\n",
+ 			skb->head, skb->mac.raw, skb->end);
+ 	if (skb->data < skb->head || skb->data > skb->end)
+ 		printk("bridge_xmit: bad head/data/end %p/%p/%p\n",
+ 			skb->head, skb->data, skb->end);
+ 
+ 	skb->len += (skb->data - skb->mac.raw);
+ 	skb->data = skb->mac.raw;
+ 	skb->pkt_bridged = 0;
+ 	dev_queue_xmit(skb, dev, pri);
+ }
+ 
+ /*
   *	Send (or queue for sending) a packet. 
   *
   *	IMPORTANT: When this is called to resend frames. The caller MUST
***************
*** 608,614 ****
  		 *	If we are bridging then pass the frame up to the
  		 *	bridging code. If it is bridged then move on
  		 */
! 		 
  		if (br_stats.flags & BR_UP)
  		{
  			/*
--- 653,661 ----
  		 *	If we are bridging then pass the frame up to the
  		 *	bridging code. If it is bridged then move on
  		 */
! 		/* bridge-layer scanning initialization */
! 		skb->ipcatch = 0;
! 		skb->indev = skb->dev;
  		if (br_stats.flags & BR_UP)
  		{
  			/*
***************
*** 617,622 ****
--- 664,671 ----
  			 */
  			 
  			int offset=skb->data-skb->mac.raw;
+ 			/* for possible use by brscancheck() */
+ 			skb->ip_hdr = (struct iphdr *) skb->data;
  			cli();
  			skb_push(skb,offset);	/* Put header back on for bridge */
  			if(br_receive_frame(skb))
***************
*** 703,710 ****
  		 * 	Has an unknown packet has been received ?
  		 */
  	 
! 		else
! 			kfree_skb(skb, FREE_WRITE);
  		/*
  		 *	Again, see if we can transmit anything now. 
  		 *	[Ought to take this out judging by tests it slows
--- 752,766 ----
  		 * 	Has an unknown packet has been received ?
  		 */
  	 
! 		else {
! 			/* bridge packets of unknown type */
! 			/* netif_rx() should prevent this */
! 			if(skb->ipcatch) {
! 				bridge_xmit(skb, skb->dev, 0);
! 			} else
! 				kfree_skb(skb, FREE_WRITE);
! 		}
! 
  		/*
  		 *	Again, see if we can transmit anything now. 
  		 *	[Ought to take this out judging by tests it slows
*** linux-2.0.28/net/core/skbuff.c	Wed Jun  5 23:45:39 1996
--- KERNEL/net/core/skbuff.c	Thu Jul 31 12:02:52 1997
***************
*** 67,72 ****
--- 67,74 ----
  
  extern atomic_t ip_frag_mem;
  
+ extern int count_timer(void);
+ 
  void show_net_buffers(void)
  {
  	printk(KERN_INFO "Networking buffers in use          : %u\n",net_skbcount);
***************
*** 77,82 ****
--- 79,85 ----
  #ifdef CONFIG_INET
  	printk(KERN_INFO "IP fragment buffer size            : %u\n",ip_frag_mem);
  #endif	
+ 	printk(KERN_INFO "The timer_head list has %d timers\n", count_timer());
  }
  
  #if CONFIG_SKB_CHECK
***************
*** 640,647 ****
  	{
  		static int count = 0;
  		if (++count < 5) {
! 			printk(KERN_ERR "alloc_skb called nonatomically from interrupt %p\n",
! 				__builtin_return_address(0));
  			priority = GFP_ATOMIC;
  		}
  	}
--- 643,652 ----
  	{
  		static int count = 0;
  		if (++count < 5) {
! 			printk(KERN_ERR "alloc_skb called nonatomically from interrupt %p, %p, %p\n",
! 				__builtin_return_address(0),
! 				__builtin_return_address(1),
! 				__builtin_return_address(2));
  			priority = GFP_ATOMIC;
  		}
  	}
***************
*** 702,707 ****
--- 707,713 ----
  	skb->end=bptr+len;
  	skb->len=0;
  	skb->destructor=NULL;
+ 	skb->indev=NULL;
  	return skb;
  }
  
*** linux-2.0.28/net/core/sock.c	Tue Oct 29 17:42:42 1996
--- KERNEL/net/core/sock.c	Mon Nov 24 22:51:30 1997
***************
*** 342,347 ****
--- 342,352 ----
  
  void sk_free(struct sock *sk)
  {
+ #if CONFIG_WEBSHIELD_DEBUG
+ 	extern void free_sockevents();
+ 	free_sockevents(sk);
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
+ 
  	kfree_s(sk,sizeof(*sk));
  }
  
*** linux-2.0.28/net/ipv4/af_inet.c	Thu Oct 17 21:18:54 1996
--- KERNEL/net/ipv4/af_inet.c	Mon Nov 24 22:51:53 1997
***************
*** 113,118 ****
--- 113,122 ----
  extern int afinet_get_info(char *, char **, off_t, int, int);
  extern int tcp_get_info(char *, char **, off_t, int, int);
  extern int udp_get_info(char *, char **, off_t, int, int);
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ extern int WSsocklog_get_info(char *, char **, off_t, int, int);
+ extern int WSbridgelog_get_info(char *, char **, off_t, int, int);
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
  
  #ifdef CONFIG_DLCI
  extern int dlci_ioctl(unsigned int, void*);
***************
*** 159,164 ****
--- 163,169 ----
  	int best = 0;
  	int size = 32767; /* a big num. */
  	struct sock *sk;
+ 	unsigned long flags;
  
  	if (base == 0) 
  		base = PROT_SOCK+1+(start & 1023);
***************
*** 171,176 ****
--- 176,184 ----
  	 *	Now look through the entire array and try to find an empty ptr. 
  	 */
  	 
+ 	/* We can't have this changing out from under us. */
+ 	save_flags(flags);
+ 	cli();
  	for(i=0; i < SOCK_ARRAY_SIZE; i++) 
  	{
  		j = 0;
***************
*** 183,188 ****
--- 191,197 ----
  		if (j == 0) 
  		{
  			start =(i+1+start )&1023;
+ 			restore_flags(flags);
  			return(i+base+1);
  		}
  		if (j < size) 
***************
*** 198,203 ****
--- 207,213 ----
  	{
  		best += SOCK_ARRAY_SIZE;
  	}
+ 	restore_flags(flags);
  	return(best+base+1);
  }
  
***************
*** 266,272 ****
   *	Remove a socket from the socket tables.
   */
  
! static void remove_sock(struct sock *sk1)
  {
  	struct sock **p;
  	unsigned long flags;
--- 276,282 ----
   *	Remove a socket from the socket tables.
   */
  
! void remove_sock(struct sock *sk1)
  {
  	struct sock **p;
  	unsigned long flags;
***************
*** 306,312 ****
--- 316,326 ----
  void destroy_sock(struct sock *sk)
  {
  	struct sk_buff *skb;
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 	extern struct timer_list *find_timer(struct timer_list *t);
+ #endif CONFIG_WEBSHIELD_DEBUG
  
+ 	log_sockevent(sk, 13, sk->rmem_alloc, sk->wmem_alloc);
  	lock_sock(sk);			/* just to be safe. */
  
    	remove_sock(sk);
***************
*** 319,325 ****
    	delete_timer(sk);
  	del_timer(&sk->delack_timer);
  	del_timer(&sk->retransmit_timer);
! 	
  	/*
  	 *	Drain any partial frames
  	 */
--- 333,351 ----
    	delete_timer(sk);
  	del_timer(&sk->delack_timer);
  	del_timer(&sk->retransmit_timer);
! 
! #ifdef CONFIG_WEBSHIELD_DEBUG
! #if 0
! /* This timer is never used, so it is not even initialized. */
! 	if (find_timer(sk->keepalive_timer)) {
! 		printk("destroy_sock: found a keepalive timer at %p, %p, %p\n",
! 			__builtin_return_address(0),
! 			__builtin_return_address(1),
! 			__builtin_return_address(2));
! 	}
! #endif
! #endif CONFIG_WEBSHIELD_DEBUG
! 
  	/*
  	 *	Drain any partial frames
  	 */
***************
*** 329,334 ****
--- 355,368 ----
  		IS_SKB(skb);
  		kfree_skb(skb, FREE_WRITE);
  	}
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 	if (find_timer(&sk->partial_timer)) {
+ 		printk("destroy_sock: found a partial timer at %p, %p, %p\n",
+ 			__builtin_return_address(0),
+ 			__builtin_return_address(1),
+ 			__builtin_return_address(2));
+ 	}
+ #endif CONFIG_WEBSHIELD_DEBUG
  
  	/*
  	 *	Cleanup up the write buffer. 
***************
*** 841,847 ****
  	}
  	
  	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
! #ifdef CONFIG_IP_TRANSPARENT_PROXY
  	/*
  	 * Superuser may bind to any address to allow transparent proxying.
  	 */
--- 875,886 ----
  	}
  	
  	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
! /* WAS_PROXY is used in this port in place of CONFIG_OP_TRANSPARENT_PROXY
!  * (TP).  I did not need all the TP stuff, but it looks good here.
!  * Eventually this will be undone.
!  */
! #define WAS_PROXY
! #ifdef WAS_PROXY
  	/*
  	 * Superuser may bind to any address to allow transparent proxying.
  	 */
***************
*** 851,857 ****
  #endif
  		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
  
! #ifndef CONFIG_IP_TRANSPARENT_PROXY
  	/*
  	 * Am I just thick or is this test really always true after the one
  	 * above?  Just taking the test out appears to be the easiest way to
--- 890,896 ----
  #endif
  		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
  
! #ifndef WAS_PROXY
  	/*
  	 * Am I just thick or is this test really always true after the one
  	 * above?  Just taking the test out appears to be the easiest way to
***************
*** 873,879 ****
  			sk->saddr = 0;  /* Use device */
  		else
  			sk->saddr = addr->sin_addr.s_addr;
! #ifndef CONFIG_IP_TRANSPARENT_PROXY
  	}
  #endif
  	if(sock->type != SOCK_RAW)
--- 912,918 ----
  			sk->saddr = 0;  /* Use device */
  		else
  			sk->saddr = addr->sin_addr.s_addr;
! #ifndef WAS_PROXY
  	}
  #endif
  	if(sock->type != SOCK_RAW)
***************
*** 1317,1327 ****
  
  		case SIOCGIFBR:
  		case SIOCSIFBR:
  #ifdef CONFIG_BRIDGE		
  			return(br_ioctl(cmd,(void *) arg));
  #else
  			return -ENOPKG;
! #endif						
  			
  		case SIOCADDDLCI:
  		case SIOCDELDLCI:
--- 1356,1368 ----
  
  		case SIOCGIFBR:
  		case SIOCSIFBR:
+ 		case SIOCBRDEBUG:
+ 		case SIOCBRNODEBUG:
  #ifdef CONFIG_BRIDGE		
  			return(br_ioctl(cmd,(void *) arg));
  #else
  			return -ENOPKG;
! #endif
  			
  		case SIOCADDDLCI:
  		case SIOCDELDLCI:
***************
*** 1406,1411 ****
--- 1447,1453 ----
  	unsigned short hpnum;
  	int firstpass = 1;
  #endif 
+ 	unsigned long flags;
  
  	hnum = ntohs(num);
  #ifdef CONFIG_IP_TRANSPARENT_PROXY
***************
*** 1421,1426 ****
--- 1463,1471 ----
  	 * socket number when we choose an arbitrary one.
  	 */
  
+ 	/* We can't have this changing out from under us. */
+ 	save_flags(flags);
+ 	cli();
  #ifdef CONFIG_IP_TRANSPARENT_PROXY
  	for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
  		s != NULL;
***************
*** 1479,1485 ****
--- 1524,1533 ----
  #else
  		if (score == 3)
  #endif
+ 		{
+ 			restore_flags(flags);
  			return s;
+ 		}
  		/* no, check if this is the best so far.. */
  		if (score <= badness)
  			continue;
***************
*** 1494,1499 ****
--- 1542,1548 ----
  		result = s;
  		badness = score;
    	}
+ 	restore_flags(flags);
    	return result;
  }
  
***************
*** 1734,1738 ****
--- 1783,1801 ----
  		0, &proc_net_inode_operations,
  		rt_cache_get_info
  	});
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 	proc_net_register(&(struct proc_dir_entry) {
+ 		PROC_NET_WSSOCKLOG, 9, "WSsocklog",
+ 		S_IFREG | S_IRUGO, 1, 0, 0,
+ 		0, &proc_net_inode_operations,
+ 		WSsocklog_get_info
+ 	});
+ 	proc_net_register(&(struct proc_dir_entry) {
+ 		PROC_NET_WSBRIDGELOG, 11, "WSbridgelog",
+ 		S_IFREG | S_IRUGO, 1, 0, 0,
+ 		0, &proc_net_inode_operations,
+ 		WSbridgelog_get_info
+ 	});
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
  #endif		/* CONFIG_PROC_FS */
  }
*** linux-2.0.28/net/ipv4/arp.c	Tue Oct 29 17:42:42 1996
--- KERNEL/net/ipv4/arp.c	Thu Jul 31 12:02:53 1997
***************
*** 1163,1169 ****
  	hash = HASH(sip);
  
  	for (entry=arp_tables[hash]; entry; entry = entry->next)
! 		if (entry->ip == sip && entry->dev == dev)
  			break;
  
  	if (entry)
--- 1163,1169 ----
  	hash = HASH(sip);
  
  	for (entry=arp_tables[hash]; entry; entry = entry->next)
! 		if (entry->ip == sip)
  			break;
  
  	if (entry)
***************
*** 1249,1255 ****
  	struct arp_table *entry;
  
  	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->next)
! 		if (entry->ip == paddr && (!dev || entry->dev == dev))
  			return entry;
  	return NULL;
  }
--- 1249,1257 ----
  	struct arp_table *entry;
  
  	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->next)
! 		/* Bridging code does not like device comparisons. */
! 		/* Add a check for nonzero pa_addr (for external interface). */
! 		if (entry->ip == paddr && (entry->dev && entry->dev->pa_addr))
  			return entry;
  	return NULL;
  }
***************
*** 1391,1396 ****
--- 1393,1421 ----
  		return 0;
  	}
  
+ 	/* Fake ARP for scanned connections. */
+ 	/* Connection setup may have a scanpair with no scandev */
+ 	if (skb && skb->sk && (skb->sk->scandev || skb->sk->scanpair))
+ 	{
+ 		/* Return the dest MAC addr most recently */
+ 		/* used by the peer. */
+ 		int i;
+ 		int gotit=0;
+ 
+ 		for (i=0; i<dev->addr_len; i++)
+ 		{
+ 			if ((haddr[i] = skb->sk->hw_addr[i]) != 0)
+ 				gotit++;
+ 		}
+ 		if (gotit)
+ 		{
+ 			/* valid address: at least 1 nonzero byte */
+ 			skb->arp = 1;
+ 			return 0;
+ 		}
+ 		/* else try a regular ARP lookup */
+ 	}
+ 
  	hash = HASH(paddr);
  	arp_fast_lock();
  
***************
*** 1585,1591 ****
  			hash = HASH(sip);
  
  			for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next)
! 				if (entry1->ip == sip && entry1->dev == dev)
  					break;
  
  			if (!entry1)
--- 1610,1616 ----
  			hash = HASH(sip);
  
  			for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next)
! 				if (entry1->ip == sip)
  					break;
  
  			if (!entry1)
***************
*** 1676,1681 ****
--- 1701,1707 ----
  	struct sk_buff *skb;
  	struct arphdr *arp;
  	unsigned char *arp_ptr;
+ 	extern char *a2b_src_hw(struct device *dev, unsigned char *);
  
  	/*
  	 *	No arp on this interface.
***************
*** 1684,1689 ****
--- 1710,1718 ----
  	if (dev->flags&IFF_NOARP)
  		return;
  
+ 	/* Let bridging code set source hw addr to internal hw addr. */
+ 	src_hw = a2b_src_hw(dev, src_hw);
+ 
  	/*
  	 *	Allocate a buffer
  	 */
***************
*** 2063,2069 ****
  	{
  		/* User supplied arp entries are definitive - RHP 960603 */
  
! 		if (entry->ip == ip && entry->mask == mask && entry->dev == dev) {
  			*entryp=entry->next;
  			arp_free_entry(entry);
  			continue;
--- 2092,2098 ----
  	{
  		/* User supplied arp entries are definitive - RHP 960603 */
  
! 		if (entry->ip == ip && entry->mask == mask) {
  			*entryp=entry->next;
  			arp_free_entry(entry);
  			continue;
*** linux-2.0.28/net/ipv4/devinet.c	Tue May  7 05:22:30 1996
--- KERNEL/net/ipv4/devinet.c	Thu Jul 31 12:02:53 1997
***************
*** 117,129 ****
  	{
  		if ((!(dev->flags & IFF_UP)) || dev->family!=AF_INET)
  			continue;
- 		/*
- 		 *	If the protocol address of the device is 0 this is special
- 		 *	and means we are address hunting (eg bootp).
- 		 */
  		 
- 		if (dev->pa_addr == 0)
- 			return IS_MYADDR;
  		/*
  		 *	Is it the exact IP address? 
  		 */
--- 117,123 ----
*** linux-2.0.28/net/ipv4/ip_input.c	Thu Jun  6 11:22:24 1996
--- KERNEL/net/ipv4/ip_input.c	Thu Jul 31 12:02:53 1997
***************
*** 426,431 ****
--- 426,434 ----
  	 *	function entry.
  	 */
  	daddr = iph->daddr;
+ #if 0
+ /* This stuff is turned off for the initial port. */
+ /* It may be turned back on in another pass. */
  #ifdef CONFIG_IP_TRANSPARENT_PROXY
  	/*
  	 *	ip_chksock adds still more overhead for forwarded traffic...
***************
*** 434,439 ****
--- 437,445 ----
  #else
  	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
  #endif
+ #endif /* 0 */
+ 	if (skb->ipcatch || iph->daddr == skb->dev->pa_addr ||
+ 		(brd = ip_chk_addr(iph->daddr)) != 0)
  	{
  		if (opt && opt->srr) 
  	        {
*** linux-2.0.28/net/ipv4/ip_output.c	Tue Oct 29 17:42:42 1996
--- KERNEL/net/ipv4/ip_output.c	Thu Jul 31 12:02:52 1997
***************
*** 163,169 ****
  	skb_reserve(skb,MAX_HEADER);
  	if (dev->hard_header)
  	{
! 		if (rt && dev == rt->rt_dev && rt->rt_hh)
  		{
  			memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
  			if (rt->rt_hh->hh_uptodate)
--- 163,170 ----
  	skb_reserve(skb,MAX_HEADER);
  	if (dev->hard_header)
  	{
! 		if (rt && dev == rt->rt_dev && rt->rt_hh &&
! 		    (!skb->sk || !skb->sk->scandev))
  		{
  			memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
  			if (rt->rt_hh->hh_uptodate)
*** linux-2.0.28/net/ipv4/proc.c	Mon Jun  3 04:23:42 1996
--- KERNEL/net/ipv4/proc.c	Mon Nov 24 22:52:15 1997
***************
*** 278,280 ****
--- 278,518 ----
  		len = length;
  	return len;
  }
+ 
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ /*
+  * Dump socket log diagnostics to the /proc/net/WSsocklog file.
+  *
+  * I think these routines handle buffer boundaries better than the ones
+  * above.  Where those position the *start pointer somewhere within the
+  * given buffer, this one always puts it at the start of the buffer.
+  *
+  * The pos and offset variables are both relative to the start of the file.
+  * The len and length variables are both relative to the start of the buffer.
+  * The tmplen variable is the size of the current line of data.
+  * If pos<offset, we have offset-pos bytes of old data to skip (olddata).
+  * If pos+tmplen>offset, we have tmplen-olddata bytes of new data.
+  *
+  * Change usedata() to proc_usedata() to be used by WSbridgelog file, too.
+  * Also move proc_usedata() outside of SOCKLOG_ENABLED ifdef.
+  */
+ void proc_usedata(char *buffer, char *tmpbuf, int *pos, int offset, int *len)
+ {
+ 	int olddata = 0;
+ 	int tmplen = strlen(tmpbuf);
+ 
+ 	if (tmplen + *pos > offset) {
+ 		/* We have some new data, */
+ 		if (*pos < offset)
+ 			/* and we have this much old data. */
+ 			olddata = offset - *pos;
+ 		strcpy(buffer+(*len), tmpbuf+olddata);
+ 		(*len) += tmplen - olddata;
+ 	}
+ 	(*pos) += tmplen;
+ }
+ 
+ #define SOCKLOG_ENABLED 1
+ #if SOCKLOG_ENABLED
+ /*
+  * Dump socket log diagnostics to the /proc/net/WSsocklog file.
+  * 40*10000 = 400KB
+  */
+ #define NR_SOCKLOG 10000
+ #define SOCKLOG_LIMIT 30
+ struct socklog_record {
+ 	struct socklog_record *next;
+ 	struct sock *sock;
+ 	unsigned short lport;
+ 	unsigned short rport;
+ 	int action;
+ 	unsigned int jiffies;
+ 	unsigned int seq1;
+ 	unsigned int seq2;
+ 	void *bt1;
+ 	void *bt2;
+ 	void *bt3;
+ };
+ struct socklog_record socklog_records[NR_SOCKLOG];
+ int socklog_next = 0;
+ #define socklog_ptr(sk) ((struct socklog_record *)(sk->keepalive_timer.data))
+ 
+ 
+ int WSsocklog_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+ {
+ 	int len = 0; /* offset from the start of the given buffer */
+ 	int pos = 0; /* offset from the start of the file */
+ 	int i;
+ 	struct sock *sp;
+ 	struct socklog_record *rp;
+ 	char tmpbuf[200]; /* Good enough.  I count around 150. */
+ 	char *actionstr;
+ 	int sockcnt;
+ 	int logcnt;
+ 
+ 	*start = buffer;
+ 
+ 	/*
+ 	 * Count the socklog_records usage.  Cli() is not needed as
+ 	 * this is an informal number.
+ 	 */
+ 	logcnt = 0;
+ 	for (rp = &socklog_records[0]; rp < &socklog_records[NR_SOCKLOG]; rp++){
+ 		if (rp->sock)
+ 			logcnt++;
+ 	}
+ 	sprintf(tmpbuf, "%d of %d records used\n", logcnt, NR_SOCKLOG);
+ 	proc_usedata(buffer, tmpbuf, &pos, offset, &len);
+ 	if (len >= length)
+ 		return length;
+ 
+ 	logcnt = sockcnt = 0;
+ 	for (i = 0; i < SOCK_ARRAY_SIZE; i++) {
+ 	  	cli();
+ 		for (sp = tcp_prot.sock_array[i]; sp != NULL; sp = sp->next) {
+ 			int infcount = 0;
+ 
+ 			sockcnt++;
+ 			for (rp = socklog_ptr(sp); rp; rp = rp->next) {
+ 				/* Break if socklog_record was reclaimed. */
+ 				/* I do not currently plan to do this. */
+ 				/* I will "free" records when I free socks. */
+ 				if (rp->sock != sp)
+ 					break;
+ 				if (++infcount > SOCKLOG_LIMIT)
+ 					break; /* avoid infinite loops */
+ 
+ 				logcnt++;
+ 				/* print socklog information */
+ 				switch (rp->action) {
+ 				case 1: actionstr="SYN_RECV"; break;
+ 				case 2: actionstr="SYN_SEND"; break;
+ /* ? */				case 3: actionstr="ESTABLISHED"; break;
+ 				case 4: actionstr="FIN"; break;
+ 				case 5: actionstr="CLOSE"; break;
+ 				case 6: actionstr="URG data"; break;
+ 				case 7: actionstr="DONTCARE1"; break;
+ 				case 8: actionstr="DONTCARE2"; break;
+ 				case 9: actionstr="RESET1"; break;
+ 				case 10: actionstr="TIME DONE"; break;
+ 				case 11: actionstr="setstate"; break;
+ 				case 12: actionstr="CLOSE2"; break;
+ 				case 13: actionstr="destroy_sock"; break;
+ 				case 14: actionstr="rexmit timer"; break;
+ 				case 15: actionstr="dontcare cleanup"; break;
+ 				case 16: actionstr="send_ack, deleting timer"; break;
+ 				case 17: actionstr="cleanup dead, state"; break;
+ 				case 18: actionstr="dontcare cleanup 2"; break;
+ 				default: actionstr="unknown"; break;
+ 				}
+ 				sprintf(tmpbuf, "%p(%u,%u): %s at %u,"
+ 					" seqs %u & %u from %p, %p, %p\n",
+ 					rp->sock, rp->lport, rp->rport,
+ 					actionstr, rp->jiffies, rp->seq1,
+ 					rp->seq2, rp->bt1, rp->bt2, rp->bt3);
+ 				proc_usedata(buffer, tmpbuf, &pos, offset, &len);
+ 				if (len > length)
+ 					break;
+ 			}
+ 			if (len > length)
+ 				break;
+ 		}
+ 		sti();
+ 		if (len > length)
+ 			break;
+ 	}
+ 	if (len < length) {
+ 		sprintf(tmpbuf, "\nCounted %d log records for the %d socks"
+ 			" in the proto table.\n", logcnt, sockcnt);
+ 		proc_usedata(buffer, tmpbuf, &pos, offset, &len);
+ 	}
+ 	if (len > length)
+ 		len = length;
+ 	return len;
+ }
+ 
+ void log_sockevent(struct sock *sp, int action, unsigned long seq1, unsigned long seq2)
+ {
+ 	struct socklog_record *rp, *limit;
+ 	unsigned long flags;
+ 	int count = 0;
+ 
+ 	save_flags(flags);
+ 	cli();
+ 	for (rp = socklog_ptr(sp); rp; rp = rp->next) {
+ 		if (++count >= SOCKLOG_LIMIT) {
+ 			restore_flags(flags);
+ 			return;
+ 		}
+ 	}
+ 
+ 	limit = &socklog_records[NR_SOCKLOG - 1];
+ 	for (rp = &socklog_records[socklog_next]; rp < limit; rp++) {
+ 		if (!rp->sock)
+ 			break;
+ 	}
+ 	if (rp == limit && socklog_next > 0) {
+ 		limit = &socklog_records[socklog_next];
+ 		for (rp = &socklog_records[0]; rp < limit; rp++) {
+ 			if (!rp->sock)
+ 				break;
+ 		}
+ 	}
+ 	if (rp == limit) {
+ 		restore_flags(flags);
+ 		return; /* argh, no free socklog_records */
+ 	}
+ 
+ 	/* Found a free one.  Add it to this sock's list */
+ 	rp->next = socklog_ptr(sp);
+ 	sp->keepalive_timer.data = (unsigned long) rp;
+ 
+ 	rp->sock = sp;
+ 	rp->lport = ntohs(sp->dummy_th.source);
+ 	rp->rport = ntohs(sp->dummy_th.dest);
+ 	rp->action = action;
+ 	rp->jiffies = jiffies;
+ 	rp->seq1 = seq1;
+ 	rp->seq2 = seq2;
+ 	rp->bt1 = __builtin_return_address(1);
+ 	rp->bt2 = __builtin_return_address(2);
+ 	rp->bt3 = __builtin_return_address(3);
+ 	restore_flags(flags);
+ }
+ 
+ void free_sockevents(struct sock *sp)
+ {
+ 	struct socklog_record *rp;
+ 	unsigned long flags;
+ 	int count = 0;
+ 
+ 	save_flags(flags);
+ 	cli();
+ 	for (rp = socklog_ptr(sp); rp; rp = rp->next) {
+ 		if (rp->sock != sp)
+ 			break;
+ 		if (++count >= SOCKLOG_LIMIT)
+ 			break;
+ 		rp->sock = 0;
+ 	}
+ 	restore_flags(flags);
+ }
+ #else
+ 
+ int WSsocklog_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+ {
+ 	return 0;
+ }
+ 
+ void log_sockevent(struct sock *sp, int action, unsigned long seq1, unsigned long seq2)
+ {
+ 	return;
+ }
+ 
+ void free_sockevents(struct sock *sp)
+ {
+ 	return;
+ }
+ 
+ #endif /* SOCKLOG_ENABLED */
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
*** linux-2.0.28/net/ipv4/tcp.c	Tue Nov 26 23:44:11 1996
--- KERNEL/net/ipv4/tcp.c	Mon Nov 24 22:52:47 1997
***************
*** 435,444 ****
  
  #include <asm/segment.h>
  
  unsigned long seq_offset;
  struct tcp_mib	tcp_statistics;
  
! static void tcp_close(struct sock *sk, unsigned long timeout);
  
  /*
   *	Find someone to 'accept'. Must be called with
--- 435,448 ----
  
  #include <asm/segment.h>
  
+ extern struct sock *TCP_scansock;
+ void remove_sock(struct sock *sk);
+ static int tcp_close_state(struct sock *sk, int dead);
+ 
  unsigned long seq_offset;
  struct tcp_mib	tcp_statistics;
  
! void tcp_close(struct sock *sk, unsigned long timeout);
  
  /*
   *	Find someone to 'accept'. Must be called with
***************
*** 453,459 ****
  	do
  	{
  		if(p->sk->state == TCP_ESTABLISHED || p->sk->state >= TCP_FIN_WAIT1)
! 			return p;
  		p=p->next;
  	}
  	while(p!=(struct sk_buff *)&s->receive_queue);
--- 457,470 ----
  	do
  	{
  		if(p->sk->state == TCP_ESTABLISHED || p->sk->state >= TCP_FIN_WAIT1)
! 		{
! 			/* Scanned socks: both socks in the pair must be */
! 			/* at or beyond ESTABLISHED before being accepted. */
! 			struct sock *sk2 = p->sk->scanpair;
! 			if(!sk2 || (sk2->state != TCP_SYN_RECV &&
! 					sk2->state != TCP_SYN_SENT))
! 				return p;
! 		}
  		p=p->next;
  	}
  	while(p!=(struct sk_buff *)&s->receive_queue);
***************
*** 493,498 ****
--- 504,535 ----
  
  
  /*
+  * Part of the general protection scheme is to make sure we are not
+  * processing a packet on a sock that can be destroyed by the TIME_DONE
+  * timer.  This means we must clear_backlog() before we
+  * tcp_set_state(TCP_CLOSE).  Do not remove_sock() because now get_sock()
+  * will skip TCP socks in TCP_CLOSE state.  This reduces races between
+  * the backlog and the TIME_DONE timer and lets the sock remain visible
+  * to netstat and WSsocklog until really destroyed.
+  *
+  * This pre-TCP_CLOSE routine is now called in the numerous places
+  * we were not correctly protecting ourselves.
+  */
+ void tcp_preclose(struct sock *sk)
+ {
+ 	struct sock *sk2 = sk->scanpair;
+ 	void clear_backlog(struct sock *sk);
+ 
+ 
+ 	sk->scanpair = NULL;
+ 	if (sk2)
+ 		sk2->scanpair = NULL;
+ 
+ 	clear_backlog(sk);
+ }
+ 
+ 
+ /*
   * This routine is called by the ICMP module when it gets some
   * sort of error condition.  If err < 0 then the socket should
   * be closed and the error returned to the user.  If err > 0
***************
*** 585,590 ****
--- 622,628 ----
  			if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
  			{
  				tcp_statistics.TcpAttemptFails++;
+ 				tcp_preclose(sk);
  				tcp_set_state(sk,TCP_CLOSE);
  				sk->error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */
  			}
***************
*** 743,748 ****
--- 781,1150 ----
  	return 0;
  }
  
+ /*
+  * The application requests file descriptor access to
+  * the other sock in a scanned pair.
+  */
+ int tcp_scan_accept(struct sock *sk)
+ {
+ 	struct socket *sock, *newsock;
+ 	struct sock *sk2;
+ 	extern int get_fd(struct inode *);
+ 	int fd;
+ 
+ 	lock_sock(sk);
+ 	/* This line mirrors tcp_accept(), which finds an acceptable sock. */
+ 	sk2 = sk->scanpair;
+ 	if (!sk2 || sk2->scanaccepted) {
+ 		release_sock(sk);
+ 		return(-EINVAL);
+ 	}
+ 
+ 	/* Lock sk2 incase we give up CPU in sock_alloc or below. */
+ 	lock_sock(sk2);
+ 
+ 	if (!(newsock = sock_alloc())) 
+ 	{
+ 		printk("NET: tcp_scan_accept: no more sockets\n");
+ 		release_sock(sk);
+ 		release_sock(sk2);
+ 		return(-ENOSR);	/* Was: EAGAIN, but we are out of system
+ 				   resources! */
+ 	}
+ 	sock = sk->socket;
+ 	newsock->type = sock->type;
+ 	newsock->ops = sock->ops;
+ 
+ 	/* mirror inet_accept after its call to tcp_accept */
+ 	newsock->data = (void *)sk2;
+ 	sk2->sleep = newsock->wait;
+ 	sk2->socket = newsock;
+ 	newsock->conn = NULL; /* AF_UNIX and FIFOs only? */
+ 	newsock->state = SS_CONNECTED;
+ 
+ 	/* done with inet_accept, now mimic sys_accept */
+ 	if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
+ 	{
+ 		sock_release(newsock);
+ 		release_sock(sk);
+ 		release_sock(sk2);
+ 		return(-EINVAL);
+ 	}
+ 	sock->file=current->files->fd[fd];
+ 
+ 	sk2->scanaccepted = 1;
+ 	release_sock(sk);
+ 	release_sock(sk2);
+ 	/* this gets returned all the way back up to the user */
+ 	return(fd);
+ }
+ 
+ /*
+  * Bridge backlogged packets on a sock cleaned by tcp_dontcare_cleanup().
+  *
+  * If called during the tcp_scan_dontcare() process, this prevents the
+  * subsequent release_sock() from accessing the backlog of a sock that
+  * will be destroyed by its TIME_DONE timer.  The sock lock sk->users
+  * does not protect the sock at that point because release_sock()
+  * releases the lock before processing the backlog.
+  *
+  * If called while processing a sock backlog (either the last ACK has
+  * arrived or a reset forced us to clean up), then this again prevents
+  * us from continuing the backlog processing.
+  *
+  * While we should not even have a backlog while processing a newly
+  * arriving packet, we are still safe in bridging the backlog.
+  *
+  * Below, clear_backlog() drops packets in the backlog.  Sometimes
+  * we do not want to bridge them.
+  */
+ void bridge_backlog(struct sock *sk)
+ {
+ 	extern bridge_xmit(struct sk_buff *skb, struct device *dev, int pri);
+ 	struct sk_buff *skb;
+ 
+   	while((skb = skb_dequeue(&sk->back_log)) != NULL) {
+ 		skb->sk = NULL;
+ 		bridge_xmit(skb, skb->dev, 0);
+ 	}
+ }
+ 
+ void clear_backlog(struct sock *sk)
+ {
+ 	struct sk_buff *skb;
+ 
+   	while((skb = skb_dequeue(&sk->back_log)) != NULL) {
+ 		skb->sk = NULL;
+ 		kfree_skb(skb, FREE_READ);
+ 	}
+ }
+ 
+ /*
+  * Cleanup dontcare socks only when both in the pair have no more
+  * unacked data.  Then get rid of them together.
+  * Added force flag for tcp_reset() to force closure of dontcare socks.
+  */
+ int tcp_dontcare_cleanup(struct sock *sk, int force)
+ {
+ 	log_sockevent(sk, 15, (int)sk->retransmit_timer.next,
+ 		(int)sk->send_head + (int)skb_peek(&sk->write_queue));
+ 	log_sockevent(sk, 17, (int)sk->dead, (int)sk->state);
+ 	/* We should not get here now that tcp_close() does not call this. */
+ 	if(sk->dontcare>1) {
+ 		printk("skip second cleanup on sk %p from %p, %p, %p\n", sk,
+ 			__builtin_return_address(0),
+ 			__builtin_return_address(1),
+ 			__builtin_return_address(2));
+ 		return 0;
+ 	}
+ 
+ 	if((force == 1) || (!sk->send_head && skb_peek(&sk->write_queue) == NULL))
+ 	{
+ 		struct sock *sk2 = sk->scanpair;
+ 		if (!sk2) {
+ 			printk("dontcare cleanup with sk2=NULL\n");
+ 		} else {
+ 			log_sockevent(sk2, 18, (int)sk->dead, (int)sk->dontcare);
+ 		}
+ 		if((sk2 && (sk2->send_head || skb_peek(&sk2->write_queue))) &&
+ 			!force)
+ 			return 0;
+ 
+ 		/* no more acks are needed, so dont accept any more packets */
+ 		remove_sock(sk);
+ 		if(sk2)
+ 			remove_sock(sk2);
+ 		sk->dontcare++; /* so we only get in here once */
+ 		sk->scanpair = NULL;
+ 		bridge_backlog(sk);
+ 		tcp_set_state(sk, TCP_CLOSE);
+ 
+ 		if(sk2 && sk2->dontcare == 1) {
+ 			sk2->dontcare++;
+ 			sk2->scanpair = NULL;
+ 			bridge_backlog(sk2);
+ 			tcp_set_state(sk2, TCP_CLOSE);
+ 		}
+ 		return 1;
+ 	}
+ 	return 0;
+ }
+ 
+ /*
+  * For SIOCSDONTCARE, transfer received packets from one socket
+  * to the write side of the other.
+  */
+ void tcp_dontcare_transfer(struct sock *sk, struct sock *sk2)
+ {
+ 	struct sk_buff *skb;
+ 	int finflag = 0;
+ 
+ 	/* first, flush anything the application has written */
+ 	tcp_send_partial(sk2);
+ 
+ 	/*
+ 	 * This routine does not account for partially read packets.
+ 	 * As it is required (see comments in tcp_scan_dontcare()) for
+ 	 * the application to have previously written any read data,
+ 	 * we will simply be rewriting some of it.
+ 	 * It is OK to retransmit some old data.
+ 	 */
+ 	while((skb=skb_dequeue(&sk->receive_queue)) != NULL)
+ 	{
+ 		if(!before(skb->seq, sk->acked_seq+1))
+ 		{
+ 			/* drop discontiguous (or just unacked) data */
+ 			/* sock_rfree() takes care of counters */
+ 			kfree_skb(skb, FREE_READ);
+ 			continue;
+ 		}
+ 		/* memory allocation accounting */
+ 		atomic_sub(skb->truesize, &sk->rmem_alloc);
+ 		atomic_add(skb->truesize, &sk2->wmem_alloc);
+ 
+ 		/*
+ 		 * Send it over to the other side.
+ 		 *
+ 		 * On the receive_queue, skb->len is the length of TCP data
+ 		 * (not including MAC, IP and TCP headers), and skb->data
+ 		 * points to the TCP data.  Fix these up.
+ 		 * See tcp_data() and tcp_read() for details.
+ 		 * skb->seq is the host formatted sequence number.
+ 		 * Update write_seq so that sent_seq gets updated, else
+ 		 * tcp_ack() will act funny.
+ 		 */
+ 		sk2->write_seq = skb->seq + skb->len;
+ 		skb->csum = csum_partial(skb->data, skb->len, 0);
+ 		skb_push(skb, skb->data-skb->mac.raw);
+ 		if(skb->h.th->fin) {
+ 			finflag = 1;
+ 			sk2->write_seq++;
+ 		}
+ 		skb->sk = sk2;
+ 		skb->free = 0;
+ 		skb->arp = 1;
+ 		skb->dev = sk2->scandev;
+ 		tcp_send_skb(sk2, skb);
+ 	}
+ 	if(sk->state == TCP_CLOSE_WAIT) {
+ 		if(tcp_close_state(sk2, 0) == 0) {
+ 			/* maybe already sent a fin?  should not get here. */
+ 			printk("dontcare_transfer: failed to fin in state %d\n",
+ 				sk2->state);
+ 		} else if(!finflag) {
+ 			tcp_send_fin(sk2);
+ 		}
+ 	}
+ }
+ 
+ /*
+  * The application no longer wishes to scan this connection.
+  * Transfer the receive buffers from each side to the write
+  * buffer on the other side.  Doing this ioctl on either socket
+  * causes both to short circuit.
+  *
+  * This requires the scanning application to have written to each
+  * side exactly as much as was read from the other side.  Writing
+  * too little, the receiver will only send ACK sequences less than
+  * the bottom of the sender's retransmit buffer because the sender
+  * discards what we at one time already ACKed.  This is a hung
+  * connection.  Writing more than what was read is less catastrophic.
+  * In this case, some of the real data gets lost but at least there
+  * is no hung connection.
+  *
+  * Therefore, EADDRNOTAVAIL is returned if the write sequence on
+  * either side is not equal to the read sequence on the other side.
+  */
+ int tcp_scan_dontcare(struct sock *sk)
+ {
+ 	struct sock *sk2;
+ 	int copied, copied2;
+ 
+ 	lock_sock(sk);
+ 	if (sk->dontcare) {
+ 		printk("skip second scan_dontcare\n");
+ 		release_sock(sk);
+ 		return(0); /* so old apps do not fail, should be EALREADY */
+ 	}
+ 	sk2 = sk->scanpair;
+ 	if (!sk2) {
+ 		release_sock(sk);
+ 		return(-EINVAL);
+ 	}
+ 	lock_sock(sk2);
+ 	/* from this point on, tcp_rcv() back_logs incoming packets */
+ 
+ 	/*
+ 	 * Not only must the amount read equal the amount written, but
+ 	 * I must account for FINs.  We must not have sent any FINs
+ 	 * (as that indicates we closed something), but we may have
+ 	 * received and ACKed FINs.  On receiving a FIN, go to CLOSE_WAIT
+ 	 * state.  On reading the FIN, increment the copied_seq and
+ 	 * mark the FIN packet as "used" (and possibly delete it).
+ 	 */
+ 	if((sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) ||
+ 	   (sk2->state != TCP_ESTABLISHED && sk2->state != TCP_CLOSE_WAIT)) {
+ 		release_sock(sk);
+ 		release_sock(sk2);
+ 		return(-EINVAL);
+ 	}
+ 	log_sockevent(sk, 7, sk->write_seq, sk->copied_seq);
+ 	log_sockevent(sk2, 8, sk2->write_seq, sk2->copied_seq);
+ 
+ 	/*
+ 	 * When calculating the amount of data read on each side, account
+ 	 * for consumed FINs.  Tcp_recvmsg() increments copied_seq and
+ 	 * sets finflag when a FIN is consumed.  In this way, the
+ 	 * application accidentally consumes an extra octet without
+ 	 * writing it.
+ 	 */
+ 	copied = sk->copied_seq;
+ 	if(sk->state == TCP_CLOSE_WAIT) {
+ 		if(sk->finflag)
+ 			copied--;
+ 	}
+ 	copied2 = sk2->copied_seq;
+ 	if(sk2->state == TCP_CLOSE_WAIT) {
+ 		if(sk2->finflag)
+ 			copied2--;
+ 	}
+ 
+ 	if(sk->write_seq != copied2)
+ 	{
+ 		printk("tcp_scan_dontcare: sk1 fails with write_seq %u != copied_seq %u\n",
+ 			(uint) sk->write_seq, (uint) copied2);
+ 		if (sk->write_seq == copied2 - 1) {
+ 			/* Diagnostics: maybe I lost a FIN count somewhere? */
+ 			struct sk_buff *skb = skb_peek(&sk2->receive_queue);
+ 			printk("  sk2: state=%d, skb_peek()=%p\n",
+ 				sk2->state, skb);
+ 			printk("    copied_seq=%u, list->prev=%p\n",
+ 				sk2->copied_seq, sk2->receive_queue.prev);
+ /* maybe I should print copied_seq and receive_queue.prev? */
+ 			if (skb) {
+ 				printk("    skb: seq=%u, used=%d, fin=%d\n",
+ 					skb->seq, skb->used, skb->h.th->fin);
+ 				printk("    skb: source=%d, dest=%d\n",
+ 					ntohs(skb->h.th->source), ntohs(skb->h.th->dest));
+ 			}
+ 		}
+ 		release_sock(sk);
+ 		release_sock(sk2);
+ 		return(-EADDRNOTAVAIL);
+ 	}
+ 
+ 	if(sk2->write_seq != copied)
+ 	{
+ 		printk("tcp_scan_dontcare: sk2 fails with write_seq %u != copied_seq %u\n",
+ 			(uint) sk2->write_seq, (uint) copied);
+ 		if (sk2->write_seq == copied - 1) {
+ 			/* Diagnostics: maybe I lost a FIN count somewhere? */
+ 			struct sk_buff *skb = skb_peek(&sk->receive_queue);
+ 			printk("  sk: state=%d, skb_peek()=%p\n",
+ 				sk->state, skb);
+ 			printk("  copied_seq=%u, list->prev=%p\n",
+ 				sk->copied_seq, sk->receive_queue.prev);
+ 			if (skb) {
+ 				printk("    skb: seq=%u, used=%d, fin=%d\n",
+ 					skb->seq, skb->used, skb->h.th->fin);
+ 				printk("    skb: source=%d, dest=%d\n",
+ 					ntohs(skb->h.th->source), ntohs(skb->h.th->dest));
+ 			}
+ 		}
+ 		release_sock(sk);
+ 		release_sock(sk2);
+ 		return(-EADDRNOTAVAIL);
+ 	}
+ 
+ 	sk->dontcare = sk2->dontcare = 1;
+ 
+ 	tcp_dontcare_transfer(sk, sk2);
+ 	tcp_dontcare_transfer(sk2, sk);
+ 	/* Also flush delayed acks to prevent unnecessary retransmission. */
+ 	if(sk2->ack_backlog)
+ 		tcp_send_ack(sk2);
+ 	if(sk->ack_backlog)
+ 		tcp_send_ack(sk);
+ 
+ 	/*
+ 	 * Release_sock() causes us to bridge whatever is on our back_log.
+ 	 * It is OK to transmit these out of order...  The sender handles
+ 	 * any necessary retransmissions.
+ 	 *
+ 	 * Do the release_sock() last.  If tcp_dontcare_cleanup() cleans
+ 	 * up the socks, the TIME_DONE timer still prevents us from
+ 	 * destroying them (at least this one) because we still have the
+ 	 * sock locked.  And tcp_dontcare_cleanup() is modified to bridge
+ 	 * out any backlogged packets on socks it cleans up because we do
+ 	 * not want to continue processing backlogs of socks that can be
+ 	 * destroyed by TIME_DONE at any time.
+ 	 */
+ 	tcp_dontcare_cleanup(sk, 0);	/* works on both socks */
+ 	release_sock(sk2);
+ 	release_sock(sk);
+ 	return 0;
+ }
+ 
  int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
  {
  	int err;
***************
*** 790,795 ****
--- 1192,1229 ----
  			put_user(amount, (int *)arg);
  			return(0);
  		}
+ 		case SIOCSLISTEN: /* set the global listening socket */
+ 		{
+ 			if (sk->state == TCP_LISTEN) {
+ 				/* turn on TCP scanning */
+ 				if (TCP_scansock)
+ 					return(-EADDRINUSE);
+ 				/* This becomes the global TCP scanner. */
+ 				sk->max_ack_backlog = 250;
+ 				TCP_scansock = sk;
+ 			} else {
+ 				/* turn off TCP scanning */
+ 				if (!TCP_scansock)
+ 					return(-EINVAL);
+ 				TCP_scansock = NULL;
+ 			}
+ 			return(0);
+ 		}
+ 		case SIOCSCANPAIR: /* accept the server sock of the pair */
+ 		{
+ 			return(tcp_scan_accept(sk));
+ 		}
+ 		case SIOCSDONTCARE: /* do not care about this connection */
+ 		{
+ 			return(tcp_scan_dontcare(sk));
+ 		}
+ 		case SIOCSINTERNAL: /* connection initiated from inside */
+ 		{
+ 			if (!sk->scandev)
+ 				return(-EINVAL);
+ 			/* return 1 if started from the internal interface */
+ 			return( (sk->scandev->internal) ? 1 : 0);
+ 		}
  		default:
  			return(-EINVAL);
  	}
***************
*** 1587,1592 ****
--- 2021,2027 ----
  
  		skb->used = 1;
  		sk->shutdown |= RCV_SHUTDOWN;
+ 		sk->finflag = 1;
  		break;
  
  	}
***************
*** 1619,1625 ****
   *	closed.
   */
  
! static int tcp_close_state(struct sock *sk, int dead)
  {
  	int ns=TCP_CLOSE;
  	int send_fin=0;
--- 2054,2060 ----
   *	closed.
   */
  
! int tcp_close_state(struct sock *sk, int dead)
  {
  	int ns=TCP_CLOSE;
  	int send_fin=0;
***************
*** 1646,1651 ****
--- 2081,2088 ----
  			send_fin=1;
  	}
  
+ 	if (ns == TCP_CLOSE)
+ 		tcp_preclose(sk);
  	tcp_set_state(sk,ns);
  
  	/*
***************
*** 1742,1748 ****
  }
  
  
! static void tcp_close(struct sock *sk, unsigned long timeout)
  {
  	struct sk_buff *skb;
  
--- 2179,2185 ----
  }
  
  
! void tcp_close(struct sock *sk, unsigned long timeout)
  {
  	struct sk_buff *skb;
  
***************
*** 1752,1762 ****
--- 2189,2241 ----
  	 */
  
  	lock_sock(sk);
+ 	log_sockevent(sk, 5, sk->write_seq, sk->copied_seq);
  
  	tcp_cache_zap();
+ 
+ 	if(TCP_scansock && sk == TCP_scansock) {
+ 		TCP_scansock = NULL;
+ 	}
+ 
+ 	/*
+ 	 * Dontcare socks do not want tcp_close to change the state.
+ 	 * tcp_close() should deaden it, dontcare_cleanup() should put
+ 	 * it in TCP_CLOSE state and put it on the TIME_DONE timer.
+ 	 * TIME_DONE will not destroy the sock until it is dead and closed.
+ 	 */
+ 	if(sk->dontcare)
+ 	{
+ 		/* Do the release_sock() before marking dead because we
+ 		 * do not want TIME_DONE to fire while processing the backlog.
+ 		 */
+ 		release_sock(sk);
+ 		sk->dead = 1;
+ 		return;
+ 	}
+ 
+ 	if(sk->scanpair) {
+ 		struct sock *sk2 = sk->scanpair;
+ 
+ 		sk->scanpair = 0;
+ 		sk2->scanpair = 0;
+ 		if(sk->scanclient && !sk2->scanaccepted) {
+ 			/* Our unaccepted partner would be lost without us */
+ 			/* This mimics the "close a listening sock" code */
+ 			tcp_close(sk2, 0);
+ 			if (!sk2->dead) {
+ 				printk("tcp_close: sk2 not marked dead\n");
+ 				sk2->dead = 1;
+ 			}
+ 		} else if(!sk->scanclient && !sk->scanaccepted) {
+ 			printk("tcp_close: closing unaccepted sock\n");
+ 			sk->dead = 1;
+ 		}
+ 	}
+ 
  	if(sk->state == TCP_LISTEN)
  	{
  		/* Special case */
+ 		tcp_preclose(sk);
  		tcp_set_state(sk, TCP_CLOSE);
  		tcp_close_pending(sk);
  		release_sock(sk);
***************
*** 2173,2181 ****
  }
  
  
  struct proto tcp_prot = {
  	tcp_close,
! 	ip_build_header,
  	tcp_connect,
  	tcp_accept,
  	ip_queue_xmit,
--- 2652,2753 ----
  }
  
  
+ #if 0
+ /*
+  * There are two sets of rules involved with packet data: incoming, outgoing.
+  * 
+  * Incoming has skb->mac.ethernet pointing to the MAC header and skb->data
+  * pointing to TCP data (as pulled up in tcp_rcv()).  It also has skb->h.raw
+  * (including iph and th) pointing to the TCP header (as set by ip_rcv()).
+  * In fact, that is how far it knows to pullup to get to TCP data, h.raw-data.
+  * And it has skb->ip_hdr pointing to the IP header.
+  * skb->head (the true start of skb) should match skb->mac.raw.
+  * 
+  * Outgoing has skb->data pointing to the MAC header (as pushed by
+  * eth_header()).  It does not have skb->mac.raw or skb->h.raw set.
+  * Also, skb->data is not likely to be skb->head since ip_build_header()
+  * calls ip_send_room() which reserves MAX_HEADER (32) before pushing just
+  * enough to hold the header (14) (alternatively, eth_header() if
+  * ip_build_header() finds no route).
+  * 
+  * This routine wants to find the MAC header since all the rest is contiguous.
+  * It prints a TCP packet either arriving or being sent.
+  * This is actually pretty handy.
+  */
+ void p_pkt(struct sk_buff *skb, struct device *dev, int outgoing)
+ {
+ 	unsigned char *data;
+ 	unsigned char *p1, *p2;
+ 	unsigned short *sp;
+ 	int i;
+ 
+ 	if (dev) {
+ 		p1 = dev->dev_addr;
+ 		printk("%s device %s, %02x:%02x:%02x:%02x:%02x:%02x\n",
+ 			(outgoing) ? "out to" : "in from", dev->name,
+ 			p1[0], p1[1], p1[2], p1[3], p1[4], p1[5]);
+ 	} else {
+ 		printk("%s some device\n", (outgoing) ? "out to" : "in from");
+ 	}
+ 	data = (outgoing) ? skb->data : skb->mac.raw;
+ 	p1 = &data[6]; /* eth->h_source */
+ 	p2 = &data[0]; /* eth->h_dest */
+ 	printk(
+ 	"  %02x:%02x:%02x:%02x:%02x:%02x to %02x:%02x:%02x:%02x:%02x:%02x\n",
+ 		p1[0], p1[1], p1[2], p1[3], p1[4], p1[5],
+ 		p2[0], p2[1], p2[2], p2[3], p2[4], p2[5]);
+ 
+ 	sp = (unsigned short *)(data + dev->hard_header_len);
+ 	for(i=0; i<48; i+=16) {
+ 		printk("  %04x %04x  %04x %04x  %04x %04x  %04x %04x\n",
+ 			ntohs(sp[0]), ntohs(sp[1]), ntohs(sp[2]), ntohs(sp[3]),
+ 			ntohs(sp[4]), ntohs(sp[5]), ntohs(sp[6]), ntohs(sp[7]));
+ 		sp += 8;
+ 	}
+ }
+ 
+ /* my_ip_queue_xmit() is substituted for ip_queue_xmit() below. */
+ void my_ip_queue_xmit(struct sock *sk, struct device *dev,
+ 			struct sk_buff *skb, int free)
+ {
+ 	if(sk && sk->scanpair) p_pkt(skb, dev, 1);
+ 	ip_queue_xmit(sk, dev, skb, free);
+ }
+ /* usually, the #if 0 is placed above p_pkt(). */
+ #endif 0
+ 
+ /*
+  * Replace ip_build_header() in the tcp_proto proto structure so that
+  * we do not rely on standard routing.  Set *dev explicitly.
+  * I see that in all cases but one (tcp_reset), the skb->sk field is
+  * set before calling prot->build_header().  This lets me identify
+  * which requests can be handled here.
+  */
+ int my_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
+ 		struct device **dev, int type, struct options *opt,
+ 		int len, int tos, int ttl, struct rtable **rp)
+ {
+ 	if(skb->sk && skb->sk->scandev) {
+ 		*dev = skb->sk->scandev;
+ 		/*
+ 		 * Identify the device not to forward the packet on.
+ 		 * I cannot simply use skb->sk->scanpair->scandev because
+ 		 * connection termination causes scanpairs to go away.
+ 		 * This can lead to duplicate FIN packets on a LAN.
+ 		 * I also cannot simply use skb->dev because it gets
+ 		 * overwritten as the outgoing interface on its way down.
+ 		 * The sk->indev field is a kludge, but it should work.
+ 		 */
+ 		skb->indev = skb->sk->indev;
+ 	}
+ 	return(ip_build_header(skb, saddr, daddr,
+ 		dev, type, opt, len, tos, ttl, rp));
+ }
+ 
+ 
  struct proto tcp_prot = {
  	tcp_close,
! 	my_build_header,
  	tcp_connect,
  	tcp_accept,
  	ip_queue_xmit,
*** linux-2.0.28/net/ipv4/tcp_input.c	Sat Nov 30 02:51:03 1996
--- KERNEL/net/ipv4/tcp_input.c	Mon Nov 24 22:52:31 1997
***************
*** 37,42 ****
--- 37,54 ----
  #include <net/tcp.h>
  
  /*
+  *	global TCP scanning socket
+  */
+ struct sock *TCP_scansock = NULL;
+ void handle_ack_synack(struct sock *sk, struct sk_buff *skb, struct tcphdr *th);
+ extern bridge_xmit(struct sk_buff *skb, struct device *dev, int pri);
+ extern void tcp_reset_xmit_timer(struct sock *sk, int why, unsigned long when);
+ void make_syn_pkt(struct sock *sk, struct sk_buff *skb, struct tcphdr *th);
+ int tcp_dontcare_cleanup(struct sock *sk, int force);
+ void handle_dupsynack(struct sock *sk, struct sk_buff *skb, struct tcphdr *th);
+ extern void tcp_preclose(struct sock *sk);
+ 
+ /*
   *	Policy code extracted so it's now separate
   */
  
***************
*** 188,194 ****
   */
   
  static void bad_tcp_sequence(struct sock *sk, struct tcphdr *th, u32 end_seq,
! 	      struct device *dev)
  {
  	if (th->rst)
  		return;
--- 200,206 ----
   */
   
  static void bad_tcp_sequence(struct sock *sk, struct tcphdr *th, u32 end_seq,
! 	      struct device *dev, struct sk_buff *skb)
  {
  	if (th->rst)
  		return;
***************
*** 202,208 ****
    	 
  	if (sk->state==TCP_SYN_SENT || sk->state==TCP_SYN_RECV) 
  	{
! 		tcp_send_reset(sk->saddr,sk->daddr,th,sk->prot,NULL,dev,0,255);
  		return;
  	}
  
--- 214,221 ----
    	 
  	if (sk->state==TCP_SYN_SENT || sk->state==TCP_SYN_RECV) 
  	{
! 		tcp_send_reset(sk->saddr, sk->daddr, th, sk->prot, NULL,
! 				dev, 0, 255, skb);
  		return;
  	}
  
***************
*** 231,240 ****
  extern __inline__ int tcp_sequence(struct sock *sk, u32 seq, u32 end_seq)
  {
  	u32 end_window = sk->lastwin_seq + sk->window;
! 	return	/* if start is at end of window, end must be too (zero window) */
! 		(seq == end_window && seq == end_seq) ||
! 		/* if start is before end of window, check for interest */
! 		(before(seq, end_window) && !before(end_seq, sk->acked_seq));
  }
  
  /*
--- 244,262 ----
  extern __inline__ int tcp_sequence(struct sock *sk, u32 seq, u32 end_seq)
  {
  	u32 end_window = sk->lastwin_seq + sk->window;
! 
! 	if (seq == end_window && seq == end_seq)
! 		return 1;
! 	if (!before(seq, end_window))
! 		return 0;
! 	if (!before(end_seq, sk->acked_seq))
! 		return 1;
! 	return 2; /* possible duplicate SYNACK */
! 
! /* Had to rework this logic and modify the return value:
!  *	return	((seq == end_window && seq == end_seq) ||
!  *		(before(seq, end_window) && !before(end_seq, sk->acked_seq)));
!  */
  }
  
  /*
***************
*** 244,249 ****
--- 266,272 ----
  
  static int tcp_reset(struct sock *sk, struct sk_buff *skb)
  {
+ 	log_sockevent(sk, 9, sk->write_seq, sk->copied_seq);
  	sk->zapped = 1;
  	/*
  	 *	We want the right error as BSD sees it (and indeed as we do).
***************
*** 260,265 ****
--- 283,350 ----
  	default:
  		sk->err = ECONNRESET;
  	}
+ 
+ 	if (sk->scanpair) {
+ 		struct sock *sk2 = sk->scanpair;
+ 		struct sock *sks = (sk->scanclient) ? sk2 : sk; /* server */
+ 		struct sock *skc = (sk->scanclient) ? sk : sk2; /* client */
+ 
+ 		/* If server still unaccepted, share the reset. */
+ 		if(!sks->scanaccepted) {
+ 			/*
+ 			 * The server side was never scan_accepted, and it
+ 			 * is too late now.
+ 			 * Mark the unreferenced server side dead and
+ 			 * schedule it for destruction.  Mark the client
+ 			 * closed appropriately.
+ 			 * Disconnect the RST from the sock and bridge it.
+ 			 * But first, bridge the reset.
+ 			 * This block also needs to disconnect the scanpair
+ 			 * peers from each other, but the dontcare block
+ 			 * below does not (otherwise I would do it in
+ 			 * tcp_dontcare_cleanup, wouldn't I?).
+ 			 */
+ 			atomic_sub(skb->truesize, &sk->rmem_alloc);
+ 			skb->sk = NULL;
+ 			bridge_xmit(skb, skb->dev, 0);
+ 
+ 			sk2->zapped = 1;
+ 			tcp_preclose(sks);
+ 			tcp_set_state(sks, TCP_CLOSE);
+ 			sks->dead = 1;
+ 			tcp_preclose(skc);
+ 			if (!skc->socket) {
+ 				/*
+ 				 * Client side not yet accepted.
+ 				 * Mark it closed and notify listener.
+ 				 */
+ 				skc->state = TCP_CLOSE;
+ 			} else {
+ 				/*
+ 				 * Client side was accepted.
+ 				 * Close it normally.
+ 				 */
+ 				tcp_set_state(skc, TCP_CLOSE);
+ 			}
+ 			skc->state_change(skc);
+ 			return(0);
+ 		}
+ 		/*
+ 		 * Else if resetting either side of a dontcare pair, kill
+ 		 * them both.
+ 		 */
+ 		if (sk->dontcare) {
+ 			sk2->zapped = 1;
+ 			tcp_dontcare_cleanup(sk, 1);
+ 			kfree_skb(skb, FREE_READ);
+ 			return(0);
+ 		}
+ 		/*
+ 		 * Else both socks are handled normally, so only reset the
+ 		 * one that received the reset.  Fall through for that.
+ 		 */
+ 	}
+ 
  #ifdef CONFIG_TCP_RFC1337
  	/*
  	 *	Time wait assassination protection [RFC1337]
***************
*** 271,280 ****
--- 356,367 ----
  	 */
  	if(sk->state!=TCP_TIME_WAIT)
  	{	
+ 		tcp_preclose(sk);
  		tcp_set_state(sk,TCP_CLOSE);
  		sk->shutdown = SHUTDOWN_MASK;
  	}
  #else	
+ 	tcp_preclose(sk);
  	tcp_set_state(sk,TCP_CLOSE);
  	sk->shutdown = SHUTDOWN_MASK;
  #endif	
***************
*** 355,369 ****
   *	Because of the way BSD works, we have to send a syn/ack now.
   *	This also means it will be harder to close a socket which is
   *	listening.
   */
   
  static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
  		 u32 daddr, u32 saddr, struct options *opt, struct device *dev, u32 seq)
  {
! 	struct sock *newsk;
  	struct tcphdr *th;
  	struct rtable *rt;
!   
  	th = skb->h.th;
  
  	/* If the socket is dead, don't accept the connection. */
--- 442,459 ----
   *	Because of the way BSD works, we have to send a syn/ack now.
   *	This also means it will be harder to close a socket which is
   *	listening.
+  *
+  *	This is modified with Step 1 of our scanning solution.
   */
   
  static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
  		 u32 daddr, u32 saddr, struct options *opt, struct device *dev, u32 seq)
  {
! 	struct sock *newsk, *sk2;
  	struct tcphdr *th;
  	struct rtable *rt;
! 	int scanflag = skb->ipcatch;
!  
  	th = skb->h.th;
  
  	/* If the socket is dead, don't accept the connection. */
***************
*** 375,381 ****
  	{
  		if(sk->debug)
  			printk("Reset on %p: Connect on dead socket.\n",sk);
! 		tcp_send_reset(daddr, saddr, th, sk->prot, opt, dev, 0,255);
  		tcp_statistics.TcpAttemptFails++;
  		kfree_skb(skb, FREE_READ);
  		return;
--- 465,471 ----
  	{
  		if(sk->debug)
  			printk("Reset on %p: Connect on dead socket.\n",sk);
! 		tcp_send_reset(daddr, saddr, th, sk->prot, opt, dev, 0,255,skb);
  		tcp_statistics.TcpAttemptFails++;
  		kfree_skb(skb, FREE_READ);
  		return;
***************
*** 414,419 ****
--- 504,514 ----
  	}
  
  	memcpy(newsk, sk, sizeof(*newsk));
+ 	newsk->scanpair = 0;
+ 	newsk->scandev = 0;
+ 	newsk->dontcare = 0;
+ 	newsk->scanclient = 0;
+ 	newsk->finflag = 0;
  	newsk->opt = NULL;
  	newsk->ip_route_cache  = NULL;
  	if (opt && opt->optlen) 
***************
*** 519,524 ****
--- 614,623 ----
  	newsk->saddr = daddr;
  	newsk->rcv_saddr = daddr;
  
+ 	/* Scanned sockets do not inherit port from listening socket. */
+ 	/* (not needed if we turn on transparent proxy above.) */
+ 	if(scanflag)
+ 		newsk->num = ntohs(skb->h.th->dest);
  	put_sock(newsk->num,newsk);
  	newsk->acked_seq = skb->seq + 1;
  	newsk->copied_seq = skb->seq + 1;
***************
*** 579,587 ****
  	 */
  
  	tcp_options(newsk,skb->h.th);
  	
  	tcp_cache_zap();
! 	tcp_send_synack(newsk, sk, skb);
  }
  
  
--- 678,811 ----
  	 */
  
  	tcp_options(newsk,skb->h.th);
+ 
+ /*
+  * Scanning Setup Step 1.
+  * Handle a SYN.  Now create the second sock of the sock pair.
+  */
+ 	if (scanflag)
+ 	{
+ 		sk2 = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
+ 		if (sk2 == NULL) 
+ 		{
+ 			printk("tcp_conn_request: failed to alloc sk2\n");
+ 			kfree_s((void *)newsk,sizeof(*newsk));
+ 			/* just ignore the syn.  It will get retransmitted. */
+ 			tcp_statistics.TcpAttemptFails++;
+ 			kfree_skb(skb, FREE_READ);
+ 			return;
+ 		}
+ 
+ 		memcpy(sk2, newsk, sizeof(*sk2));
+ 		newsk->scanpair = sk2;
+ 		sk2->scanpair = newsk;
+ 		newsk->scandev = skb->dev;
+ 		sk2->scandev = 0; /* should be set by SYNACK */
+ 		sk2->dontcare = 0;
+ 		sk2->scanclient = 0;
+ 		sk2->finflag = 0;
+ 		newsk->scanclient = 1;
+ 		/* do this here instead of tcp_send_synack() */
+ 		tcp_select_window(newsk);
+ 
+ 		/*
+ 		 * Reinitialize and swap a bunch of values.
+ 		 * Notable differences are in the sequence numbers,
+ 		 * state, and IP addresses.
+ 		 *
+ 		 * Because newsk is connected from the client, it represents
+ 		 * the server socket.  Similarly, sk2 makes the connection
+ 		 * to the server, so it represents the client socket.
+ 		 * Therefore, use the same sequence number and IP addresses
+ 		 * as from the client, and enter the TCP_SYN_SENT state.
+ 		 */
+ 		skb_queue_head_init(&sk2->write_queue);
+ 		skb_queue_head_init(&sk2->receive_queue);
+ 		skb_queue_head_init(&sk2->back_log);
+ 
+ 		sk2->acked_seq = 0;			/* no ack yet */
+ 		sk2->lastwin_seq = 0;			/* no ack yet */
+ 		sk2->copied_seq = 0;			/* no ack yet */
+ 		sk2->fin_seq = 0;			/* no fin yet */
+ 
+ 		sk2->state = TCP_SYN_SENT;		/* sending SYN */
+ 		sk2->write_seq = skb->seq+1;		/* keep this seq # */
+ 		sk2->window_seq = sk2->write_seq;
+ 		sk2->sent_seq = sk2->write_seq;
+ 		sk2->rcv_ack_seq = sk2->write_seq-1;
+ 
+ 		init_timer(&sk2->timer);
+ 		sk2->timer.data = (unsigned long)sk2;
+ 		init_timer(&sk2->retransmit_timer);
+ 		sk2->retransmit_timer.data = (unsigned long)sk2;
+ 		init_timer(&sk2->delack_timer);
+ 		sk2->delack_timer.data = (unsigned long)sk2;
+ 
+ 		/*
+ 		 * newsk has the addresses swapped with those in the
+ 		 * incoming SYN packet, sk2 keeps them unswapped.
+ 		 *
+ 		 * Note: get_sock() uses dummy_th.dest to represent the
+ 		 * remote port (which is the source port in an incoming
+ 		 * packet) and uses num to represent the local port (the
+ 		 * destination port in an incoming packet).  newsk gets
+ 		 * its num automatically from sk, so sk2 must get it
+ 		 * manually from skb->h.th->source.
+ 		 */
+ 		sk2->dummy_th.source = skb->h.th->source;
+ 		sk2->dummy_th.dest = skb->h.th->dest;
+ 		sk2->daddr = daddr;
+ 		sk2->saddr = saddr;
+ 		sk2->rcv_saddr = saddr; /* used in get_sock() */
+ 		sk2->num = ntohs(skb->h.th->source);
+ 
+ 		/* now deal with MSS */
+ 		rt = ip_rt_route(sk2->daddr, 0);
+ 		sk2->ip_route_cache = rt;
+ 		
+ 		if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
+ 			sk2->window_clamp = rt->rt_window;
+ 		else
+ 			sk2->window_clamp = 0;
+ 			
+ 		if (sk->user_mss)
+ 			sk2->mtu = sk->user_mss;
+ 		else if (rt)
+ 			sk2->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
+ 		else 
+ 			sk2->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
+ 		tcp_select_window(sk2);
+ 
+ 		/* put us on the protocols search list: we are alive */
+ 		put_sock(sk2->num, sk2);
+ 
+ 		sk2->scanaccepted = 0;
+ 		make_syn_pkt(sk2, skb, skb->h.th);
+ 
+ 		/*
+ 		 * Charge the sock_buff to newsk as we do in tcp_send_synack.
+ 		 *
+ 		 * This charges the skb memory costs to newsk instead of
+ 		 * sk (TCP_scansock), even though skb sits on TCP_scansock's
+ 		 * receive_queue.  The skb->sk points to newsk, and
+ 		 * newsk->scanpair points to the sock pair.
+ 		 *   TCP_scansock.receive_queue -> skb -> sk -> scanpair
+ 		 */
+ 		 
+ 		skb->sk = newsk;
+ 		atomic_sub(skb->truesize, &sk->rmem_alloc);
+ 		atomic_add(skb->truesize, &newsk->rmem_alloc);
+ 		skb_queue_tail(&sk->receive_queue, skb);
+ 		sk->ack_backlog++; /* needed for tcp_accept() */
+ 		tcp_statistics.TcpOutSegs++;
+ 		log_sockevent(sk2, 2, sk2->write_seq, sk2->copied_seq);
+ 	}
  	
+ 	log_sockevent(newsk, 1, newsk->write_seq, newsk->copied_seq);
+ 
  	tcp_cache_zap();
! 	if (!scanflag)
! 		tcp_send_synack(newsk, sk, skb);
  }
  
  
***************
*** 659,665 ****
   *	the algorithm is wrong, the code is wrong.
   */
  
! static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len)
  {
  	int flag = 0;
  	u32 window_seq;
--- 883,890 ----
   *	the algorithm is wrong, the code is wrong.
   */
  
! static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len,
! 			struct sk_buff *skb)
  {
  	int flag = 0;
  	u32 window_seq;
***************
*** 1093,1098 ****
--- 1318,1329 ----
  		tcp_send_partial(sk);
  	}
  
+ 	if (sk->send_head == NULL && sk->dontcare)
+ 	{
+ 		if (tcp_dontcare_cleanup(sk, 0))
+ 			return 1;
+ 	}
+ 
  	/*
  	 * In the LAST_ACK case, the other end FIN'd us.  We then FIN'd them, and
  	 * we are now waiting for an acknowledge to our FIN.  The other end is
***************
*** 1111,1116 ****
--- 1342,1348 ----
  		if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) 
  		{
  			sk->shutdown = SHUTDOWN_MASK;
+ 			tcp_preclose(sk);
  			tcp_set_state(sk,TCP_CLOSE);
  			return 1;
  		}
***************
*** 1168,1173 ****
--- 1400,1412 ----
  		tcp_options(sk,th);
  		sk->dummy_th.dest=th->source;
  		sk->copied_seq = sk->acked_seq;
+ /*
+  * Scanning Setup Step 3.
+  * Process the ACK of the SYNACK (common case).
+  * Or at least process the ACK of a crossed SYN (uncommon case).
+  */
+ 		if(sk->scanpair)
+ 			handle_ack_synack(sk, skb, th);
  		if(!sk->dead)
  			sk->state_change(sk);
  		if(sk->max_window==0)
***************
*** 1247,1252 ****
--- 1486,1492 ----
  {
  	sk->fin_seq = skb->end_seq;
  
+ 	log_sockevent(sk, 4, sk->fin_seq, sk->copied_seq);
  	if (!sk->dead) 
  	{
  		sk->state_change(sk);
***************
*** 1565,1576 ****
  				if(sk->dead)
  				{
  					sk->acked_seq = new_seq + th->fin;
! 					tcp_send_reset(sk->saddr, sk->daddr, skb->h.th,
! 						sk->prot, NULL, skb->dev, 0, 255);
  					tcp_statistics.TcpEstabResets++;
  					sk->err = EPIPE;
  					sk->error_report(sk);
  					sk->shutdown = SHUTDOWN_MASK;
  					tcp_set_state(sk,TCP_CLOSE);
  					kfree_skb(skb, FREE_READ);
  					return 0;
--- 1805,1818 ----
  				if(sk->dead)
  				{
  					sk->acked_seq = new_seq + th->fin;
! 					tcp_send_reset(sk->saddr, sk->daddr,
! 						skb->h.th, sk->prot, NULL,
! 						skb->dev, 0, 255, skb);
  					tcp_statistics.TcpEstabResets++;
  					sk->err = EPIPE;
  					sk->error_report(sk);
  					sk->shutdown = SHUTDOWN_MASK;
+ 					tcp_preclose(sk);
  					tcp_set_state(sk,TCP_CLOSE);
  					kfree_skb(skb, FREE_READ);
  					return 0;
***************
*** 1607,1612 ****
--- 1849,1855 ----
  {
  	u32 ptr = ntohs(th->urg_ptr);
  
+ 	log_sockevent(sk, 6, sk->write_seq, sk->copied_seq);
  	if (ptr)
  		ptr--;
  	ptr += ntohl(th->seq);
***************
*** 1635,1641 ****
--- 1878,1888 ----
  	 *	or we break the sematics of SIOCATMARK (and thus sockatmark())
  	 */
  	if (sk->urg_seq == sk->copied_seq)
+ 	{
+ 		printk("tcp_check_urg: increasing copied_seq %u for %x, %x\n",
+ 			sk->copied_seq, th->source, th->dest);
  		sk->copied_seq++;	/* Move the copied sequence on correctly */
+ 	}
  	sk->urg_data = URG_NOTYET;
  	sk->urg_seq = ptr;
  }
***************
*** 1747,1752 ****
--- 1994,2242 ----
  }
  #endif
  
+ /* Forward the duplicate SYN. */
+ void handle_dupsyn_scan(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
+ {
+ 	if (!sk->scanpair)
+ 	{
+ 		printk("dupsyn dropping forward\n");
+ 		return;
+ 	}
+ 	make_syn_pkt(sk->scanpair, skb, th);
+ }
+ 
+ /* Forward the crossed SYN.  This will rarely be used. */
+ void handle_cross_syns(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
+ {
+ 	struct sock *sk2 = sk->scanpair;
+ 
+ 	if (!sk2)
+ 	{
+ 		printk("cross_syns dropping forward\n");
+ 		return;
+ 	}
+ 	/* record ISS info as handle_synack_scan() does */
+ 	sk2->write_seq = skb->seq+1;		/* keep this seq # */
+ 	sk2->window_seq = sk2->write_seq + ntohs(skb->h.th->window);
+ 	sk2->sent_seq = sk2->write_seq;
+ 	sk2->rcv_ack_seq = sk2->write_seq-1;
+ 	make_syn_pkt(sk->scanpair, skb, th);
+ }
+ 
+ /* Forward the SYNACK. */
+ void handle_synack_scan(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
+ {
+ 	struct sock *sk2 = sk->scanpair;
+ 
+ 	if (!sk2)
+ 	{
+ 		printk("synack dropping forward\n");
+ 		return;
+ 	}
+ 	sk2->write_seq = skb->seq+1;		/* keep this seq # */
+ 	sk->lastwin_seq = skb->seq+1;
+ 	sk->acked_seq = skb->seq+1;
+ 	sk2->window_seq = sk2->write_seq + ntohs(skb->h.th->window);
+ 	sk2->sent_seq = sk2->write_seq;
+ 	sk2->rcv_ack_seq = sk2->write_seq-1;
+ 	make_syn_pkt(sk->scanpair, skb, th);
+ }
+ 
+ /*
+  * Normally, the ACK of the SYNACK represents the last connection-setup
+  * communication.  Unless we crossed SYNs, our two socks are ESTABLISHED.
+  * Put them on the normal timers.
+  */
+ void handle_ack_synack(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
+ {
+ 	struct sock *sk2 = sk->scanpair;
+ 
+ 	if (!sk2)
+ 	{
+ 		printk("ack_synack dropping forward\n");
+ 		return;
+ 	}
+ 	make_syn_pkt(sk2, skb, th);
+ 
+ 	/* I already know sk is ESTABLISHED.  tcp_ack() made it so. */
+ 	if(sk2->state == TCP_SYN_RECV || sk2->state == TCP_SYN_SENT)
+ 		return;
+ 
+ 	/* what timers are we interested in? */
+ 	if(sk->keepopen) {
+ 		tcp_reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
+ 	} else {
+ 		del_timer(&sk->retransmit_timer);
+ 	}
+ }
+ 
+ /*
+  * Mimic tcp_rcv() for dontcare socks to avoid TCP state changes.
+  */
+ void dontcare_rcv(struct sock *sk, struct sk_buff *skb,
+ 		struct tcphdr *th, int len)
+ {
+ 	u32 seq, end_seq, ack_seq;
+ 	int tcp_update_mac(struct sock *sk, struct sk_buff *skb);
+ 
+ 	if (sk->users) {
+ 		skb->sk = NULL;
+ 		kfree_skb(skb, FREE_READ);
+ 		return;
+ 	}
+ 
+ 	seq = ntohl(th->seq);
+ 	end_seq = seq + th->syn + th->fin + len - th->doff*4;
+ 	ack_seq = ntohl(th->ack_seq);
+ 	if (tcp_sequence(sk, seq, end_seq-th->syn) != 1) {
+ 		/* bad sequence number */
+ 		skb->sk = NULL;
+ 		kfree_skb(skb, FREE_READ);
+ 		return;
+ 	}
+ 
+ 	if (th->rst) {
+ 		tcp_reset(sk, skb);
+ 		return;
+ 	}
+ 	if (tcp_update_mac(sk, skb) == -1)
+ 		return; /* corrupt packet structure */
+ 	if (th->ack)
+ 		tcp_ack(sk, th, ack_seq, len, skb);
+ 	skb->sk = NULL;
+ 	kfree_skb(skb, FREE_READ);
+ }
+ 
+ #if 0
+ /* used only for debugging statements in tcp_update_mac(). */
+ void macstr(char *buf, char *cp, int len)
+ {
+ 	while(len-- > 0) {
+ 		sprintf(buf, "%02x", (unsigned char) *cp);
+ 		cp++;
+ 		buf += 2;
+ 		if(len)
+ 			*buf++ = ':';
+ 	}
+ 	*buf = '\0';
+ }
+ 
+ /* Provide separate static buffers.  Buffer sizes should be based on */
+ /* dev->addr_len, not ETH_ALEN.  I use neither. */
+ char *macstr1(char *cp, int len)
+ {
+ 	static char buf1[30];	/* must be over 6*2+5*1+1=18 */
+ 	macstr(buf1, cp, len);
+ 	return buf1;
+ }
+ char *macstr2(char *cp, int len)
+ {
+ 	static char buf2[30];	/* must be over 6*2+5*1+1=18 */
+ 	macstr(buf2, cp, len);
+ 	return buf2;
+ }
+ #endif
+ 
+ /*
+  * BoP reports a panic apparently caused by mac.raw getting set
+  * to time in seconds (and other garbage).  This is similar to
+  * what was seen in br_tx_frame() and bridge_xmit() a while ago.
+  * Now it shows up in tcp_update_mac().  Check the skb there and
+  * in tcp_rcv().
+  */
+ int check_skbuff(struct sk_buff *skb, char *str)
+ {
+ 	/*
+ 	 * BoP reports a panic apparently caused by mac.raw getting set
+ 	 * to time in seconds (and other garbage).  This is similar to
+ 	 * what was seen in br_tx_frame() and bridge_xmit() a while ago.
+ 	 * Here are some similar diagnostics.
+ 	 */
+ 	if (skb->mac.raw < skb->head || skb->mac.raw > skb->end) {
+ 		printk("%s: bad head/mac.raw/end %p/%p/%p\n",
+ 			str, skb->head, skb->mac.raw, skb->end);
+ 		return -1;
+ 	}
+ 	if (skb->data < skb->head || skb->data > skb->end) {
+ 		printk("%s: bad head/data/end %p/%p/%p\n",
+ 			str, skb->head, skb->data, skb->end);
+ 		return -1;
+ 	}
+ 	return 0;
+ }
+ 
+ /*
+  *	Update the destination MAC address used by either interface.
+  *	This resolves some routing issues by using whatever MAC that the
+  *	peer provided.  This also seems to work around ARP.
+  *
+  *	The sk->hw_addr field is used in arp_find().
+  *	This code and the diagnostics above really belong elsewhere (arp.c?).
+  */
+ int tcp_update_mac(struct sock *sk, struct sk_buff *skb)
+ {
+ 	struct device *dev = sk->scandev;
+ 	struct sock *sk2 = sk->scanpair;
+ 	char *dmac = (char *) &skb->mac.ethernet->h_dest;
+ 
+ 	if(!dev)	/* starting a connection */
+ 	{
+ 		if(!sk->scanpair)
+ 			return 0; /* SYN? */
+ 		/* SYNACKs arrive at a sock with no scandev */
+ 		dev = sk->scandev = skb->dev;
+ 	}
+ 	if(!sk2)	/* closing a scanned connection */
+ 		return 0;
+ 
+ 	if (check_skbuff(skb, "tcp_update_mac") == -1)
+ 		return -1;
+ 
+ 	/*
+ 	 * Must also update sk->indev.  This value is put in skb->indev
+ 	 * (by my_build_header()) of each outgoing packet so that the
+ 	 * bridging code in br_tx_frame() knows not to put the packet
+ 	 * on this device.
+ 	 */
+ 	sk2->indev = dev;
+ 
+ 	/* inbound packet arrived */
+ 	if(memcmp(sk2->hw_addr, dmac, dev->addr_len)) {
+ 		/* destination MAC address is changing */
+ #if 0
+ 		/* unnecessary diagnostics */
+ 		/* The first change is always from 0 to non 0. */
+ 		printk("tcp_update_mac: change MAC from %s to %s\n",
+ 		macstr1(sk2->hw_addr, dev->addr_len),
+ 		macstr2(dmac, dev->addr_len));
+ #endif
+ 		memcpy(sk2->hw_addr, dmac, dev->addr_len);
+ 	}
+ 	return 0;
+ }
+ 
+ /*
+  *	Indicate whether this is a close enough match between the packet
+  *	and sock to be used for a scanned connection.  This need not be
+  *	exact to accomodate IP transparent proxy (as in Linux 2.0).
+  */
+ int close_match_sk(struct sock *sk, unsigned short dest, unsigned long daddr)
+ {
+ 	return(sk->num == ntohs(dest) && sk->saddr == daddr);
+ }
+ 
+ /*
+  *	Indicate whether this was an exact match or a wildcard match.
+  *	That is important when deciding to bridge or handle a packet.
+  */
+ int exact_match_sk(struct sock *sk, unsigned short dest, unsigned long saddr,
+ 				unsigned short source, unsigned long daddr)
+ {
+ 	return(sk->num == ntohs(dest) && sk->dummy_th.dest == source &&
+ 		sk->saddr == daddr && sk->daddr == saddr);
+ }
+ 
+ 
  /*
   *	A TCP packet has arrived.
   *		skb->h.raw is the TCP header.
***************
*** 1759,1764 ****
--- 2249,2256 ----
  	struct tcphdr *th;
  	struct sock *sk;
  	__u32 seq;
+ 	void remove_sock(struct sock *sk1);
+ 	static int phantom_cnt = 0;
  #ifdef CONFIG_IP_TRANSPARENT_PROXY
  	int r;
  #endif
***************
*** 1773,1786 ****
  	sk = skb->sk;
  	if (!redo) {
  		tcp_statistics.TcpInSegs++;
! 		if (skb->pkt_type!=PACKET_HOST)
  			goto discard_it;
  
  		/*
  		 *	Pull up the IP header.
  		 */
  	
  		skb_pull(skb, skb->h.raw-skb->data);
  
  		/*
  		 *	Try to use the device checksum if provided.
--- 2265,2282 ----
  	sk = skb->sk;
  	if (!redo) {
  		tcp_statistics.TcpInSegs++;
! 		if (!skb->ipcatch && skb->pkt_type!=PACKET_HOST)
  			goto discard_it;
  
  		/*
  		 *	Pull up the IP header.
  		 */
  	
+ 		if (check_skbuff(skb, "tcp_rcv") == -1)
+ 			return 0;
  		skb_pull(skb, skb->h.raw-skb->data);
+ 		if (check_skbuff(skb, "tcp_rcv2") == -1)
+ 			return 0;
  
  		/*
  		 *	Try to use the device checksum if provided.
***************
*** 1796,1801 ****
--- 2292,2339 ----
  				/* CHECKSUM_UNNECESSARY */
  		}
  		sk = get_tcp_sock(saddr, th->source, daddr, th->dest, dev->pa_addr, skb->redirport);
+ 		/*
+ 		 *	Handle catchable packets specially using the best-fit
+ 		 *	sock given by get_sock() if appropriate.
+ 		 *	Non-SYN packets with no exact match are bridged.
+ 		 *	Null TCP_scansock causes SYN packets to be reset.
+ 		 */
+ 		if (skb->ipcatch) {
+ 			if (th->syn) {
+ 				/* keep the selected sock or use TCP_scansock */
+ 				if (!sk || !close_match_sk(sk, th->dest, daddr))
+ 					sk = TCP_scansock; /* may be NULL */
+ 				if (sk && sk == TCP_scansock) {
+ 					/* peer must not yet (or still) exist */
+ 					/* also see TIMEWAIT case down below */
+ 					struct sock *sktmp;
+ 					sktmp = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr, dev->pa_addr, skb->redirport);
+ 					if (sktmp && exact_match_sk(sktmp,
+ 						th->source, daddr, th->dest,
+ 						saddr)) {
+ 						/* found peer, gotta fail */
+ 						goto no_tcp_socket;
+ 					}
+ 				}
+ 			} else {
+ 				if (!sk || !exact_match_sk(sk, th->dest, saddr,
+ 							th->source, daddr)) {
+ 					sk = NULL;
+ 				} else if (sk && sk->dontcare) {
+ 					/* Let the (!redo && sk->users) case be
+ 					 * backlogged by the !redo code.
+ 					 */
+ 					dontcare_rcv(sk, skb, th, len);
+ 					return(0);
+ 				}
+ 				if (!sk) {
+ 					/* bridge_xmit() does the skb_push() */
+ 					bridge_xmit(skb, dev, 0);
+ 					return(0);
+ 				}
+ 			}
+ 		}
+ 
  		if (!sk)
  			goto no_tcp_socket;
  		skb->sk = sk;
***************
*** 1837,1842 ****
--- 2375,2443 ----
  		return(0);
  	}
  
+ 	if (sk->dontcare)
+ 	{
+ 		/*
+ 		 * This must be a 'redo' packet that arrived while in the
+ 		 * process of making the sock a dontcare sock.  Since it
+ 		 * has not yet been processed, just drop it.
+ 		 */
+ 		skb->sk = NULL;
+ 		kfree_skb(skb, FREE_READ);
+ 		return(0);
+ 	}
+ 
+ 	/*
+ 	 *	Update destination MAC information for peer interface.
+ 	 */
+ 	if (skb->ipcatch)
+ 		if (tcp_update_mac(sk, skb) == -1)
+ 			return(0); /* corrupt packet structure */
+ 
+ 	/*
+ 	 * Check for packets coming through a phantom connection.
+ 	 * A phantom connection happens when a (Windows 3.1) SYN
+ 	 * retransmit comes in right after a dontcare cleanup.  Yikes!
+ 	 * The clue is that the packet will ack a sequence we have not
+ 	 * initialized because we never received a SYNACK.  The packet
+ 	 * can come from either side, though we have only seen simple
+ 	 * ACKs from the client.
+ 	 */
+ 	if (sk->state == TCP_SYN_RECV || sk->state == TCP_SYN_SENT) {
+ 		struct sock *sk2 = sk->scanpair;
+ 
+ 		if (sk2 && skb->ipcatch && th->ack && !th->syn && !th->rst &&
+ 		   (sk2->state == TCP_SYN_RECV || sk2->state == TCP_SYN_SENT)) {
+ 			/*
+ 			 * Kill both socks in a phantom connection.
+ 			 * Since the server has no reference, mark it dead.
+ 			 */
+ 			struct sock *skc = (sk->scanclient) ? sk : sk2;
+ 			struct sock *sks;
+ 
+ 			if (++phantom_cnt < 5) {
+ 				printk("Killing phantom connection [%u,%u]\n",
+ 					sk->num, sk2->num);
+ 			}
+ 			sks = skc->scanpair;
+ 			tcp_preclose(sks);
+ 			sks->dead = 1;
+ 			tcp_set_state(sks, TCP_CLOSE);
+ 			/*
+ 			 * On the listener queue, closed incomplete connections
+ 			 * are destroyed by the listener in inet_accept().
+ 			 */
+ 			tcp_preclose(skc);
+ 			if (!skc->dead) {
+ 				skc->state = TCP_CLOSE;
+ 				skc->state_change(skc);
+ 			} else
+ 				tcp_set_state(skc, TCP_CLOSE);
+ 			skb->sk = NULL;
+ 			bridge_xmit(skb, dev, 0);
+ 			return(0);
+ 		}
+ 	}
  
  	/*
  	 *	Charge the memory to the socket. 
***************
*** 1871,1877 ****
  		if(sk->state==TCP_LISTEN)
  		{
  			if(th->ack)	/* These use the socket TOS.. might want to be the received TOS */
! 				tcp_send_reset(daddr,saddr,th,sk->prot,opt,dev,0, 255);
  
  			/*
  			 *	We don't care for RST, and non SYN are absorbed (old segments)
--- 2472,2479 ----
  		if(sk->state==TCP_LISTEN)
  		{
  			if(th->ack)	/* These use the socket TOS.. might want to be the received TOS */
! 				tcp_send_reset(daddr, saddr, th, sk->prot,
! 					opt, dev, 0, 255, skb);
  
  			/*
  			 *	We don't care for RST, and non SYN are absorbed (old segments)
***************
*** 1888,1894 ****
  			 */
  			if(th->rst || !th->syn || th->ack || (r = ip_chk_addr(daddr)) == IS_BROADCAST || r == IS_MULTICAST)
  #else
! 			if(th->rst || !th->syn || th->ack || ip_chk_addr(daddr)!=IS_MYADDR)
  #endif
  			{
  				kfree_skb(skb, FREE_READ);
--- 2490,2497 ----
  			 */
  			if(th->rst || !th->syn || th->ack || (r = ip_chk_addr(daddr)) == IS_BROADCAST || r == IS_MULTICAST)
  #else
! 			if(th->rst || !th->syn || th->ack ||
! 			   (!skb->ipcatch && ip_chk_addr(daddr)!=IS_MYADDR))
  #endif
  			{
  				kfree_skb(skb, FREE_READ);
***************
*** 1924,1929 ****
--- 2527,2534 ----
  		 
  		if (sk->state == TCP_SYN_RECV && th->syn && skb->seq+1 == sk->acked_seq)
  		{
+ 			if (sk->scanpair)
+ 				handle_dupsyn_scan(sk, skb, th);
  			kfree_skb(skb, FREE_READ);
  			return 0;
  		}
***************
*** 1952,1958 ****
  					   different connection  [ th->rst is checked in tcp_send_reset()] */
  					tcp_statistics.TcpAttemptFails++;
  					tcp_send_reset(daddr, saddr, th,
! 						sk->prot, opt,dev,0,255);
  					kfree_skb(skb, FREE_READ);
  					return(0);
  				}
--- 2557,2563 ----
  					   different connection  [ th->rst is checked in tcp_send_reset()] */
  					tcp_statistics.TcpAttemptFails++;
  					tcp_send_reset(daddr, saddr, th,
! 						sk->prot, opt,dev,0,255,skb);
  					kfree_skb(skb, FREE_READ);
  					return(0);
  				}
***************
*** 1964,1970 ****
  					   start. Shouldn't happen but cover it */
  	         			tcp_statistics.TcpAttemptFails++;
  	                                tcp_send_reset(daddr, saddr, th,
! 	                                        sk->prot, opt,dev,0,255);
  					kfree_skb(skb, FREE_READ);
  					return 0;
  				}
--- 2569,2575 ----
  					   start. Shouldn't happen but cover it */
  	         			tcp_statistics.TcpAttemptFails++;
  	                                tcp_send_reset(daddr, saddr, th,
! 	                                        sk->prot, opt,dev,0,255,skb);
  					kfree_skb(skb, FREE_READ);
  					return 0;
  				}
***************
*** 1974,1980 ****
  				 * processing stuff. [We know it's good, and
  				 * we know it's the SYN,ACK we want.]
  				 */
! 				tcp_ack(sk,th,skb->ack_seq,len);
  
  
  				/*
--- 2579,2585 ----
  				 * processing stuff. [We know it's good, and
  				 * we know it's the SYN,ACK we want.]
  				 */
! 				tcp_ack(sk,th,skb->ack_seq,len,skb);
  
  
  				/*
***************
*** 1984,1992 ****
  				sk->acked_seq = skb->seq+1;
  				sk->lastwin_seq = skb->seq+1;
  				sk->fin_seq = skb->seq;
- 				tcp_send_ack(sk);
  				tcp_set_state(sk, TCP_ESTABLISHED);
  				tcp_options(sk,th);
  				sk->dummy_th.dest=th->source;
  				sk->copied_seq = sk->acked_seq;
  				if(!sk->dead)
--- 2589,2606 ----
  				sk->acked_seq = skb->seq+1;
  				sk->lastwin_seq = skb->seq+1;
  				sk->fin_seq = skb->seq;
  				tcp_set_state(sk, TCP_ESTABLISHED);
  				tcp_options(sk,th);
+ /*
+  * Scanning Setup Step 2.
+  * Handle the SYNACK.  Usually, this will put the server side of the
+  * sock pair into ESTABLISHED state.
+  */
+ 				if(sk->scanpair) {
+ 					handle_synack_scan(sk, skb, th);
+ 				} else {
+ 					tcp_send_ack(sk);
+ 				}
  				sk->dummy_th.dest=th->source;
  				sk->copied_seq = sk->acked_seq;
  				if(!sk->dead)
***************
*** 2022,2027 ****
--- 2636,2643 ----
  						return tcp_reset(sk,skb);
  					}
  					tcp_set_state(sk,TCP_SYN_RECV);
+ 					if(sk->scanpair)
+ 						handle_cross_syns(sk, skb, th);
  					
  					/*
  					 *	FIXME:
***************
*** 2061,2069 ****
--- 2677,2692 ----
  			atomic_sub(skb->truesize, &sk->rmem_alloc);
  			skb->sk = NULL;
  			sk->err=ECONNRESET;
+ 			tcp_preclose(sk);
  			tcp_set_state(sk, TCP_CLOSE);
  			sk->shutdown = SHUTDOWN_MASK;
  			sk=get_sock(&tcp_prot, th->dest, saddr, th->source, daddr, dev->pa_addr, skb->redirport);
+ 			if (skb->ipcatch) {
+ 				if (!sk || !close_match_sk(sk, th->dest, daddr))
+ 				/* keep the selected sock or use TCP_scansock */
+ 					sk = TCP_scansock; /* may be NULL */
+ 				/* should also look for peer... later */
+ 			}
  			/* this is not really correct: we should check sk->users */
  			if (sk && sk->state==TCP_LISTEN)
  			{
***************
*** 2084,2094 ****
  	 *	I have time to test it hard and look at what gcc outputs 
  	 */
  
! 	if (!tcp_sequence(sk, skb->seq, skb->end_seq-th->syn))
! 	{
! 		bad_tcp_sequence(sk, th, skb->end_seq-th->syn, dev);
  		kfree_skb(skb, FREE_READ);
  		return 0;
  	}
  
  	if(th->rst)
--- 2707,2726 ----
  	 *	I have time to test it hard and look at what gcc outputs 
  	 */
  
! 	/* Hacked the interface to tcp_sequence() for duplicate SYNACKs. */
! 	switch (tcp_sequence(sk, skb->seq, skb->end_seq-th->syn)) {
! 	case 2: /* specific bad sequence number: a duplicate packet */
! 		if (sk->scanpair && th->syn && th->ack)
! 			/* handle duplicate SYNACKs */
! 			handle_dupsynack(sk, skb, th);
! 		/* fall thru */
! 	case 0: /* all bad sequence numbers */
! 		bad_tcp_sequence(sk, th, skb->end_seq-th->syn, dev, skb);
  		kfree_skb(skb, FREE_READ);
  		return 0;
+ 	case 1: /* good sequence number */
+ 	default:
+ 		break;
  	}
  
  	if(th->rst)
***************
*** 2111,2117 ****
  	 
  	if(th->syn && skb->seq!=sk->syn_seq)
  	{
! 		tcp_send_reset(daddr,saddr,th, &tcp_prot, opt, dev,0, 255);
  		return tcp_reset(sk,skb);	
  	}
  
--- 2743,2749 ----
  	 
  	if(th->syn && skb->seq!=sk->syn_seq)
  	{
! 		tcp_send_reset(daddr,saddr,th, &tcp_prot, opt, dev,0, 255,skb);
  		return tcp_reset(sk,skb);	
  	}
  
***************
*** 2120,2126 ****
  	 */
  	 
  
! 	if(th->ack && !tcp_ack(sk,th,skb->ack_seq,len))
  	{
  		/*
  		 *	Our three way handshake failed.
--- 2752,2758 ----
  	 */
  	 
  
! 	if(th->ack && !tcp_ack(sk,th,skb->ack_seq,len,skb))
  	{
  		/*
  		 *	Our three way handshake failed.
***************
*** 2128,2134 ****
  		 
  		if(sk->state==TCP_SYN_RECV)
  		{
! 			tcp_send_reset(daddr, saddr, th,sk->prot, opt, dev,0,255);
  		}
  		kfree_skb(skb, FREE_READ);
  		return 0;
--- 2760,2767 ----
  		 
  		if(sk->state==TCP_SYN_RECV)
  		{
! 			tcp_send_reset(daddr, saddr, th, sk->prot, opt,
! 				dev, 0, 255, skb);
  		}
  		kfree_skb(skb, FREE_READ);
  		return 0;
***************
*** 2172,2178 ****
  	/*
  	 *	No such TCB. If th->rst is 0 send a reset (checked in tcp_send_reset)
  	 */
! 	tcp_send_reset(daddr, saddr, th, &tcp_prot, opt,dev,0,255);
  
  discard_it:
  	/*
--- 2805,2811 ----
  	/*
  	 *	No such TCB. If th->rst is 0 send a reset (checked in tcp_send_reset)
  	 */
! 	tcp_send_reset(daddr, saddr, th, &tcp_prot, opt, dev, 0, 255, skb);
  
  discard_it:
  	/*
*** linux-2.0.28/net/ipv4/tcp_output.c	Mon Sep  2 05:18:26 1996
--- KERNEL/net/ipv4/tcp_output.c	Mon Nov 24 22:55:05 1997
***************
*** 28,33 ****
--- 28,34 ----
  #include <linux/ip_fw.h>
  #include <linux/firewall.h>
  #include <linux/interrupt.h>
+ #include <linux/if_ether.h>
  
  
  /*
***************
*** 164,169 ****
--- 165,175 ----
  	skb->seq = ntohl(th->seq);
  	skb->end_seq = skb->seq + size - 4*th->doff;
  
+ 	/* This is new here because now send_skb can send a FIN. */
+ 	/* Failure to do this can cause write_xmit to drop this packet. */
+ 	if(th->fin)
+ 		skb->end_seq++;
+ 
  	/*
  	 *	We must queue if
  	 *
***************
*** 444,449 ****
--- 450,475 ----
  		 *	Discard the surplus MAC header
  		 */
  		 
+ 		/*
+ 		 * SCANHACK: I may want to reconsider this in the future.
+ 		 * This does make every attempt to find the right destination
+ 		 * MAC addr.  It can find the latest dest in sk->hw_addr.
+ 		 *
+ 		 * An alternative to this ugly goto is to set up a fake
+ 		 * rtable and hh_cache to follow the normal flow.  That
+ 		 * looks like more work than it is worth.
+ 		 */
+ 		if (sk->scandev)
+ 		{
+ 			iph = skb->ip_hdr;
+ 			th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2));
+ 			size = ntohs(iph->tot_len) - (iph->ihl<<2);
+ 			iph->id = htons(ip_id_count++);
+ 			ip_send_check(iph);
+ 			skb->dev = dev = sk->scandev;
+ 			skb->arp = 1;
+ 			goto scanhack;
+ 		}
  		skb_pull(skb,((unsigned char *)skb->ip_hdr)-skb->data);
  
  		/*
***************
*** 540,545 ****
--- 566,572 ----
  			 *	this up by avoiding a full checksum.
  			 */
  		 
+ scanhack:
  			th->ack_seq = htonl(sk->acked_seq);
  			clear_delayed_acks(sk);
  			th->window = ntohs(tcp_select_window(sk));
***************
*** 614,620 ****
   */
   
  void tcp_send_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
! 	  struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl)
  {
  	struct sk_buff *buff;
  	struct tcphdr *t1;
--- 641,648 ----
   */
   
  void tcp_send_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
! 	struct proto *prot, struct options *opt, struct device *dev, int tos,
! 	int ttl, struct sk_buff *skb)
  {
  	struct sk_buff *buff;
  	struct tcphdr *t1;
***************
*** 646,658 ****
  	 *	Put in the IP header and routing stuff. 
  	 */
  
! 	tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
! 			   sizeof(struct tcphdr),tos,ttl,NULL);
! 	if (tmp < 0) 
  	{
!   		buff->free = 1;
! 		sock_wfree(NULL, buff);
! 		return;
  	}
  
  	t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
--- 674,714 ----
  	 *	Put in the IP header and routing stuff. 
  	 */
  
! 	if (skb && skb->ipcatch)
  	{
! 		/* do it manually because my ARP hooks do not work for resets */
! 		struct iphdr *iph;
! 		struct ethhdr *eth1 = skb->mac.ethernet;
! 		struct ethhdr *eth2;
! 
! 		ndev = skb->dev;
! 		skb_reserve(buff, MAX_HEADER);
! 		eth2 = (struct ethhdr *) skb_push(buff, ndev->hard_header_len);
! 		iph = buff->ip_hdr = (struct iphdr *) skb_put(buff, 20);
! 
! 		memcpy(eth2->h_dest, eth1->h_source, 6);
! 		memcpy(eth2->h_source, eth1->h_dest, 6);
! 		eth2->h_proto = eth1->h_proto;
! 
! 		iph->version  = 4;
! 		iph->ihl      = 5;
! 		iph->tos      = tos;
! 		iph->frag_off = 0;
! 		iph->ttl      = ttl;
! 		iph->daddr    = daddr;
! 		iph->saddr    = saddr;
! 		iph->protocol = IPPROTO_TCP;
! 	}
! 	else
! 	{
! 		tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP,
! 				opt, sizeof(struct tcphdr),tos,ttl,NULL);
! 		if (tmp < 0) 
! 		{
! 			buff->free = 1;
! 			sock_wfree(NULL, buff);
! 			return;
! 		}
  	}
  
  	t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
***************
*** 931,936 ****
--- 987,996 ----
  	    && skb_queue_empty(&sk->write_queue)
  	    && sk->ip_xmit_timeout == TIME_WRITE)
  	{
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 		if (sk->dontcare)
+ 			log_sockevent(sk, 16, 0, 0);
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
  		if (sk->keepopen)
  			tcp_reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
  		else
***************
*** 1252,1255 ****
--- 1312,1464 ----
  	/* Checksum the shrunk buffer */
  	tcp_send_check(th, sk->saddr, sk->daddr, 
  		   th->doff * 4 + len , skb);
+ }
+ 
+ /*
+  *	Make a best attempt at forwarding a connection setup packet.
+  *	This is used by scanned sockets during connection setup to
+  *	handle SYNs, SYNACKs, ACKs of SYNACKs, and retransmits.
+  *	It sends the packet with free=1 to prevent queueing for
+  *	automatic retransmission.
+  */
+ void make_syn_pkt(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
+ {
+ 	struct sk_buff *buff;
+ 	int tmp;
+ 	struct tcphdr *t1;
+ 	struct device *ndev;
+ 	unsigned char *ptr;
+ 	int pkt_size;
+ 	int seg_size;
+ 
+ #ifndef CONFIG_BRIDGE
+ 	ndev = skb->dev->devpair;
+ #else
+ 	ndev = skb->dev;	/* good enough for intelligent bridge */
+ #endif
+ 	if(!ndev)
+ 	{
+ 		printk("no device pair?\n");
+ 		return;
+ 	}
+ 
+ 	/* only SYN/SYNACK packets get TCP options */
+ 	if(th->syn)
+ 	{
+ 		seg_size = 24;
+ 		pkt_size = MAX_SYN_SIZE;
+ 	}
+ 	else
+ 	{
+ 		seg_size = 20;
+ 		pkt_size = MAX_SYN_SIZE - 4;
+ 	}
+ 	buff = sock_wmalloc(sk, pkt_size, 1, GFP_ATOMIC);
+ 	if (buff == NULL) 
+ 	{
+ 		/* Oh, well.  Maybe next retransmit will have better luck. */
+ 		printk("dropping forwarded packet\n");
+ 		return;
+ 	}
+ 	buff->protocol = htons(ETH_P_IP);
+ 	buff->sk = sk;
+ 	buff->arp = 1;
+ 	buff->localroute = sk->localroute;
+ 
+ 	/*
+ 	 * Copy MAC and IP:
+ 	 *
+ 	 * Reserving space for the MAC header moves the focal points up
+ 	 * to the middle of skbLogical space.  The push allocates some
+ 	 * of the reserved space for the use by the MAC header at
+ 	 * skb->data, the front focal point.  The put allocates space
+ 	 * for the IP header up to skb->tail, the tail focal point.
+ 	 */
+ 	skb_reserve(buff, MAX_HEADER);
+ 	ptr = skb_push(buff, ndev->hard_header_len);
+ 	buff->ip_hdr = (struct iphdr *) skb_put(buff, 20);
+ 	tmp = ndev->hard_header_len + 20;
+ 	memcpy(buff->data, skb->mac.raw, tmp);
+ 	buff->ip_hdr->ihl = 5;		/* no IP options */
+ 	buff->indev = skb->indev;	/* try to fix a SYN war */
+ 
+ 	/* Allocate space for the TCP header. */
+ 	t1 = (struct tcphdr *) skb_put(buff, sizeof(struct tcphdr));
+ 
+ 	memcpy(t1, (void *)&(sk->dummy_th), sizeof(*t1));
+ 	/* these next 5 lines seem to be the hardest to get right */
+ 	t1->seq = th->seq;
+ /* BRYAN: I am not really sure if these two lines are necessary now. */
+ 	buff->seq = skb->seq;
+ 	buff->end_seq = buff->seq+1;
+ 	t1->ack = th->ack;
+ 	t1->ack_seq = th->ack_seq;
+ 
+ 	t1->window = ntohs(sk->window);
+ 	t1->res1=0;
+ 	t1->res2=0;
+ 	t1->rst = 0;
+ 	t1->urg = 0;
+ 	t1->psh = 0;
+ 	t1->syn = th->syn;
+ 	t1->urg_ptr = 0;
+ 	t1->doff = (th->syn) ? 6 : 5;
+ 
+ 	/*
+ 	 *	Put in the TCP options to say MTU. 
+ 	 */
+ 
+ 	buff->csum = 0;
+ 	if(th->syn)
+ 	{
+ 		ptr = skb_put(buff, 4);
+ 		ptr[0] = 2;
+ 		ptr[1] = 4;
+ 		ptr[2] = (sk->mtu) >> 8;
+ 		ptr[3] = (sk->mtu) & 0xff;
+ 		buff->csum = csum_partial(ptr, 4, 0);
+ 	}
+ 	tcp_send_check(t1, sk->saddr, sk->daddr, seg_size, buff);
+ 	/* Use 2 so ip_queue_xmit() does not increase IP packet id. */
+ 	sk->prot->queue_xmit(sk, ndev, buff, 2);
+ 
+ 	/* reset timer on client sock */
+ 	if (!sk->scanclient)
+ 		sk = sk->scanpair;
+ 	if (sk->state == TCP_SYN_RECV || sk->state == TCP_SYN_SENT ||
+ 		sk->scanpair->state == TCP_SYN_RECV ||
+ 		sk->scanpair->state == TCP_SYN_SENT)
+ 		tcp_reset_xmit_timer(sk, TIME_NEWSCAN, 4*60*HZ); /* 4 minutes */
+ 	else
+ 		del_timer(&sk->retransmit_timer);
+ }
+ 
+ void handle_dupsynack(struct sock *sk, struct sk_buff *skb, struct tcphdr *th)
+ {
+ 	struct sock *sk2 = sk->scanpair;
+ 
+ 	/* only do this duplicate SYNACK transmission in the right state */
+ 	if (!sk2 || sk->state != TCP_ESTABLISHED)
+ 	{
+ 		printk("dupsynack dropping forward: wrong state %d\n",
+ 			sk->state);
+ 		return;
+ 	}
+ 	if (sk2->state == TCP_SYN_RECV)
+ 	{
+ 		/* We never received the ACK of the SYNACK.  Try again. */
+ 		if (sk2->write_seq != skb->seq+1)
+ 		{
+ 			printk("dupsynack dropping forward: ISS %d+1 != %d\n",
+ 				(int) skb->seq, (int) sk2->write_seq);
+ 			return;
+ 		}
+ 		make_syn_pkt(sk->scanpair, skb, th);
+ 	}
+ 	else
+ 	{
+ 		/* The SYNACK sender did not receive the ACK of the */
+ 		/* SYNACK we previously forwarded.  Send a new ACK. */
+ 		tcp_send_ack(sk);
+ 	}
  }
*** linux-2.0.28/net/ipv4/tcp_timer.c	Sun Jun  2 03:23:13 1996
--- KERNEL/net/ipv4/tcp_timer.c	Mon Nov 24 22:55:24 1997
***************
*** 26,31 ****
--- 26,33 ----
  
  #include <net/tcp.h>
  
+ extern void tcp_preclose(struct sock *sk);
+ 
  void tcp_delack_timer(unsigned long data)
  {
  	tcp_send_ack((struct sock *) data);
***************
*** 38,43 ****
--- 40,51 ----
  void tcp_reset_xmit_timer(struct sock *sk, int why, unsigned long when)
  {
  	del_timer(&sk->retransmit_timer);
+ 
+ #ifdef CONFIG_WEBSHIELD_DEBUG
+ 	if(sk->dontcare)
+ 		log_sockevent(sk, 14, why, when);
+ #endif /* CONFIG_WEBSHIELD_DEBUG */
+ 
  	sk->ip_xmit_timeout = why;
  	if (why == TIME_WRITE) {
  		/* In this case we want to timeout on the first packet
***************
*** 178,183 ****
--- 186,192 ----
  		sk->error_report(sk);
  		del_timer(&sk->retransmit_timer);
  		tcp_statistics.TcpAttemptFails++;	/* Is this right ??? - FIXME - */
+ 		tcp_preclose(sk);
  		tcp_set_state(sk,TCP_CLOSE);
  		/* Don't FIN, we got nothing back */
  		return 0;
***************
*** 206,211 ****
--- 215,221 ----
  			/*
  			 *	Clean up time.
  			 */
+ 			tcp_preclose(sk);
  			tcp_set_state(sk, TCP_CLOSE);
  			return 0;
  		}
***************
*** 292,297 ****
--- 302,331 ----
  		sk->retransmits++;
  		sk->prot->retransmits++;
  		tcp_write_timeout(sk);
+ 		break;
+ 
+ 	case TIME_NEWSCAN:
+ 		/*
+ 		 * Kill both socks in an incomplete connection.
+ 		 * This timer is only set on the client sock.
+ 		 * Since the scanpair has no reference, mark it dead.
+ 		 */
+ 		if (sk->scanpair) {
+ 			struct sock *sk2 = sk->scanpair;
+ 			sk2->dead = 1;
+ 			tcp_preclose(sk2);
+ 			tcp_set_state(sk2, TCP_CLOSE);
+ 		}
+ 		/*
+ 		 * On the listener queue, closed incomplete connections are
+ 		 * destroyed by the listener in inet_accept().
+ 		 */
+ 		tcp_preclose(sk);
+ 		if (!sk->dead) {
+ 			sk->state = TCP_CLOSE;
+ 			sk->state_change(sk);
+ 		} else
+ 			tcp_set_state(sk, TCP_CLOSE);
  		break;
  
  	default:
*** linux-2.0.28/net/ipv4/timer.c	Thu Feb 22 06:50:46 1996
--- KERNEL/net/ipv4/timer.c	Mon Nov 24 22:55:38 1997
***************
*** 87,92 ****
--- 87,93 ----
  {
  	struct sock *sk = (struct sock*)data;
  	int why = sk->timeout;
+ 	void tcp_preclose(struct sock *sk);
  
  	/* 
  	 * only process if socket is not in use
***************
*** 94,102 ****
  
  	if (sk->users)
  	{
  		sk->timer.expires = jiffies+HZ;
  		add_timer(&sk->timer);
! 		sti();
  		return;
  	}
  
--- 95,111 ----
  
  	if (sk->users)
  	{
+ 		static int flag = 0;
+ 
+ 		if (why == TIME_DONE && flag < 5) {
+ 			printk("TIME_DONE: sk[%u,%u](%p)->users=%d\n",
+ 				htons(sk->dummy_th.source),
+ 				htons(sk->dummy_th.dest), sk, sk->users);
+ 			flag++;
+ 		}
  		sk->timer.expires = jiffies+HZ;
  		add_timer(&sk->timer);
! 		sti(); /* BRYAN: this looks useless */
  		return;
  	}
  
***************
*** 115,120 ****
--- 124,130 ----
  	{
  		case TIME_DONE:
  			/* If the socket hasn't been closed off, re-try a bit later */
+ 			log_sockevent(sk, 10, sk->dead, sk->state);
  			if (!sk->dead) {
  				reset_timer(sk, TIME_DONE, TCP_DONE_TIME);
  				break;
***************
*** 139,144 ****
--- 149,155 ----
  
  		case TIME_CLOSE:
  			/* We've waited long enough, close the socket. */
+ 			tcp_preclose(sk);
  			sk->state = TCP_CLOSE;
  			delete_timer (sk);
  			if (!sk->dead)
*** linux-2.0.28/net/socket.c	Mon Jul 15 01:31:51 1996
--- KERNEL/net/socket.c	Thu Jul 31 12:02:51 1997
***************
*** 169,175 ****
   *	Obtains the first available file descriptor and sets it up for use. 
   */
  
! static int get_fd(struct inode *inode)
  {
  	int fd;
  
--- 169,175 ----
   *	Obtains the first available file descriptor and sets it up for use. 
   */
  
! int get_fd(struct inode *inode)
  {
  	int fd;
  
