*** 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 #include + #include #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 #include #include 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 #include #include + #include + + /* + * 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; iaddr_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 posoffset, 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 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 + 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 /* + * 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 #include #include + #include /* *************** *** 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 + 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;