## Automatically generated incremental diff ## From: linux-2.4.22-bk23 ## To: linux-2.4.22-bk24 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.4.22-bk23/Documentation/Configure.help linux-2.4.22-bk24/Documentation/Configure.help --- linux-2.4.22-bk23/Documentation/Configure.help 2003-09-23 03:06:40.000000000 -0700 +++ linux-2.4.22-bk24/Documentation/Configure.help 2003-09-23 03:07:02.000000000 -0700 @@ -6108,6 +6108,61 @@ It is safe to say N here for now. +The SCTP Protocol (EXPERIMENTAL) +CONFIG_IP_SCTP + Stream Control Transmission Protocol + + From RFC 2960 (http://www.ietf.org/rfc/rfc2960.txt) + + "SCTP is a reliable transport protocol operating on top of a + connectionless packet network such as IP. It offers the following + services to its users: + + -- acknowledged error-free non-duplicated transfer of user data, + -- data fragmentation to conform to discovered path MTU size, + -- sequenced delivery of user messages within multiple streams, + with an option for order-of-arrival delivery of individual user + messages, + -- optional bundling of multiple user messages into a single SCTP + packet, and + -- network-level fault tolerance through supporting of multi- + homing at either or both ends of an association." + + This protocol support is also available as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). The module will be called sctp. If you want to compile it + as a module, say M here and read . + + If in doubt, say N. + +SCTP: Use old checksum (Adler-32) +CONFIG_SCTP_ADLER32 + RCF2960 currently specifies the Adler-32 checksum algorithm for SCTP. + This has been deprecated and replaced by an algorithm now referred + to as crc32c. + + If you say Y, this will use the Adler-32 algorithm, this might be + useful for interoperation with downlevel peers. + + If unsure, say N. + +SCTP: Debug messages +CONFIG_SCTP_DBG_MSG + If you say Y, this will enable verbose debugging messages. + + If unsure, say N. However, if you are running into problems, use + this option to gather detailed trace information + +SCTP: Debug object counts +CONFIG_SCTP_DBG_OBJCNT + If you say Y, this will enable debugging support for counting the + type of objects that are currently allocated. This is useful for + identifying memory leaks. If the /proc filesystem is enabled this + debug information can be viewed by + 'cat /proc/net/sctp/sctp_dbg_objcnt' + + If unsure, say N + Kernel httpd acceleration CONFIG_KHTTPD The kernel httpd acceleration daemon (kHTTPd) is a (limited) web diff -urN linux-2.4.22-bk23/Makefile linux-2.4.22-bk24/Makefile --- linux-2.4.22-bk23/Makefile 2003-09-23 03:06:41.000000000 -0700 +++ linux-2.4.22-bk24/Makefile 2003-09-23 03:07:03.000000000 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 22 -EXTRAVERSION = -bk23 +EXTRAVERSION = -bk24 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -urN linux-2.4.22-bk23/arch/sh/Makefile linux-2.4.22-bk24/arch/sh/Makefile --- linux-2.4.22-bk23/arch/sh/Makefile 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/Makefile 2003-09-23 03:07:06.000000000 -0700 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1.1.1.2.2 2002/05/10 17:58:54 jzs Exp $ +# $Id: Makefile,v 1.1.1.1.2.4 2003/08/22 04:04:16 sugioka Exp $ # # This file is subject to the terms and conditions of the GNU General Public # License. See the file "COPYING" in the main directory of this archive @@ -87,7 +87,7 @@ FORCE: ; zImage: vmlinux - @$(MAKEBOOT) zImage + @$(MAKEBOOT) $@ compressed: zImage diff -urN linux-2.4.22-bk23/arch/sh/boot/compressed/Makefile linux-2.4.22-bk24/arch/sh/boot/compressed/Makefile --- linux-2.4.22-bk23/arch/sh/boot/compressed/Makefile 2000-10-02 11:57:33.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/boot/compressed/Makefile 2003-09-23 03:07:06.000000000 -0700 @@ -18,9 +18,9 @@ # # ZIMAGE_OFFSET is the load offset of the compression loader # -ZIMAGE_OFFSET = $(shell printf "0x%8x" $$[0x80000000+0x$(CONFIG_MEMORY_START)+0x200000+0x10000]) +ZIMAGE_OFFSET = $(shell printf "0x%8x" $$[0x80000000+0x$(CONFIG_MEMORY_START)+0x$(CONFIG_BOOT_LINK_OFFSET)]) -ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS) +ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS) all: vmlinux diff -urN linux-2.4.22-bk23/arch/sh/boot/compressed/head.S linux-2.4.22-bk24/arch/sh/boot/compressed/head.S --- linux-2.4.22-bk23/arch/sh/boot/compressed/head.S 2001-01-28 18:56:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/boot/compressed/head.S 2003-09-23 03:07:06.000000000 -0700 @@ -2,6 +2,7 @@ * linux/arch/sh/boot/compressed/head.S * * Copyright (C) 1999 Stuart Menefy + * Copyright (C) 2003 SUGIOKA Toshinobu */ .text @@ -14,7 +15,54 @@ mov.l init_sr, r1 ldc r1, sr - /* First clear BSS */ + /* Move myself to proper location if necessary */ + mova 1f, r0 + mov.l 1f, r2 + cmp/eq r2, r0 + bt clear_bss + sub r0, r2 + mov.l bss_start_addr, r0 + mov #0xe0, r1 + and r1, r0 ! align cache line + mov.l text_start_addr, r3 + mov r0, r1 + sub r2, r1 +3: + mov.l @r1, r4 + mov.l @(4,r1), r5 + mov.l @(8,r1), r6 + mov.l @(12,r1), r7 + mov.l @(16,r1), r8 + mov.l @(20,r1), r9 + mov.l @(24,r1), r10 + mov.l @(28,r1), r11 + mov.l r4, @r0 + mov.l r5, @(4,r0) + mov.l r6, @(8,r0) + mov.l r7, @(12,r0) + mov.l r8, @(16,r0) + mov.l r9, @(20,r0) + mov.l r10, @(24,r0) + mov.l r11, @(28,r0) +#ifdef CONFIG_CPU_SH4 + ocbwb @r0 +#endif + cmp/hi r3, r0 + add #-32, r0 + bt/s 3b + add #-32, r1 + mov.l 2f, r0 + jmp @r0 + nop + + .align 2 +1: .long 1b +2: .long clear_bss +text_start_addr: + .long startup + + /* Clear BSS */ +clear_bss: mov.l end_addr, r1 mov.l bss_start_addr, r2 mov #0, r0 diff -urN linux-2.4.22-bk23/arch/sh/config.in linux-2.4.22-bk24/arch/sh/config.in --- linux-2.4.22-bk23/arch/sh/config.in 2003-09-23 03:06:47.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/config.in 2003-09-23 03:07:06.000000000 -0700 @@ -28,6 +28,7 @@ comment 'Processor type and features' choice 'SuperH system type' \ "Generic CONFIG_SH_GENERIC \ + SH4-202-MicroDev CONFIG_SH_SH4202_MICRODEV \ SolutionEngine CONFIG_SH_SOLUTION_ENGINE \ SolutionEngine7751 CONFIG_SH_7751_SOLUTION_ENGINE \ SHMobileSolutionEngine CONFIG_SH_MOBILE_SOLUTION_ENGINE \ @@ -68,7 +69,9 @@ SH7708 CONFIG_CPU_SUBTYPE_SH7708 \ SH7709 CONFIG_CPU_SUBTYPE_SH7709 \ SH7750 CONFIG_CPU_SUBTYPE_SH7750 \ + SH7751R CONFIG_CPU_SUBTYPE_SH7751R \ SH7751 CONFIG_CPU_SUBTYPE_SH7751 \ + SH4-202 CONFIG_CPU_SUBTYPE_SH4_202 \ ST40RA/ST40STB1 CONFIG_CPU_SUBTYPE_ST40STB1 \ ST40GX1 CONFIG_CPU_SUBTYPE_ST40GX1" SH7708 if [ "$CONFIG_CPU_SUBTYPE_SH7300" = "y" ]; then @@ -95,6 +98,19 @@ define_bool CONFIG_CPU_SH3 n define_bool CONFIG_CPU_SH4 y fi +if [ "$CONFIG_CPU_SUBTYPE_SH7751R" = "y" ]; then + define_bool CONFIG_CPU_SH3 n + define_bool CONFIG_CPU_SH4 y + define_bool CONFIG_CPU_SUBTYPE_SH7751 y +fi +if [ "$CONFIG_CPU_SUBTYPE_SH4_202" = "y" ]; then + define_bool CONFIG_CPU_SH3 n + define_bool CONFIG_CPU_SH4 y +fi +if [ "$CONFIG_CPU_SUBTYPE_SH7751R" = "y" -o \ + "$CONFIG_CPU_SUBTYPE_SH4_202" = "y" ]; then + bool 'Use 2-way set associative caches' CONFIG_SH_CACHE_ASSOC +fi if [ "$CONFIG_CPU_SUBTYPE_ST40STB1" = "y" ]; then define_bool CONFIG_CPU_SH3 n define_bool CONFIG_CPU_SH4 y @@ -157,6 +173,9 @@ hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 hex 'Physical memory size' CONFIG_MEMORY_SIZE 00400000 fi + +hex 'Link address offset for booting' CONFIG_BOOT_LINK_OFFSET 00210000 + dep_bool 'Enable OC RAM zone (experimental)' CONFIG_SCRATCH_SPACE $CONFIG_EXPERIMENTAL if [ "$CONFIG_CPU_SUBTYPE_ST40" = "y" ]; then @@ -332,9 +351,9 @@ # source drivers/input/Config.in -if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then - source drivers/maple/Config.in -fi +#if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then +# source drivers/maple/Config.in +#fi mainmenu_option next_comment comment 'Character devices' diff -urN linux-2.4.22-bk23/arch/sh/kernel/Makefile linux-2.4.22-bk24/arch/sh/kernel/Makefile --- linux-2.4.22-bk23/arch/sh/kernel/Makefile 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/kernel/Makefile 2003-09-23 03:07:06.000000000 -0700 @@ -93,6 +93,9 @@ obj-$(CONFIG_SH_KEYWEST) += mach_keywest.o setup_keywest.o io_keywest.o machine-specific-objs += mach_keywest.o setup_keywest.o io_keywest.o +obj-$(CONFIG_SH_SH4202_MICRODEV)+= mach_microdev.o setup_microdev.o io_microdev.o irq_microdev.o +machine-specific-objs += mach_microdev.o setup_microdev.o io_microdev.o irq_microdev.o + # Doesn't compile well, so don't include in machine-specific-objs obj-$(CONFIG_HD64465) += setup_hd64465.o io_hd64465.o hd64465_gpio.o obj-$(CONFIG_SH_DMIDA) += mach_dmida.o diff -urN linux-2.4.22-bk23/arch/sh/kernel/entry.S linux-2.4.22-bk24/arch/sh/kernel/entry.S --- linux-2.4.22-bk23/arch/sh/kernel/entry.S 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/kernel/entry.S 2003-09-23 03:07:06.000000000 -0700 @@ -589,6 +589,7 @@ mov #0, k1 mov.b k1, @k0 #endif + mov.l @r15+, k2 ! restore EXPEVT mov k4, r15 rte nop @@ -678,6 +679,7 @@ 9: mov.l 3f, k1 ! ! Save the user registers on the stack. + mov.l k2, @-r15 ! EXPEVT add #-4, r15 ! placeholder ! sts.l macl, @-r15 diff -urN linux-2.4.22-bk23/arch/sh/kernel/fpu.c linux-2.4.22-bk24/arch/sh/kernel/fpu.c --- linux-2.4.22-bk23/arch/sh/kernel/fpu.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/kernel/fpu.c 2003-09-23 03:07:06.000000000 -0700 @@ -18,6 +18,14 @@ #include #include +/* The PR (precision) bit in the FP Status Register must be clear when + * an frchg instruction is executed, otherwise the instruction is undefined. + * Executing frchg with PR set causes a trap on some SH4 implementations. + */ + +#define FPSCR_RCHG 0x00000000 + + /* * Save FPU registers onto task structure. * Assume called with FPU enabled (SR.FD=0). @@ -61,9 +69,11 @@ "fmov.s fr3, @-%0\n\t" "fmov.s fr2, @-%0\n\t" "fmov.s fr1, @-%0\n\t" - "fmov.s fr0, @-%0" + "fmov.s fr0, @-%0\n\t" + "lds %2, fpscr\n\t" : /* no output */ : "r" ((char *)(&tsk->thread.fpu.hard.status)), + "r" (FPSCR_RCHG), "r" (FPSCR_INIT) : "memory"); @@ -112,7 +122,7 @@ "lds.l @%0+, fpscr\n\t" "lds.l @%0+, fpul\n\t" : /* no output */ - : "r" (&tsk->thread.fpu), "r" (FPSCR_INIT) + : "r" (&tsk->thread.fpu), "r" (FPSCR_RCHG) : "memory"); } @@ -160,9 +170,10 @@ "fsts fpul, fr13\n\t" "fsts fpul, fr14\n\t" "fsts fpul, fr15\n\t" - "frchg" + "frchg\n\t" + "lds %2, fpscr\n\t" : /* no output */ - : "r" (0), "r" (FPSCR_INIT)); + : "r" (0), "r" (FPSCR_RCHG), "r" (FPSCR_INIT)); } /** diff -urN linux-2.4.22-bk23/arch/sh/kernel/io_microdev.c linux-2.4.22-bk24/arch/sh/kernel/io_microdev.c --- linux-2.4.22-bk23/arch/sh/kernel/io_microdev.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/kernel/io_microdev.c 2003-09-23 03:07:06.000000000 -0700 @@ -0,0 +1,219 @@ +/* + * linux/arch/sh/kernel/io_microdev.c + * + * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com) + * + * SuperH SH4-202 MicroDev board support. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + */ + +#include +#include +#include + + + /* + * we need to have a 'safe' address to re-direct all I/O requests + * that we do not explicitly wish to handle. This safe address + * must have the following properies: + * + * * writes are ignored (no exception) + * * reads are benign (no side-effects) + * * accesses of width 1, 2 and 4-bytes are all valid. + * + * The Processor Version Register (PVR) has these properties. + */ +#define PVR 0xff000030 /* Processor Version Register */ + + +#define IO_LAN91C111 0x300ul /* I/O port for SMSC LAN91C111 Ethernet chip */ + +#define PORT2ADDR(x) (microdev_isa_port2addr(x)) + + +static inline void delay(void) +{ + ctrl_inw(0xa0000000); +} + +unsigned char microdev_inb(unsigned long port) +{ + return *(volatile unsigned char*)PORT2ADDR(port); +} + +unsigned short microdev_inw(unsigned long port) +{ + return *(volatile unsigned short*)PORT2ADDR(port); +} + +unsigned int microdev_inl(unsigned long port) +{ + return *(volatile unsigned int*)PORT2ADDR(port); +} + +void microdev_outb(unsigned char b, unsigned long port) +{ + /* + * There is a board feature with the current SH4-202 MicroDev + * in that the 2 byte enables (nBE0 and nBE1) are tied together (and to the + * Chip Select Line (Ethernet_CS)). Due to this conectivity, it is not possible + * to safely perform 8-bit writes to the Ethernet registers, as 16-bits + * will be consumed from the Data lines (corrupting the other byte). + * Hence, this function is written to impliment 16-bit read/modify/write + * for all byte-wide acceses. + * + * Note: there is no problem with byte READS (even or odd). + * + * Sean McGoogan - 16th June 2003. + */ + if ( (port>=IO_LAN91C111) && (port=IO_LAN91C111) && (offset - * Based largely on io_se.c - * - * I/O routine for SH-Mobile SolutionEngine. + * Copyright (C) 2003 YOSHII Takashi */ #include -#include -#include #include -#include +#include + +#define badio(fn, a) panic("bad i/o operation %s for %08lx.", #fn, a) + +struct iop { + unsigned long start, end; + unsigned long base; + struct iop *(*check)(struct iop *p, unsigned long port); + unsigned char (*inb)(struct iop *p, unsigned long port); + unsigned short (*inw)(struct iop *p, unsigned long port); + void (*outb)(struct iop *p, unsigned char value, unsigned long port); + void (*outw)(struct iop *p, unsigned short value, unsigned long port); +}; + +struct iop *simple_check(struct iop *p, unsigned long port){ + if((p->start <= port) && (port <= p->end)) + return p; + else + badio(check, port); +} + +unsigned short simple_inw(struct iop *p, unsigned long port){ + return *(unsigned short*)(p->base+port); +} + +void simple_outw(struct iop *p, unsigned short value, unsigned long port){ + *(unsigned short*)(p->base+port)=value; +} + +unsigned char bad_inb(struct iop *p, unsigned long port) +{ + badio(inb, port); +} + +void bad_outb(struct iop *p, unsigned char value, unsigned long port){ + badio(inw, port); +} + +/* MSTLANEX01 LAN at 0xb400:0000 */ +static struct iop laniop = { + .start = 0x200, // device is at 0x300, but start here for probing. + .end = 0x30f, + .base = 0xb4000000, + .check = simple_check, + .inb = bad_inb, + .inw = simple_inw, + .outb = bad_outb, + .outw = simple_outw, +}; + +static __inline__ struct iop *port2iop(unsigned long port){ + if(laniop.check(&laniop, port)) + return &laniop; + else + badio(check, port); /* XXX: dummy fallback routine? */ +} static inline void delay(void) { @@ -19,151 +69,117 @@ ctrl_inw(0xac000000); } -#define maybebadio(name,port) \ - printk("bad PC-like io %s for port 0x%lx at 0x%08lx\n", \ - #name, (port), (volatile unsigned long) __builtin_return_address(0)) - unsigned char shmse_inb(unsigned long port) { - if (PXSEG(port)){ - return *(volatile unsigned char *) port; - }else{ - maybebadio(inb, port); - return 0; - } + struct iop *p=port2iop(port); + return (p->inb)(p, port); } unsigned char shmse_inb_p(unsigned long port) { - unsigned char v; - - if (PXSEG(port)){ - v = *(volatile unsigned char *)port; - }else{ - maybebadio(inb_p, port); - return 0; - } + unsigned char v=shmse_inb(port); delay(); return v; } unsigned short shmse_inw(unsigned long port) { - if (PXSEG(port)){ - return *(volatile unsigned short *) port; - }else{ - maybebadio(inw, port); - return 0; - } + struct iop *p=port2iop(port); + return (p->inw)(p, port); } unsigned int shmse_inl(unsigned long port) { - if (PXSEG(port)){ - return *(volatile unsigned short *) port; - }else{ - maybebadio(inl, port); - return 0; - } + badio(inl, port); } void shmse_outb(unsigned char value, unsigned long port) { - if (PXSEG(port)){ - *(volatile unsigned char *)port = value; - }else{ - maybebadio(outb, port); - } + struct iop *p=port2iop(port); + (p->outb)(p, value, port); } void shmse_outb_p(unsigned char value, unsigned long port) { - if (PXSEG(port)){ - *(volatile unsigned char *)port = value; - }else{ - maybebadio(outb_p, port); - } + shmse_outb(value, port); delay(); } void shmse_outw(unsigned short value, unsigned long port) { - if (PXSEG(port)){ - *(volatile unsigned short *)port = value; - }else{ - maybebadio(outw, port); - } + struct iop *p=port2iop(port); + (p->outw)(p, value, port); } void shmse_outl(unsigned int value, unsigned long port) { - if (PXSEG(port)){ - *(volatile unsigned long *)port = value; - }else{ - maybebadio(outl, port); - } + badio(outl, port); } void shmse_insb(unsigned long port, void *addr, unsigned long count) { - unsigned char *p = addr; - while (count--) *p++ = shmse_inb(port); + unsigned char *a = addr; + struct iop *p=port2iop(port); + while (count--) *a++ = (p->inb)(p, port); } void shmse_insw(unsigned long port, void *addr, unsigned long count) { - unsigned short *p = addr; - while (count--) *p++ = shmse_inw(port); + unsigned short *a = addr; + struct iop *p=port2iop(port); + while (count--) *a++ = (p->inw)(p, port); } void shmse_insl(unsigned long port, void *addr, unsigned long count) { - maybebadio(insl, port); + badio(insl, port); } void shmse_outsb(unsigned long port, const void *addr, unsigned long count) { - unsigned char *p = (unsigned char *)addr; - while (count--) shmse_outb(*p++, port); + unsigned char *a = (unsigned char*)addr; + struct iop *p=port2iop(port); + while (count--) (p->outb)(p, *a++, port); } void shmse_outsw(unsigned long port, const void *addr, unsigned long count) { - unsigned short *p = (unsigned short *)addr; - while (count--) shmse_outw(*p++, port); + unsigned short *a = (unsigned short*)addr; + struct iop *p=port2iop(port); + while (count--) (p->outw)(p, *a++, port); } void shmse_outsl(unsigned long port, const void *addr, unsigned long count) { - maybebadio(outsw, port); + badio(outsw, port); } unsigned char shmse_readb(unsigned long addr) { - return *(volatile unsigned char*)addr; + badio(readb, addr); } unsigned short shmse_readw(unsigned long addr) { - return *(volatile unsigned short*)addr; + badio(readw, addr); } unsigned int shmse_readl(unsigned long addr) { - return *(volatile unsigned long*)addr; + badio(readl, addr); } void shmse_writeb(unsigned char b, unsigned long addr) { - *(volatile unsigned char*)addr = b; + badio(writeb, addr); } void shmse_writew(unsigned short b, unsigned long addr) { - *(volatile unsigned short*)addr = b; + badio(writew, addr); } void shmse_writel(unsigned int b, unsigned long addr) { - *(volatile unsigned long*)addr = b; + badio(writel, addr); } diff -urN linux-2.4.22-bk23/arch/sh/kernel/irq_microdev.c linux-2.4.22-bk24/arch/sh/kernel/irq_microdev.c --- linux-2.4.22-bk23/arch/sh/kernel/irq_microdev.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/kernel/irq_microdev.c 2003-09-23 03:07:06.000000000 -0700 @@ -0,0 +1,158 @@ +/* + * linux/arch/sh/kernel/irq_microdev.c + * + * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com) + * + * SuperH SH4-202 MicroDev board support. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + */ + +#include +#include +#include + +#include +#include +#include + +#define NUM_EXTERNAL_IRQS 16 /* IRL0 .. IRL15 */ + + +static const struct +{ + unsigned char fpgaIrq; + unsigned char mapped; + const char *name; +} fpgaIrqTable[NUM_EXTERNAL_IRQS] = +{ + { 0, 0, "unused" }, /* IRQ #0 IRL=15 0x200 */ + { 0, 0, "unused" }, /* IRQ #1 IRL=14 0x220 */ + { 0, 0, "unused" }, /* IRQ #2 IRL=13 0x240 */ + { 18, 1, "Ethernet" }, /* IRQ #3 IRL=12 0x260 */ + { 0, 0, "unused" }, /* IRQ #4 IRL=11 0x280 */ + { 0, 0, "unused" }, /* IRQ #5 IRL=10 0x2a0 */ + { 0, 0, "unused" }, /* IRQ #6 IRL=9 0x2c0 */ + { 0, 0, "unused" }, /* IRQ #7 IRL=8 0x2e0 */ + { 8, 1, "PCI INTA" }, /* IRQ #8 IRL=7 0x300 */ + { 9, 1, "PCI INTB" }, /* IRQ #9 IRL=6 0x320 */ + { 10, 1, "PCI INTC" }, /* IRQ #10 IRL=5 0x340 */ + { 11, 1, "PCI INTD" }, /* IRQ #11 IRL=4 0x360 */ + { 0, 0, "unused" }, /* IRQ #12 IRL=3 0x380 */ + { 0, 0, "unused" }, /* IRQ #13 IRL=2 0x3a0 */ + { 0, 0, "unused" }, /* IRQ #14 IRL=1 0x3c0 */ + { 0, 0, "unused" }, /* IRQ #15 IRL=0 0x3e0 */ +}; + +static void enable_microdev_irq(unsigned int irq); +static void disable_microdev_irq(unsigned int irq); + + /* shutdown is same as "disable" */ +#define shutdown_microdev_irq disable_microdev_irq + +static void mask_and_ack_microdev(unsigned int); +static void end_microdev_irq(unsigned int irq); + +static unsigned int startup_microdev_irq(unsigned int irq) +{ + enable_microdev_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type microdev_irq_type = { + "MicroDev-IRQ", + startup_microdev_irq, + shutdown_microdev_irq, + enable_microdev_irq, + disable_microdev_irq, + mask_and_ack_microdev, + end_microdev_irq +}; + +static void disable_microdev_irq(unsigned int irq) +{ + unsigned int flags; + unsigned int fpgaIrq; + + if (irq >= NUM_EXTERNAL_IRQS) return; + if (!fpgaIrqTable[irq].mapped) return; + + fpgaIrq = fpgaIrqTable[irq].fpgaIrq; + + /* disable interrupts */ + save_and_cli(flags); + + /* disable interupts on the FPGA INTC register */ + ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTDSB_REG); + + /* restore interrupts */ + restore_flags(flags); +} + +static void enable_microdev_irq(unsigned int irq) +{ + unsigned long priorityReg, priorities, pri; + unsigned int flags; + unsigned int fpgaIrq; + + + if (irq >= NUM_EXTERNAL_IRQS) return; + if (!fpgaIrqTable[irq].mapped) return; + + pri = 15 - irq; + + fpgaIrq = fpgaIrqTable[irq].fpgaIrq; + priorityReg = MICRODEV_FPGA_INTPRI_REG(fpgaIrq); + + /* disable interrupts */ + save_and_cli(flags); + + /* set priority for the interrupt */ + priorities = ctrl_inl(priorityReg); + priorities &= ~MICRODEV_FPGA_INTPRI_MASK(fpgaIrq); + priorities |= MICRODEV_FPGA_INTPRI_LEVEL(fpgaIrq, pri); + ctrl_outl(priorities, priorityReg); + + /* enable interupts on the FPGA INTC register */ + ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTENB_REG); + + /* restore interrupts */ + restore_flags(flags); +} + + /* This functions sets the desired irq handler to be a MicroDev type */ +static void __init make_microdev_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = µdev_irq_type; + disable_microdev_irq(irq); +} + +static void mask_and_ack_microdev(unsigned int irq) +{ + disable_microdev_irq(irq); +} + +static void end_microdev_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + { + enable_microdev_irq(irq); + } +} + +extern void __init init_microdev_irq(void) +{ + int i; + + /* disable interupts on the FPGA INTC register */ + ctrl_outl(~0ul, MICRODEV_FPGA_INTDSB_REG); + + for (i = 0; i < NUM_EXTERNAL_IRQS; i++) + { + make_microdev_irq(i); + } +} + + diff -urN linux-2.4.22-bk23/arch/sh/kernel/mach_microdev.c linux-2.4.22-bk24/arch/sh/kernel/mach_microdev.c --- linux-2.4.22-bk23/arch/sh/kernel/mach_microdev.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/kernel/mach_microdev.c 2003-09-23 03:07:06.000000000 -0700 @@ -0,0 +1,77 @@ +/* + * linux/arch/sh/kernel/mach_microdev.c + * + * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com) + * + * Machine vector for the SuperH SH4-202 MicroDev board. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + */ + +#include +#include + +#include +#include +#include + +#include +#include + +void setup_microdev(void); + +/* + * The Machine Vector + */ + +struct sh_machine_vector mv_sh4202_microdev __initmv = { + mv_name: "SH4-202 MicroDev", + + mv_nr_irqs: 72, /* QQQ need to check this - use the MACRO */ + + mv_inb: microdev_inb, + mv_inw: microdev_inw, + mv_inl: microdev_inl, + mv_outb: microdev_outb, + mv_outw: microdev_outw, + mv_outl: microdev_outl, + + mv_inb_p: microdev_inb_p, + mv_inw_p: microdev_inw_p, + mv_inl_p: microdev_inl_p, + mv_outb_p: microdev_outb_p, + mv_outw_p: microdev_outw_p, + mv_outl_p: microdev_outl_p, + + mv_insb: microdev_insb, + mv_insw: microdev_insw, + mv_insl: microdev_insl, + mv_outsb: microdev_outsb, + mv_outsw: microdev_outsw, + mv_outsl: microdev_outsl, + + mv_readb: generic_readb, + mv_readw: generic_readw, + mv_readl: generic_readl, + mv_writeb: generic_writeb, + mv_writew: generic_writew, + mv_writel: generic_writel, + + mv_ioremap: generic_ioremap, + mv_iounmap: generic_iounmap, + + mv_isa_port2addr: microdev_isa_port2addr, + + mv_init_arch: setup_microdev, + + mv_init_irq: init_microdev_irq, + + mv_rtc_gettimeofday: sh_rtc_gettimeofday, + mv_rtc_settimeofday: sh_rtc_settimeofday, + + mv_hw_sh4202_microdev: 1, +}; +ALIAS_MV(sh4202_microdev) + diff -urN linux-2.4.22-bk23/arch/sh/kernel/setup_microdev.c linux-2.4.22-bk24/arch/sh/kernel/setup_microdev.c --- linux-2.4.22-bk23/arch/sh/kernel/setup_microdev.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/kernel/setup_microdev.c 2003-09-23 03:07:06.000000000 -0700 @@ -0,0 +1,37 @@ +/* + * linux/arch/sh/kernel/setup_microdev.c + * + * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com) + * + * SuperH SH4-202 MicroDev board support. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + */ + +#include +#include +#include + + /* General-Purpose base address on CPU-board FPGA */ +#define MICRODEV_FPGA_GP_BASE 0xa6100000ul + + /* Address of Cache Control Register */ +#define CCR 0xff00001cul + +/* + * Initialize the board + */ +void __init setup_microdev(void) +{ + int * const fpgaRevisionRegister = (int*)(MICRODEV_FPGA_GP_BASE + 0x8ul); + const int fpgaRevision = *fpgaRevisionRegister; + int * const CacheControlRegister = (int*)CCR; + + printk("SuperH SH4-202 MicroDev board (FPGA rev: 0x%0x, CCR: 0x%0x)\n", + fpgaRevision, *CacheControlRegister); + + return; +} + + diff -urN linux-2.4.22-bk23/arch/sh/kernel/setup_shmse.c linux-2.4.22-bk24/arch/sh/kernel/setup_shmse.c --- linux-2.4.22-bk23/arch/sh/kernel/setup_shmse.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/kernel/setup_shmse.c 2003-09-23 03:07:06.000000000 -0700 @@ -15,6 +15,12 @@ void __init init_shmse_IRQ(void) { + ctrl_outw(0x0028, 0xb0a00000); // mode set [active low]. + ctrl_outw(0x000a, INTC_ICR1); // IRQ mode; IRQ0,1 enable. + /* PC_IRQ[0-3] -> IRQ0 (32) */ + make_ipr_irq( IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, 0x0f-IRQ0_IRQ); + /* A_IRQ[0-3] -> IRQ1 (33) */ + make_ipr_irq( IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, 0x0f-IRQ1_IRQ); } void __init setup_shmse(void) diff -urN linux-2.4.22-bk23/arch/sh/kernel/sh_ksyms.c linux-2.4.22-bk24/arch/sh/kernel/sh_ksyms.c --- linux-2.4.22-bk23/arch/sh/kernel/sh_ksyms.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/kernel/sh_ksyms.c 2003-09-23 03:07:06.000000000 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include extern void dump_thread(struct pt_regs *, struct user *); @@ -38,6 +39,7 @@ /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); +EXPORT_SYMBOL(csum_partial_copy_generic); EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strpbrk); @@ -45,6 +47,7 @@ EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strcat); EXPORT_SYMBOL(strncat); @@ -81,6 +84,8 @@ EXPORT_SYMBOL(__down_interruptible); EXPORT_SYMBOL(__down_trylock); +/* Delay loops */ +EXPORT_SYMBOL(__ndelay); EXPORT_SYMBOL(__udelay); EXPORT_SYMBOL(__const_udelay); @@ -105,3 +110,4 @@ EXPORT_SYMBOL(flush_dcache_page); #endif EXPORT_SYMBOL(flush_tlb_page); +EXPORT_SYMBOL(__xdiv64_32); diff -urN linux-2.4.22-bk23/arch/sh/lib/Makefile linux-2.4.22-bk24/arch/sh/lib/Makefile --- linux-2.4.22-bk23/arch/sh/lib/Makefile 2001-09-08 12:29:09.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/lib/Makefile 2003-09-23 03:07:06.000000000 -0700 @@ -4,7 +4,7 @@ L_TARGET = lib.a obj-y = delay.o memcpy.o memset.o memmove.o memchr.o old-checksum.o \ - checksum.o strcasecmp.o strlen.o + checksum.o strcasecmp.o strlen.o udivdi3.o div64.o USE_STANDARD_AS_RULE := true diff -urN linux-2.4.22-bk23/arch/sh/lib/div64.S linux-2.4.22-bk24/arch/sh/lib/div64.S --- linux-2.4.22-bk23/arch/sh/lib/div64.S 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/lib/div64.S 2003-09-23 03:07:06.000000000 -0700 @@ -0,0 +1,46 @@ +/* + * unsigned long long __xdiv64_32(unsigned long long n, unsigned long d); + */ + +#include + +.text +ENTRY(__xdiv64_32) +#ifdef __LITTLE_ENDIAN__ + mov r4, r0 + mov r5, r1 +#else + mov r4, r1 + mov r5, r0 +#endif + cmp/hs r6, r1 + bf.s 1f + mov #0, r2 + + mov r1, r2 + mov #0, r3 + div0u + .rept 32 + rotcl r2 + div1 r6, r3 + .endr + rotcl r2 + mul.l r6, r2 + sts macl, r3 + sub r3, r1 +1: + div0u + .rept 32 + rotcl r0 + div1 r6, r1 + .endr +#ifdef __LITTLE_ENDIAN__ + mov r2, r1 + rts + rotcl r0 +#else + rotcl r0 + mov r0, r1 + rts + mov r2, r0 +#endif diff -urN linux-2.4.22-bk23/arch/sh/lib/udivdi3.c linux-2.4.22-bk24/arch/sh/lib/udivdi3.c --- linux-2.4.22-bk23/arch/sh/lib/udivdi3.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/arch/sh/lib/udivdi3.c 2003-09-23 03:07:06.000000000 -0700 @@ -0,0 +1,16 @@ +/* + * Simple __udivdi3 function which doesn't use FPU. + */ + +#include + +extern u64 __xdiv64_32(u64 n, u32 d); +extern void panic(const char * fmt, ...); + +u64 __udivdi3(u64 n, u64 d) +{ + if (d & ~0xffffffff) + panic("Need true 64-bit/64-bit division"); + return __xdiv64_32(n, (u32)d); +} + diff -urN linux-2.4.22-bk23/arch/sh/mm/__clear_user_page-sh4.S linux-2.4.22-bk24/arch/sh/mm/__clear_user_page-sh4.S --- linux-2.4.22-bk23/arch/sh/mm/__clear_user_page-sh4.S 2001-09-08 12:29:09.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/mm/__clear_user_page-sh4.S 1969-12-31 16:00:00.000000000 -0800 @@ -1,49 +0,0 @@ -/* $Id$ - * - * __clear_user_page implementation of SuperH - * - * Copyright (C) 2001 Niibe Yutaka & Kaz Kojima - * - */ - -/* - * __clear_user_page - * @to: P1 address (with same color) - * @orig_to: P1 address - * - * void __clear_user_page(void *to, void *orig_to) - */ - -/* - * r0 --- scratch - * r4 --- to - * r5 --- orig_to - * r6 --- to + 4096 - */ -#include -ENTRY(__clear_user_page) - mov r4,r6 - mov.w .L4096,r0 - add r0,r6 - mov #0,r0 - ! -1: ocbi @r5 - add #32,r5 - movca.l r0,@r4 - mov r4,r1 - add #32,r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - add #28,r4 - cmp/eq r6,r4 - bf/s 1b - ocbwb @r1 - ! - rts - nop -.L4096: .word 4096 diff -urN linux-2.4.22-bk23/arch/sh/mm/__copy_user_page-sh4.S linux-2.4.22-bk24/arch/sh/mm/__copy_user_page-sh4.S --- linux-2.4.22-bk23/arch/sh/mm/__copy_user_page-sh4.S 2001-09-08 12:29:09.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/mm/__copy_user_page-sh4.S 1969-12-31 16:00:00.000000000 -0800 @@ -1,69 +0,0 @@ -/* $Id: __copy_user_page-sh4.S,v 1.1 2001/07/23 09:02:17 gniibe Exp $ - * - * __copy_user_page implementation of SuperH - * - * Copyright (C) 2001 Niibe Yutaka & Kaz Kojima - * - */ - -/* - * __copy_user_page - * @to: P1 address (with same color) - * @from: P1 address - * @orig_to: P1 address - * - * void __copy_user_page(void *to, void *from, void *orig_to) - */ - -/* - * r0, r1, r2, r3, r4, r5, r6, r7 --- scratch - * r8 --- from + 4096 - * r9 --- orig_to - * r10 --- to - * r11 --- from - */ -#include -ENTRY(__copy_user_page) - mov.l r8,@-r15 - mov.l r9,@-r15 - mov.l r10,@-r15 - mov.l r11,@-r15 - mov r4,r10 - mov r5,r11 - mov r6,r9 - mov r5,r8 - mov.w .L4096,r0 - add r0,r8 - ! -1: ocbi @r9 - add #32,r9 - mov.l @r11+,r0 - mov.l @r11+,r1 - mov.l @r11+,r2 - mov.l @r11+,r3 - mov.l @r11+,r4 - mov.l @r11+,r5 - mov.l @r11+,r6 - mov.l @r11+,r7 - movca.l r0,@r10 - mov r10,r0 - add #32,r10 - mov.l r7,@-r10 - mov.l r6,@-r10 - mov.l r5,@-r10 - mov.l r4,@-r10 - mov.l r3,@-r10 - mov.l r2,@-r10 - mov.l r1,@-r10 - ocbwb @r0 - cmp/eq r11,r8 - bf/s 1b - add #28,r10 - ! - mov.l @r15+,r11 - mov.l @r15+,r10 - mov.l @r15+,r9 - mov.l @r15+,r8 - rts - nop -.L4096: .word 4096 diff -urN linux-2.4.22-bk23/arch/sh/mm/cache-sh4.c linux-2.4.22-bk24/arch/sh/mm/cache-sh4.c --- linux-2.4.22-bk23/arch/sh/mm/cache-sh4.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/mm/cache-sh4.c 2003-09-23 03:07:06.000000000 -0700 @@ -32,8 +32,17 @@ #define CCR_CACHE_ICI 0x0800 /* IC Invalidate */ #define CCR_CACHE_IIX 0x8000 /* IC Index Enable */ -/* Default CCR setup: 8k+16k-byte cache,P1-wb,enable */ -#define CCR_CACHE_VAL (CCR_CACHE_ICE|CCR_CACHE_CB|CCR_CACHE_OCE) + + +#if defined(CONFIG_SH_CACHE_ASSOC) +#define CCR_CACHE_EMODE 0x80000000 +/* CCR setup for associative mode: 16k+32k 2-way, P1 copy-back, enable */ +#define CCR_CACHE_VAL (CCR_CACHE_EMODE|CCR_CACHE_ENABLE|CCR_CACHE_CB) +#else +/* Default CCR setup: 8k+16k-byte cache, P1-copy-back, enable */ +#define CCR_CACHE_VAL (CCR_CACHE_ENABLE|CCR_CACHE_CB) +#endif + #define CCR_CACHE_INIT (CCR_CACHE_VAL|CCR_CACHE_OCI|CCR_CACHE_ICI) #define CCR_CACHE_ENABLE (CCR_CACHE_OCE|CCR_CACHE_ICE) @@ -43,7 +52,7 @@ #define CACHE_UPDATED 2 #define CACHE_ASSOC 8 -#define CACHE_OC_WAY_SHIFT 13 +#define CACHE_OC_WAY_SHIFT 14 #define CACHE_IC_WAY_SHIFT 13 #define CACHE_OC_ENTRY_SHIFT 5 #define CACHE_IC_ENTRY_SHIFT 5 @@ -53,6 +62,8 @@ #define CACHE_IC_NUM_ENTRIES 256 #define CACHE_OC_NUM_ENTRIES 512 +#define CACHE_NUM_WAYS 2 + static void __init detect_cpu_and_cache_system(void) { @@ -60,6 +71,8 @@ cpu_data->type = CPU_ST40; #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) cpu_data->type = CPU_SH7750; +#elif defined(CONFIG_CPU_SUBTYPE_SH4_202) + cpu_data->type = CPU_SH4202; #else #error Unknown SH4 CPU type #endif @@ -80,6 +93,26 @@ */ unsigned long addr, data; +#if defined(CONFIG_SH_CACHE_ASSOC) + unsigned long way; + + for (way = 0; way <= CACHE_NUM_WAYS; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + for (addr = CACHE_OC_ADDRESS_ARRAY + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + waybit + + (CACHE_OC_NUM_ENTRIES << + CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + + data = ctrl_inl(addr); + + if ((data & (CACHE_UPDATED|CACHE_VALID)) + == (CACHE_UPDATED|CACHE_VALID)) + ctrl_outl(data & ~CACHE_UPDATED, addr); + } + } +#else for (addr = CACHE_OC_ADDRESS_ARRAY; addr < (CACHE_OC_ADDRESS_ARRAY+ (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); @@ -89,6 +122,7 @@ == (CACHE_UPDATED|CACHE_VALID)) ctrl_outl(data & ~CACHE_UPDATED, addr); } +#endif } ctrl_outl(CCR_CACHE_INIT, CCR); @@ -215,6 +249,12 @@ save_and_cli(flags); jump_to_P2(); ctrl_outl(0, index); /* Clear out Valid-bit */ + +#if defined(CONFIG_SH_CACHE_ASSOC) + /* Must invalidate both ways for associative cache */ + ctrl_outl(0, index | (1 << CACHE_IC_WAY_SHIFT)); +#endif + back_to_P1(); restore_flags(flags); } @@ -225,7 +265,7 @@ unsigned long flags; extern void __flush_cache_4096(unsigned long addr, unsigned long phys, unsigned long exec_offset); -#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40) +#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40) || defined(CONFIG_CPU_SUBTYPE_SH4_202) if (start >= CACHE_OC_ADDRESS_ARRAY) { /* * SH7751 and ST40 have no restriction to handle cache. @@ -481,3 +521,118 @@ up(&p3map_sem[(address & CACHE_ALIAS)>>12]); } } + + +/****************************************************************************/ + +#if defined(CONFIG_SH_CACHE_ASSOC) +/* + * It is no possible to use the approach implement in clear_page.S when we + * are in 2-way set associative mode as it would only clear half the cache, in + * general. For the moment we simply implement it as a iteration through the + * cache flushing both ways, this in itself is not optimial as the delay latency + * for interupts is probably longer than necessary! + * + * benedict.gaster.superh.com + */ +void __flush_dcache_all(void) +{ + unsigned long flags; + unsigned long addr; + unsigned long way; + + save_and_cli(flags); +#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202) + jump_to_P2(); +#endif + /* Clear the U and V bits for each line and each way. On SH-4, this + * causes write-back if both U and V are set before the address write. + */ + for (way = 0; way <= 1; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + waybit + + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + ctrl_outl(0, addr); + } + } + +#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202) + back_to_P1(); +#endif + restore_flags(flags); +} + +void flush_cache_4096_all(unsigned long start) +{ + unsigned long phys = PHYSADDR(start); + + /* Loop all the D-cache */ + flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys); +} +#endif + + + + + + +/****************************************************************************/ + +#if defined(CONFIG_SH_CACHE_ASSOC) +/* + * It is no possible to use the approach implement in clear_page.S when we + * are in 2-way set associative mode as it would only clear half the cache, in + * general. For the moment we simply implement it as a iteration through the + * cache flushing both ways, this in itself is not optimial as the delay latency + * for interupts is probably longer than necessary! + * + * benedict.gaster.superh.com + */ +void __flush_dcache_all(void) +{ + unsigned long flags; + unsigned long addr; + unsigned long way; + + save_and_cli(flags); +#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202) + jump_to_P2(); +#endif + /* Clear the U and V bits for each line and each way. On SH-4, this + * causes write-back if both U and V are set before the address write. + */ + for (way = 0; way <= 1; ++way) { + unsigned long waybit = way << CACHE_OC_WAY_SHIFT; + + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY + waybit; + addr < (CACHE_OC_ADDRESS_ARRAY + waybit + + (CACHE_OC_NUM_ENTRIES << CACHE_OC_ENTRY_SHIFT)); + addr += (1 << CACHE_OC_ENTRY_SHIFT)) { + ctrl_outl(0, addr); + } + } + +#if !defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202) + back_to_P1(); +#endif + restore_flags(flags); +} + +void flush_cache_4096_all(unsigned long start) +{ + unsigned long phys = PHYSADDR(start); + + /* Loop all the D-cache */ + flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys); +} +#endif + + + + + diff -urN linux-2.4.22-bk23/arch/sh/mm/clear_page.S linux-2.4.22-bk24/arch/sh/mm/clear_page.S --- linux-2.4.22-bk23/arch/sh/mm/clear_page.S 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh/mm/clear_page.S 2003-09-23 03:07:06.000000000 -0700 @@ -194,6 +194,40 @@ nop .L4096: .word 4096 +/* + * __flush_cache_4096 + * + * Flush the page at the specified physical address by writing to to + * the memory mapped address array. + * The offset into the memory mapped cache array selects the `color' of the + * virtual addresses which will be checked. + * Lower two bits of phys control the operation (invalidate/write-back). + * + * void __flush_cache_4096(unsigned long addr, unsigned long phys, + * unsigned long exec_offset); + * + * @addr: address of the memory mapped cache address array + * @phys: P1 address to be flushed + * @exec_offset: set to 0x20000000 if the flush needs to be executed from P2 + * (ie from uncached memory), otherwise 0. + */ + +/* + * Updated for the 2-way associative cache option on the SH-4-202 and SH7751R. + * + * The current implmentation simply adds an additional loop to flush the + * other way, but this could be improved by merging both loops to handle the + * flushing of boths ways with one iteration. + * + * benedict.gaster@superh.com + */ + +/* + * r4 --- addr + * r5 --- phys + * r6 --- exec_offset + */ + ENTRY(__flush_cache_4096) mov.l 1f,r3 add r6,r3 @@ -206,6 +240,9 @@ .align 2 1: .long 2f 2: +#if defined (CONFIG_SH_CACHE_ASSOC) + mov r5, r3 +#endif .rept 32 mov.l r5,@r0 mov.l r5,@(32,r0) @@ -214,6 +251,21 @@ add r2,r5 add r2,r0 .endr + +#if defined (CONFIG_SH_CACHE_ASSOC) + mov r4, r0 + mov #0x40, r1 ! set bit 14 in r0 to imply 2 way. + shll8 r1 + or r1, r0 + .rept 32 + mov.l r3,@r0 + mov.l r3,@(32,r0) + mov.l r3,@(r0,r6) + mov.l r3,@(r0,r7) + add r2,r3 + add r2,r0 + .endr +#endif nop nop nop @@ -222,7 +274,8 @@ nop nop rts - nop + nop + ENTRY(__flush_dcache_all) mov.l 2f,r0 @@ -233,6 +286,46 @@ or r1,r2 mov #32,r3 shll2 r3 + +! TODO : make this be dynamically selected based on CPU probing rather than assembled-in + +#if defined (CONFIG_SH_CACHE_ASSOC) + mov #0x40, r5 + shll8 r5 + add r4, r5 ! r5 = r4 + 16k +1: + ldc r2,sr ! set BL bit + movca.l r0,@r4 + movca.l r0,@r5 + ocbi @r4 + add #32,r4 + ocbi @r5 + add #32,r5 + movca.l r0,@r4 + movca.l r0,@r5 + ocbi @r4 + add #32,r4 + ocbi @r5 + add #32,r5 + movca.l r0,@r4 + movca.l r0,@r5 + ocbi @r4 + add #32,r4 + ocbi @r5 + add #32,r5 + movca.l r0,@r4 + movca.l r0,@r5 + ocbi @r4 + add #32, r4 + ocbi @r5 + ldc r1,sr ! restore SR + dt r3 + bf/s 1b + add #32,r5 + + rts + nop +#else 1: ldc r2,sr ! set BL bit movca.l r0,@r4 @@ -253,8 +346,11 @@ rts nop +#endif /* CONFIG_SH_CACHE_ASSOC */ + .align 2 2: .long 0xffffc000 + 3: .long SYMBOL_NAME(empty_zero_page) 4: .long 0x10000000 ! BL bit @@ -268,28 +364,73 @@ mov.l 4f,r2 or r1,r2 mov #32,r3 +! TODO : make this be dynamically selected based on CPU probing rather than assembled-in + +#if defined (CONFIG_SH_CACHE_ASSOC) + mov #0x40, r5 + shll8 r5 + add r4, r5 ! r5 = r4 + 16k 1: ldc r2,sr ! set BL bit movca.l r0,@r4 + movca.l r0,@r5 ocbi @r4 add #32,r4 + ocbi @r5 + add #32,r5 movca.l r0,@r4 + movca.l r0,@r5 ocbi @r4 add #32,r4 + ocbi @r5 + add #32,r5 movca.l r0,@r4 + movca.l r0,@r5 ocbi @r4 add #32,r4 + ocbi @r5 + add #32,r5 movca.l r0,@r4 + movca.l r0,@r5 ocbi @r4 + add #32,r4 + ocbi @r5 + ldc r1,sr ! restore SR dt r3 bf/s 1b - add #32,r4 + add #32,r5 rts - nop + nop +#else +1: + ldc r2,sr ! set BL bit + movca.l r0,@r4 + ocbi @r4 + add #32,r4 + + movca.l r0,@r4 + ocbi @r4 + add #32,r4 + movca.l r0,@r4 + ocbi @r4 + add #32,r4 + movca.l r0,@r4 + ocbi @r4 + + ldc r1,sr ! restore SR + dt r3 + bf/s 1b + add #32,r4 + + rts + nop +#endif + .align 2 2: .long 0xffffc000 3: .long SYMBOL_NAME(empty_zero_page) 4: .long 0x10000000 ! BL bit + #endif diff -urN linux-2.4.22-bk23/arch/sh64/config.in linux-2.4.22-bk24/arch/sh64/config.in --- linux-2.4.22-bk23/arch/sh64/config.in 2003-09-23 03:06:47.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh64/config.in 2003-09-23 03:07:07.000000000 -0700 @@ -54,6 +54,8 @@ "2 CONFIG_SH64_PGTABLE_2_LEVEL \ 3 CONFIG_SH64_PGTABLE_3_LEVEL" 2 +bool 'Fixup misaligned loads/stores occurring in user mode' CONFIG_SH64_USER_MISALIGNED_FIXUP + # Use 32-bit addressing for now. # EMI based. # P2 (UNCACHED) required to use identity mapping @@ -300,6 +302,7 @@ bool 'GDB Stub kernel debug' CONFIG_DEBUG_KERNEL_WITH_GDB_STUB bool "Debug: audit page tables on return from syscall/exception/interrupt" CONFIG_SH64_PAGE_TABLE_AUDIT dep_bool "Debug: report TLB fill/purge activity through /proc/tlb" CONFIG_SH64_PROC_TLB $CONFIG_PROC_FS +dep_bool "Debug: report ASIDS through /proc/asids" CONFIG_SH64_PROC_ASIDS $CONFIG_PROC_FS int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 diff -urN linux-2.4.22-bk23/arch/sh64/defconfig linux-2.4.22-bk24/arch/sh64/defconfig --- linux-2.4.22-bk23/arch/sh64/defconfig 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh64/defconfig 2003-09-23 03:07:07.000000000 -0700 @@ -39,6 +39,7 @@ # CONFIG_SH64_FPU_DENORM_FLUSH is not set CONFIG_SH64_PGTABLE_2_LEVEL=y # CONFIG_SH64_PGTABLE_3_LEVEL is not set +# CONFIG_SH64_USER_MISALIGNED_FIXUP is not set # # Memory options @@ -477,6 +478,7 @@ # CONFIG_DEBUG_KERNEL_WITH_GDB_STUB is not set # CONFIG_SH64_PAGE_TABLE_AUDIT is not set # CONFIG_SH64_PROC_TLB is not set +# CONFIG_SH64_PROC_ASIDS is not set # # Library routines diff -urN linux-2.4.22-bk23/arch/sh64/kernel/fpu.c linux-2.4.22-bk24/arch/sh64/kernel/fpu.c --- linux-2.4.22-bk23/arch/sh64/kernel/fpu.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh64/kernel/fpu.c 2003-09-23 03:07:07.000000000 -0700 @@ -23,47 +23,56 @@ * has the property that no matter whether considered as single or as * double precision, it still represents a signalling NAN. */ -#define sNAN64 0xFFFFFFFFFFFFFFFF +#define sNAN64 0xFFFFFFFFFFFFFFFFULL +#define sNAN32 0xFFFFFFFFUL +static union sh_fpu_union init_fpuregs = { + .hard = { + .fp_regs = { [0 ... 63] = sNAN32 }, + .fpscr = FPSCR_INIT + } +}; +#if 0 static struct sh_fpu_hard_struct init_fpuregs = { - { [0 ... 31] = sNAN64 }, + { [0 ... 63] = sNAN32 }, FPSCR_INIT }; +#endif inline void fpsave(struct sh_fpu_hard_struct *fpregs) { - asm volatile("fst.d %0, (0*8)," __d(0) "\n\t" - "fst.d %0, (1*8)," __d(2) "\n\t" - "fst.d %0, (2*8)," __d(4) "\n\t" - "fst.d %0, (3*8)," __d(6) "\n\t" - "fst.d %0, (4*8)," __d(8) "\n\t" - "fst.d %0, (5*8)," __d(10) "\n\t" - "fst.d %0, (6*8)," __d(12) "\n\t" - "fst.d %0, (7*8)," __d(14) "\n\t" - "fst.d %0, (8*8)," __d(16) "\n\t" - "fst.d %0, (9*8)," __d(18) "\n\t" - "fst.d %0, (10*8)," __d(20) "\n\t" - "fst.d %0, (11*8)," __d(22) "\n\t" - "fst.d %0, (12*8)," __d(24) "\n\t" - "fst.d %0, (13*8)," __d(26) "\n\t" - "fst.d %0, (14*8)," __d(28) "\n\t" - "fst.d %0, (15*8)," __d(30) "\n\t" - "fst.d %0, (16*8)," __d(32) "\n\t" - "fst.d %0, (17*8)," __d(34) "\n\t" - "fst.d %0, (18*8)," __d(36) "\n\t" - "fst.d %0, (19*8)," __d(38) "\n\t" - "fst.d %0, (20*8)," __d(40) "\n\t" - "fst.d %0, (21*8)," __d(42) "\n\t" - "fst.d %0, (22*8)," __d(44) "\n\t" - "fst.d %0, (23*8)," __d(46) "\n\t" - "fst.d %0, (24*8)," __d(48) "\n\t" - "fst.d %0, (25*8)," __d(50) "\n\t" - "fst.d %0, (26*8)," __d(52) "\n\t" - "fst.d %0, (27*8)," __d(54) "\n\t" - "fst.d %0, (28*8)," __d(56) "\n\t" - "fst.d %0, (29*8)," __d(58) "\n\t" - "fst.d %0, (30*8)," __d(60) "\n\t" - "fst.d %0, (31*8)," __d(62) "\n\t" + asm volatile("fst.p %0, (0*8)," __p(0) "\n\t" + "fst.p %0, (1*8)," __p(2) "\n\t" + "fst.p %0, (2*8)," __p(4) "\n\t" + "fst.p %0, (3*8)," __p(6) "\n\t" + "fst.p %0, (4*8)," __p(8) "\n\t" + "fst.p %0, (5*8)," __p(10) "\n\t" + "fst.p %0, (6*8)," __p(12) "\n\t" + "fst.p %0, (7*8)," __p(14) "\n\t" + "fst.p %0, (8*8)," __p(16) "\n\t" + "fst.p %0, (9*8)," __p(18) "\n\t" + "fst.p %0, (10*8)," __p(20) "\n\t" + "fst.p %0, (11*8)," __p(22) "\n\t" + "fst.p %0, (12*8)," __p(24) "\n\t" + "fst.p %0, (13*8)," __p(26) "\n\t" + "fst.p %0, (14*8)," __p(28) "\n\t" + "fst.p %0, (15*8)," __p(30) "\n\t" + "fst.p %0, (16*8)," __p(32) "\n\t" + "fst.p %0, (17*8)," __p(34) "\n\t" + "fst.p %0, (18*8)," __p(36) "\n\t" + "fst.p %0, (19*8)," __p(38) "\n\t" + "fst.p %0, (20*8)," __p(40) "\n\t" + "fst.p %0, (21*8)," __p(42) "\n\t" + "fst.p %0, (22*8)," __p(44) "\n\t" + "fst.p %0, (23*8)," __p(46) "\n\t" + "fst.p %0, (24*8)," __p(48) "\n\t" + "fst.p %0, (25*8)," __p(50) "\n\t" + "fst.p %0, (26*8)," __p(52) "\n\t" + "fst.p %0, (27*8)," __p(54) "\n\t" + "fst.p %0, (28*8)," __p(56) "\n\t" + "fst.p %0, (29*8)," __p(58) "\n\t" + "fst.p %0, (30*8)," __p(60) "\n\t" + "fst.p %0, (31*8)," __p(62) "\n\t" "_fgetscr " __f(63) "\n\t" "fst.s %0, (32*8)," __f(63) "\n\t" @@ -76,49 +85,49 @@ static inline void fpload(struct sh_fpu_hard_struct *fpregs) { - asm volatile("fld.d %0, (0*8)," __d(0) "\n\t" - "fld.d %0, (1*8)," __d(2) "\n\t" - "fld.d %0, (2*8)," __d(4) "\n\t" - "fld.d %0, (3*8)," __d(6) "\n\t" - "fld.d %0, (4*8)," __d(8) "\n\t" - "fld.d %0, (5*8)," __d(10) "\n\t" - "fld.d %0, (6*8)," __d(12) "\n\t" - "fld.d %0, (7*8)," __d(14) "\n\t" - "fld.d %0, (8*8)," __d(16) "\n\t" - "fld.d %0, (9*8)," __d(18) "\n\t" - "fld.d %0, (10*8)," __d(20) "\n\t" - "fld.d %0, (11*8)," __d(22) "\n\t" - "fld.d %0, (12*8)," __d(24) "\n\t" - "fld.d %0, (13*8)," __d(26) "\n\t" - "fld.d %0, (14*8)," __d(28) "\n\t" - "fld.d %0, (15*8)," __d(30) "\n\t" - "fld.d %0, (16*8)," __d(32) "\n\t" - "fld.d %0, (17*8)," __d(34) "\n\t" - "fld.d %0, (18*8)," __d(36) "\n\t" - "fld.d %0, (19*8)," __d(38) "\n\t" - "fld.d %0, (20*8)," __d(40) "\n\t" - "fld.d %0, (21*8)," __d(42) "\n\t" - "fld.d %0, (22*8)," __d(44) "\n\t" - "fld.d %0, (23*8)," __d(46) "\n\t" - "fld.d %0, (24*8)," __d(48) "\n\t" - "fld.d %0, (25*8)," __d(50) "\n\t" - "fld.d %0, (26*8)," __d(52) "\n\t" - "fld.d %0, (27*8)," __d(54) "\n\t" - "fld.d %0, (28*8)," __d(56) "\n\t" - "fld.d %0, (29*8)," __d(58) "\n\t" - "fld.d %0, (30*8)," __d(60) "\n\t" + asm volatile("fld.p %0, (0*8)," __p(0) "\n\t" + "fld.p %0, (1*8)," __p(2) "\n\t" + "fld.p %0, (2*8)," __p(4) "\n\t" + "fld.p %0, (3*8)," __p(6) "\n\t" + "fld.p %0, (4*8)," __p(8) "\n\t" + "fld.p %0, (5*8)," __p(10) "\n\t" + "fld.p %0, (6*8)," __p(12) "\n\t" + "fld.p %0, (7*8)," __p(14) "\n\t" + "fld.p %0, (8*8)," __p(16) "\n\t" + "fld.p %0, (9*8)," __p(18) "\n\t" + "fld.p %0, (10*8)," __p(20) "\n\t" + "fld.p %0, (11*8)," __p(22) "\n\t" + "fld.p %0, (12*8)," __p(24) "\n\t" + "fld.p %0, (13*8)," __p(26) "\n\t" + "fld.p %0, (14*8)," __p(28) "\n\t" + "fld.p %0, (15*8)," __p(30) "\n\t" + "fld.p %0, (16*8)," __p(32) "\n\t" + "fld.p %0, (17*8)," __p(34) "\n\t" + "fld.p %0, (18*8)," __p(36) "\n\t" + "fld.p %0, (19*8)," __p(38) "\n\t" + "fld.p %0, (20*8)," __p(40) "\n\t" + "fld.p %0, (21*8)," __p(42) "\n\t" + "fld.p %0, (22*8)," __p(44) "\n\t" + "fld.p %0, (23*8)," __p(46) "\n\t" + "fld.p %0, (24*8)," __p(48) "\n\t" + "fld.p %0, (25*8)," __p(50) "\n\t" + "fld.p %0, (26*8)," __p(52) "\n\t" + "fld.p %0, (27*8)," __p(54) "\n\t" + "fld.p %0, (28*8)," __p(56) "\n\t" + "fld.p %0, (29*8)," __p(58) "\n\t" + "fld.p %0, (30*8)," __p(60) "\n\t" "fld.s %0, (32*8)," __f(63) "\n\t" "_fputscr " __f(63) "\n\t" - "fld.d %0, (31*8)," __d(62) "\n\t" + "fld.p %0, (31*8)," __p(62) "\n\t" : /* no output */ : "r" (fpregs) ); } void fpinit(struct sh_fpu_hard_struct *fpregs) { - *fpregs = init_fpuregs; + *fpregs = init_fpuregs.hard; } asmlinkage void @@ -163,7 +172,7 @@ fpload(¤t->thread.fpu.hard); } else { /* First time FPU user. */ - fpload(&init_fpuregs); + fpload(&init_fpuregs.hard); current->used_math = 1; } release_fpu(); diff -urN linux-2.4.22-bk23/arch/sh64/kernel/process.c linux-2.4.22-bk24/arch/sh64/kernel/process.c --- linux-2.4.22-bk23/arch/sh64/kernel/process.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh64/kernel/process.c 2003-09-23 03:07:07.000000000 -0700 @@ -937,3 +937,48 @@ return pc; } +/* Provide a /proc/asids file that lists out the + ASIDs currently associated with the processes. (If the DM.PC register is + examined through the debug link, this shows ASID + PC. To make use of this, + the PID->ASID relationship needs to be known. This is primarily for + debugging.) + */ + +#if defined(CONFIG_SH64_PROC_ASIDS) +#include +#include + +static int +asids_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void *data) +{ + int len=0; + struct task_struct *p; + read_lock(&tasklist_lock); + for_each_task(p) { + int pid = p->pid; + struct mm_struct *mm; + if (!pid) continue; + mm = p->mm; + if (mm) { + unsigned long asid, context; + context = mm->context; + asid = (context & 0xff); + len += sprintf(buf+len, "%5d : %02x\n", pid, asid); + } else { + len += sprintf(buf+len, "%5d : (none)\n", pid); + } + } + read_unlock(&tasklist_lock); + *eof = 1; + return len; +} + +static int __init register_proc_asids(void) +{ + create_proc_read_entry("asids", 0, NULL, asids_proc_info, NULL); + return 0; +} + +__initcall(register_proc_asids); +#endif + diff -urN linux-2.4.22-bk23/arch/sh64/kernel/traps.c linux-2.4.22-bk24/arch/sh64/kernel/traps.c --- linux-2.4.22-bk23/arch/sh64/kernel/traps.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.22-bk24/arch/sh64/kernel/traps.c 2003-09-23 03:07:07.000000000 -0700 @@ -7,6 +7,7 @@ * * Copyright (C) 2000, 2001 Paolo Alberelli * Copyright (C) 2003 Paul Mundt + * Copyright (C) 2003 Richard Curnow * */ @@ -27,11 +28,15 @@ #include #include +#include +#include + #include #include #include #include #include +#include #undef DEBUG_EXCEPTION #ifdef DEBUG_EXCEPTION @@ -42,14 +47,13 @@ #define show_excp_regs(a, b, c, d) #endif +static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, + unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk); + #define DO_ERROR(trapnr, signr, str, name, tsk) \ asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ { \ - show_excp_regs(__FUNCTION__, trapnr, signr, regs); \ - tsk->thread.error_code = error_code; \ - tsk->thread.trap_no = trapnr; \ - if (user_mode(regs)) force_sig(signr, tsk); \ - die_if_no_fixup(str,regs,error_code); \ + do_unhandled_exception(trapnr, signr, str, __FUNCTION__, error_code, regs, current); \ } spinlock_t die_lock; @@ -84,11 +88,34 @@ } } -DO_ERROR( 7, SIGSEGV, "address error (load)", address_error_load, current) -DO_ERROR( 8, SIGSEGV, "address error (store)", address_error_store, current) DO_ERROR(13, SIGILL, "illegal slot instruction", illegal_slot_inst, current) DO_ERROR(87, SIGSEGV, "address error (exec)", address_error_exec, current) + +/* Implement misaligned load/store handling for kernel (and optionally for user + mode too). Limitation : only SHmedia mode code is handled - there is no + handling at all for misaligned accesses occurring in SHcompact code yet. */ + +static int misaligned_fixup(struct pt_regs *regs); + +asmlinkage void do_address_error_load(unsigned long error_code, struct pt_regs *regs) +{ + if (misaligned_fixup(regs) < 0) { + do_unhandled_exception(7, SIGSEGV, "address error(load)", __FUNCTION__, + error_code, regs, current); + } + return; +} + +asmlinkage void do_address_error_store(unsigned long error_code, struct pt_regs *regs) +{ + if (misaligned_fixup(regs) < 0) { + do_unhandled_exception(8, SIGSEGV, "address error(store)", __FUNCTION__, + error_code, regs, current); + } + return; +} + #if defined(CONFIG_SH64_ID2815_WORKAROUND) #define OPCODE_INVALID 0 @@ -121,7 +148,7 @@ which should take ITLBMISS or EXECPROT exceptions at the target falsely take RESINST at the target instead. */ - unsigned long opcode; + unsigned long opcode = 0x6ff4fff0; /* guaranteed reserved opcode */ unsigned long pc, aligned_pc; int get_user_error; int trapnr = 12; @@ -203,11 +230,7 @@ } } - show_excp_regs("do_reserved_inst", trapnr, signr, regs); - current->thread.error_code = error_code; - current->thread.trap_no = trapnr; - if (user_mode(regs)) force_sig(signr, current); - die_if_no_fixup(exception_name, regs, error_code); + do_unhandled_exception(trapnr, signr, exception_name, "do_reserved_inst", error_code, regs, current); } #else /* CONFIG_SH64_ID2815_WORKAROUND */ @@ -302,3 +325,640 @@ show_task(NULL); } +static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, + unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) +{ + show_excp_regs(fn_name, trapnr, signr, regs); + tsk->thread.error_code = error_code; + tsk->thread.trap_no = trapnr; + if (user_mode(regs)) force_sig(signr, tsk); + die_if_no_fixup(str, regs, error_code); +} + +static int read_opcode(unsigned long long pc, unsigned long *result_opcode) +{ + int get_user_error; + unsigned long aligned_pc; + unsigned long opcode; + + if ((pc & 3) == 1) { + /* SHmedia */ + aligned_pc = pc & ~3; + if (!access_ok(VERIFY_READ, aligned_pc, sizeof(unsigned long))) { + get_user_error = -EFAULT; + } else { + get_user_error = __get_user(opcode, (unsigned long *)aligned_pc); + *result_opcode = opcode; + } + return get_user_error; + } else if ((pc & 1) == 0) { + /* SHcompact */ + /* TODO : provide handling for this. */ + return -EFAULT; + } else { + /* misaligned */ + return -EFAULT; + } +} + +static int address_is_sign_extended(__u64 a) +{ + __u64 b; +#if (NEFF == 32) + b = (__u64)(__s64)(__s32)(a & 0xffffffffUL); + return (b == a) ? 1 : 0; +#else +#error "Sign extend check only works for NEFF==32" +#endif +} + +static int generate_and_check_address(struct pt_regs *regs, + __u32 opcode, + int displacement_not_indexed, + int width_shift, + __u64 *address) +{ + /* return -1 for fault, 0 for OK */ + + __u64 base_address, addr; + int basereg; + int do_as_user = user_mode(regs); + + basereg = (opcode >> 20) & 0x3f; + base_address = regs->regs[basereg]; + if (displacement_not_indexed) { + __s64 displacement; + displacement = (opcode >> 10) & 0x3ff; + displacement = ((displacement << 54) >> 54); /* sign extend */ + addr = (__u64)((__s64)base_address + (displacement << width_shift)); + } else { + __u64 offset; + int offsetreg; + offsetreg = (opcode >> 10) & 0x3f; + offset = regs->regs[offsetreg]; + addr = base_address + offset; + } + + /* Check sign extended */ + if (!address_is_sign_extended(addr)) { + return -1; + } + +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + /* Check accessible. For misaligned access in the kernel, assume the + address is always accessible (and if not, just fault when the + load/store gets done.) */ + if (do_as_user) { + if (addr >= TASK_SIZE) { + return -1; + } + /* Do access_ok check later - it depends on whether it's a load or a store. */ + } +#endif + + *address = addr; + return 0; +} + +/* Default value as for sh */ +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) +static int user_mode_unaligned_fixup_count = 10; +static int user_mode_unaligned_fixup_enable = 1; +#endif + +static int kernel_mode_unaligned_fixup_count = 32; + +static void misaligned_kernel_word_load(__u64 address, int do_sign_extend, __u64 *result) +{ + unsigned short x; + unsigned char *p, *q; + p = (unsigned char *) (int) address; + q = (unsigned char *) &x; + q[0] = p[0]; + q[1] = p[1]; + + if (do_sign_extend) { + *result = (__u64)(__s64) *(short *) &x; + } else { + *result = (__u64) x; + } +} + +static void misaligned_kernel_word_store(__u64 address, __u64 value) +{ + unsigned short x; + unsigned char *p, *q; + p = (unsigned char *) (int) address; + q = (unsigned char *) &x; + + x = (__u16) value; + p[0] = q[0]; + p[1] = q[1]; +} + +static int misaligned_load(struct pt_regs *regs, + __u32 opcode, + int displacement_not_indexed, + int width_shift, + int do_sign_extend) +{ + /* Return -1 for a fault, 0 for OK */ + int error; + int destreg; + __u64 address; + + error = generate_and_check_address(regs, opcode, + displacement_not_indexed, width_shift, &address); + if (error < 0) { + return error; + } + + destreg = (opcode >> 4) & 0x3f; +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + if (user_mode(regs)) { + __u64 buffer; + + if (!access_ok(VERIFY_READ, (unsigned long) address, 1UL< 0) { + return -1; /* fault */ + } + switch (width_shift) { + case 1: + if (do_sign_extend) { + regs->regs[destreg] = (__u64)(__s64) *(__s16 *) &buffer; + } else { + regs->regs[destreg] = (__u64) *(__u16 *) &buffer; + } + break; + case 2: + regs->regs[destreg] = (__u64)(__s64) *(__s32 *) &buffer; + break; + case 3: + regs->regs[destreg] = buffer; + break; + default: + printk("Unexpected width_shift %d in misaligned_load, PC=%08lx\n", + width_shift, (unsigned long) regs->pc); + break; + } + } else +#endif + { + /* kernel mode - we can take short cuts since if we fault, it's a genuine bug */ + __u64 lo, hi; + + switch (width_shift) { + case 1: + misaligned_kernel_word_load(address, do_sign_extend, ®s->regs[destreg]); + break; + case 2: + asm ("ldlo.l %1, 0, %0" : "=r" (lo) : "r" (address)); + asm ("ldhi.l %1, 3, %0" : "=r" (hi) : "r" (address)); + regs->regs[destreg] = lo | hi; + break; + case 3: + asm ("ldlo.q %1, 0, %0" : "=r" (lo) : "r" (address)); + asm ("ldhi.q %1, 7, %0" : "=r" (hi) : "r" (address)); + regs->regs[destreg] = lo | hi; + break; + + default: + printk("Unexpected width_shift %d in misaligned_load, PC=%08lx\n", + width_shift, (unsigned long) regs->pc); + break; + } + } + + return 0; + +} + +static int misaligned_store(struct pt_regs *regs, + __u32 opcode, + int displacement_not_indexed, + int width_shift) +{ + /* Return -1 for a fault, 0 for OK */ + int error; + int srcreg; + __u64 address; + + error = generate_and_check_address(regs, opcode, + displacement_not_indexed, width_shift, &address); + if (error < 0) { + return error; + } + + srcreg = (opcode >> 4) & 0x3f; +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + if (user_mode(regs)) { + __u64 buffer; + + if (!access_ok(VERIFY_WRITE, (unsigned long) address, 1UL<regs[srcreg]; + break; + case 2: + *(__u32 *) &buffer = (__u32) regs->regs[srcreg]; + break; + case 3: + buffer = regs->regs[srcreg]; + break; + default: + printk("Unexpected width_shift %d in misaligned_store, PC=%08lx\n", + width_shift, (unsigned long) regs->pc); + break; + } + + if (__copy_user((void *)(int)address, &buffer, (1 << width_shift)) > 0) { + return -1; /* fault */ + } + } else +#endif + { + /* kernel mode - we can take short cuts since if we fault, it's a genuine bug */ + __u64 val = regs->regs[srcreg]; + + switch (width_shift) { + case 1: + misaligned_kernel_word_store(address, val); + break; + case 2: + asm ("stlo.l %1, 0, %0" : : "r" (val), "r" (address)); + asm ("sthi.l %1, 3, %0" : : "r" (val), "r" (address)); + break; + case 3: + asm ("stlo.q %1, 0, %0" : : "r" (val), "r" (address)); + asm ("sthi.q %1, 7, %0" : : "r" (val), "r" (address)); + break; + + default: + printk("Unexpected width_shift %d in misaligned_store, PC=%08lx\n", + width_shift, (unsigned long) regs->pc); + break; + } + } + + return 0; + +} + +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) +/* Never need to fix up misaligned FPU accesses within the kernel since that's a real + error. */ +static int misaligned_fpu_load(struct pt_regs *regs, + __u32 opcode, + int displacement_not_indexed, + int width_shift, + int do_paired_load) +{ + /* Return -1 for a fault, 0 for OK */ + int error; + int destreg; + __u64 address; + + error = generate_and_check_address(regs, opcode, + displacement_not_indexed, width_shift, &address); + if (error < 0) { + return error; + } + + destreg = (opcode >> 4) & 0x3f; + if (user_mode(regs)) { + __u64 buffer; + __u32 buflo, bufhi; + + if (!access_ok(VERIFY_READ, (unsigned long) address, 1UL< 0) { + return -1; /* fault */ + } + /* 'current' may be the current owner of the FPU state, so + context switch the registers into memory so they can be + indexed by register number. */ + if (last_task_used_math == current) { + grab_fpu(); + fpsave(¤t->thread.fpu.hard); + release_fpu(); + last_task_used_math = NULL; + regs->sr |= SR_FD; + } + + buflo = *(__u32*) &buffer; + bufhi = *(1 + (__u32*) &buffer); + + switch (width_shift) { + case 2: + current->thread.fpu.hard.fp_regs[destreg] = buflo; + break; + case 3: + if (do_paired_load) { + current->thread.fpu.hard.fp_regs[destreg] = buflo; + current->thread.fpu.hard.fp_regs[destreg+1] = bufhi; + } else { +#if defined(CONFIG_LITTLE_ENDIAN) + current->thread.fpu.hard.fp_regs[destreg] = bufhi; + current->thread.fpu.hard.fp_regs[destreg+1] = buflo; +#else + current->thread.fpu.hard.fp_regs[destreg] = buflo; + current->thread.fpu.hard.fp_regs[destreg+1] = bufhi; +#endif + } + break; + default: + printk("Unexpected width_shift %d in misaligned_fpu_load, PC=%08lx\n", + width_shift, (unsigned long) regs->pc); + break; + } + return 0; + } else { + die ("Misaligned FPU load inside kernel", regs, 0); + return -1; + } + + +} + +static int misaligned_fpu_store(struct pt_regs *regs, + __u32 opcode, + int displacement_not_indexed, + int width_shift, + int do_paired_load) +{ + /* Return -1 for a fault, 0 for OK */ + int error; + int srcreg; + __u64 address; + + error = generate_and_check_address(regs, opcode, + displacement_not_indexed, width_shift, &address); + if (error < 0) { + return error; + } + + srcreg = (opcode >> 4) & 0x3f; + if (user_mode(regs)) { + __u64 buffer; + /* Initialise these to NaNs. */ + __u32 buflo=0xffffffffUL, bufhi=0xffffffffUL; + + if (!access_ok(VERIFY_WRITE, (unsigned long) address, 1UL<thread.fpu.hard); + release_fpu(); + last_task_used_math = NULL; + regs->sr |= SR_FD; + } + + switch (width_shift) { + case 2: + buflo = current->thread.fpu.hard.fp_regs[srcreg]; + break; + case 3: + if (do_paired_load) { + buflo = current->thread.fpu.hard.fp_regs[srcreg]; + bufhi = current->thread.fpu.hard.fp_regs[srcreg+1]; + } else { +#if defined(CONFIG_LITTLE_ENDIAN) + bufhi = current->thread.fpu.hard.fp_regs[srcreg]; + buflo = current->thread.fpu.hard.fp_regs[srcreg+1]; +#else + buflo = current->thread.fpu.hard.fp_regs[srcreg]; + bufhi = current->thread.fpu.hard.fp_regs[srcreg+1]; +#endif + } + break; + default: + printk("Unexpected width_shift %d in misaligned_fpu_store, PC=%08lx\n", + width_shift, (unsigned long) regs->pc); + break; + } + + *(__u32*) &buffer = buflo; + *(1 + (__u32*) &buffer) = bufhi; + if (__copy_user((void *)(int)address, &buffer, (1 << width_shift)) > 0) { + return -1; /* fault */ + } + return 0; + } else { + die ("Misaligned FPU load inside kernel", regs, 0); + return -1; + } +} +#endif + +static int misaligned_fixup(struct pt_regs *regs) +{ + unsigned long opcode; + int error; + int major, minor; + +#if !defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + /* Never fixup user mode misaligned accesses without this option enabled. */ + return -1; +#else + if (!user_mode_unaligned_fixup_enable) return -1; +#endif + + error = read_opcode(regs->pc, &opcode); + if (error < 0) { + return error; + } + major = (opcode >> 26) & 0x3f; + minor = (opcode >> 16) & 0xf; + +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + if (user_mode(regs) && (user_mode_unaligned_fixup_count > 0)) { + --user_mode_unaligned_fixup_count; + /* Only do 'count' worth of these reports, to remove a potential DoS against syslog */ + printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n", + current->comm, current->pid, (__u32)regs->pc, opcode); + } else +#endif + if (!user_mode(regs) && (kernel_mode_unaligned_fixup_count > 0)) { + --kernel_mode_unaligned_fixup_count; + if (in_interrupt()) { + printk("Fixing up unaligned kernelspace access in interrupt pc=0x%08x ins=0x%08lx\n", + (__u32)regs->pc, opcode); + } else { + printk("Fixing up unaligned kernelspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n", + current->comm, current->pid, (__u32)regs->pc, opcode); + } + } + + + switch (major) { + case (0x84>>2): /* LD.W */ + error = misaligned_load(regs, opcode, 1, 1, 1); + break; + case (0xb0>>2): /* LD.UW */ + error = misaligned_load(regs, opcode, 1, 1, 0); + break; + case (0x88>>2): /* LD.L */ + error = misaligned_load(regs, opcode, 1, 2, 1); + break; + case (0x8c>>2): /* LD.Q */ + error = misaligned_load(regs, opcode, 1, 3, 0); + break; + + case (0xa4>>2): /* ST.W */ + error = misaligned_store(regs, opcode, 1, 1); + break; + case (0xa8>>2): /* ST.L */ + error = misaligned_store(regs, opcode, 1, 2); + break; + case (0xac>>2): /* ST.Q */ + error = misaligned_store(regs, opcode, 1, 3); + break; + + case (0x40>>2): /* indexed loads */ + switch (minor) { + case 0x1: /* LDX.W */ + error = misaligned_load(regs, opcode, 0, 1, 1); + break; + case 0x5: /* LDX.UW */ + error = misaligned_load(regs, opcode, 0, 1, 0); + break; + case 0x2: /* LDX.L */ + error = misaligned_load(regs, opcode, 0, 2, 1); + break; + case 0x3: /* LDX.Q */ + error = misaligned_load(regs, opcode, 0, 3, 0); + break; + default: + error = -1; + break; + } + break; + + case (0x60>>2): /* indexed stores */ + switch (minor) { + case 0x1: /* STX.W */ + error = misaligned_store(regs, opcode, 0, 1); + break; + case 0x2: /* STX.L */ + error = misaligned_store(regs, opcode, 0, 2); + break; + case 0x3: /* STX.Q */ + error = misaligned_store(regs, opcode, 0, 3); + break; + default: + error = -1; + break; + } + break; + +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + case (0x94>>2): /* FLD.S */ + error = misaligned_fpu_load(regs, opcode, 1, 2, 0); + break; + case (0x98>>2): /* FLD.P */ + error = misaligned_fpu_load(regs, opcode, 1, 3, 1); + break; + case (0x9c>>2): /* FLD.D */ + error = misaligned_fpu_load(regs, opcode, 1, 3, 0); + break; + case (0x1c>>2): /* floating indexed loads */ + switch (minor) { + case 0x8: /* FLDX.S */ + error = misaligned_fpu_load(regs, opcode, 0, 2, 0); + break; + case 0xd: /* FLDX.P */ + error = misaligned_fpu_load(regs, opcode, 0, 3, 1); + break; + case 0x9: /* FLDX.D */ + error = misaligned_fpu_load(regs, opcode, 0, 3, 0); + break; + default: + error = -1; + break; + } + break; + case (0xb4>>2): /* FLD.S */ + error = misaligned_fpu_store(regs, opcode, 1, 2, 0); + break; + case (0xb8>>2): /* FLD.P */ + error = misaligned_fpu_store(regs, opcode, 1, 3, 1); + break; + case (0xbc>>2): /* FLD.D */ + error = misaligned_fpu_store(regs, opcode, 1, 3, 0); + break; + case (0x3c>>2): /* floating indexed stores */ + switch (minor) { + case 0x8: /* FSTX.S */ + error = misaligned_fpu_store(regs, opcode, 0, 2, 0); + break; + case 0xd: /* FSTX.P */ + error = misaligned_fpu_store(regs, opcode, 0, 3, 1); + break; + case 0x9: /* FSTX.D */ + error = misaligned_fpu_store(regs, opcode, 0, 3, 0); + break; + default: + error = -1; + break; + } + break; +#endif + + default: + /* Fault */ + error = -1; + break; + } + + if (error < 0) { + return error; + } else { + regs->pc += 4; /* Skip the instruction that's just been emulated */ + return 0; + } + +} + +static ctl_table unaligned_table[] = { + {1, "kernel_reports", &kernel_mode_unaligned_fixup_count, + sizeof(int), 0644, NULL, &proc_dointvec}, +#if defined(CONFIG_SH64_USER_MISALIGNED_FIXUP) + {2, "user_reports", &user_mode_unaligned_fixup_count, + sizeof(int), 0644, NULL, &proc_dointvec}, + {3, "user_enable", &user_mode_unaligned_fixup_enable, + sizeof(int), 0644, NULL, &proc_dointvec}, +#endif + {0} +}; + +static ctl_table unaligned_root[] = { + {1, "unaligned_fixup", NULL, 0, 0555, unaligned_table}, + {0} +}; + +static ctl_table sh64_root[] = { + {1, "sh64", NULL, 0, 0555, unaligned_root}, + {0} +}; +static struct ctl_table_header *sysctl_header; +static int __init init_sysctl(void) +{ + sysctl_header = register_sysctl_table(sh64_root, 0); + return 0; +} + +__initcall(init_sysctl); + diff -urN linux-2.4.22-bk23/drivers/char/sh-sci.c linux-2.4.22-bk24/drivers/char/sh-sci.c --- linux-2.4.22-bk23/drivers/char/sh-sci.c 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.22-bk24/drivers/char/sh-sci.c 2003-09-23 03:07:15.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: sh-sci.c,v 1.1.1.1.2.7 2003/07/16 18:45:31 yoshii Exp $ +/* $Id: sh-sci.c,v 1.1.1.1.2.10 2003/09/17 23:38:56 davidm-sf Exp $ * * linux/drivers/char/sh-sci.c * @@ -6,6 +6,7 @@ * Copyright (C) 1999, 2000 Niibe Yutaka * Copyright (C) 2000 Sugioka Toshinobu * Modified to support multiple serial ports. Stuart Menefy (May 2000). + * Modified to support SecureEdge. David McCullough (2002) * Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003). * * TTY code is based on sx.c (Specialix SX driver) by: @@ -239,6 +240,51 @@ #endif /* CONFIG_SERIAL_CONSOLE */ +#if defined(CONFIG_SH_SECUREEDGE5410) + +struct timer_list sci_timer_struct; +static unsigned char sci_dcdstatus[2]; + +/* + * This subroutine is called when the RS_TIMER goes off. It is used + * to monitor the state of the DCD lines - since they have no edge + * sensors and interrupt generators. + */ +static void sci_timer(unsigned long data) +{ + unsigned short s, i; + unsigned char dcdstatus[2]; + + s = SECUREEDGE_READ_IOPORT(); + dcdstatus[0] = !(s & 0x10); + dcdstatus[1] = !(s & 0x1); + + for (i = 0; i < 2; i++) { + if (dcdstatus[i] != sci_dcdstatus[i]) { + if (sci_ports[i].gs.count != 0) { + if (sci_ports[i].gs.flags & ASYNC_CHECK_CD) { + if (dcdstatus[i]) { /* DCD has gone high */ + wake_up_interruptible(&sci_ports[i].gs.open_wait); + } else if (!((sci_ports[i].gs.flags&ASYNC_CALLOUT_ACTIVE) && + (sci_ports[i].gs.flags & ASYNC_CALLOUT_NOHUP))) { + if (sci_ports[i].gs.tty) + tty_hangup(sci_ports[i].gs.tty); + } + } + } + } + sci_dcdstatus[i] = dcdstatus[i]; + } + + sci_timer_struct.expires = jiffies + HZ/25; + add_timer(&sci_timer_struct); +} + +#endif + + + + #ifdef CONFIG_SH_KGDB /* Is the SCI ready, ie is there a char waiting? */ @@ -359,7 +405,7 @@ /* We need to set SCPCR to enable RTS/CTS */ data = ctrl_inw(SCPCR); /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ - ctrl_outw(data&0x0fcf, SCPCR); + ctrl_outw(data&0x0cff, SCPCR); } if (cflag & CRTSCTS) fcr_val |= SCFCR_MCE; @@ -370,7 +416,7 @@ data = ctrl_inw(SCPCR); /* Clear out SCP7MD1,0, SCP4MD1,0, Set SCP6MD1,0 = {01} (output) */ - ctrl_outw((data&0x0fcf)|0x1000, SCPCR); + ctrl_outw((data&0x0cff)|0x1000, SCPCR); data = ctrl_inb(SCPDR); /* Set /RTS2 (bit6) = 0 */ @@ -413,7 +459,29 @@ /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ /* If you have signals for DTR and DCD, please implement here. */ - ; + +#if defined(CONFIG_SH_SECUREEDGE5410) + int flags; + + save_and_cli(flags); + if (port == &sci_ports[1]) { /* port 1 only */ + if (dtr == 0) + SECUREEDGE_WRITE_IOPORT(0x0080, 0x0080); + else if (dtr == 1) + SECUREEDGE_WRITE_IOPORT(0x0000, 0x0080); + } + if (port == &sci_ports[0]) { /* port 0 only */ + if (dtr == 0) + SECUREEDGE_WRITE_IOPORT(0x0200, 0x0200); + else if (dtr == 1) + SECUREEDGE_WRITE_IOPORT(0x0000, 0x0200); + if (rts == 0) + SECUREEDGE_WRITE_IOPORT(0x0100, 0x0100); + else if (rts == 1) + SECUREEDGE_WRITE_IOPORT(0x0000, 0x0100); + } + restore_flags(flags); +#endif } static int sci_getsignals(struct sci_port *port) @@ -421,15 +489,34 @@ /* This routine is used for geting signals of: DTR, DCD, DSR, RI, and CTS/RTS */ +#if defined(CONFIG_SH_SECUREEDGE5410) + if (port == &sci_ports[1]) { /* port 1 only */ + unsigned short s = SECUREEDGE_READ_IOPORT(); + int rc = TIOCM_RTS|TIOCM_DSR|TIOCM_CTS; + + if ((s & 0x0001) == 0) + rc |= TIOCM_CAR; + if ((SECUREEDGE_READ_IOPORT() & 0x0080) == 0) + rc |= TIOCM_DTR; + return(rc); + } + if (port == &sci_ports[0]) { /* port 0 only */ + unsigned short s = SECUREEDGE_READ_IOPORT(); + int rc = TIOCM_DSR; + + if ((s & 0x0010) == 0) + rc |= TIOCM_CAR; + if ((s & 0x0004) == 0) + rc |= TIOCM_CTS; + if ((SECUREEDGE_READ_IOPORT() & 0x0200) == 0) + rc |= TIOCM_DTR; + if ((SECUREEDGE_READ_IOPORT() & 0x0100) == 0) + rc |= TIOCM_RTS; + return(rc); + } +#endif + return TIOCM_DTR|TIOCM_RTS|TIOCM_DSR; -/* - (((o_stat & OP_DTR)?TIOCM_DTR:0) | - ((o_stat & OP_RTS)?TIOCM_RTS:0) | - ((i_stat & IP_CTS)?TIOCM_CTS:0) | - ((i_stat & IP_DCD)?TIOCM_CAR:0) | - ((i_stat & IP_DSR)?TIOCM_DSR:0) | - ((i_stat & IP_RI) ?TIOCM_RNG:0) -*/ } static void sci_set_baud(struct sci_port *port, int baud) @@ -458,6 +545,11 @@ case 57600: t = BPS_57600; break; + case 230400: + if (BPS_230400 != BPS_115200) { + t = BPS_230400; + break; + } default: printk(KERN_INFO "sci: unsupported baud rate: %d, using 115200 instead.\n", baud); case 115200: @@ -513,6 +605,11 @@ port->init_pins(port, cflag); sci_out(port, SCSCR, SCSCR_INIT(port)); + + if (cflag & CLOCAL) + port->gs.flags &= ~ASYNC_CHECK_CD; + else + port->gs.flags |= ASYNC_CHECK_CD; } static int sci_set_real_termios(void *ptr) @@ -525,22 +622,6 @@ sci_enable_rx_interrupts(port); } - /* Tell line discipline whether we will do input cooking */ - if (I_OTHER(port->gs.tty)) - clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); - else - set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); - -/* Tell line discipline whether we will do output cooking. - * If OPOST is set and no other output flags are set then we can do output - * processing. Even if only *one* other flag in the O_OTHER group is set - * we do cooking in software. - */ - if (O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty)) - set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); - else - clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); - return 0; } @@ -561,8 +642,8 @@ static void sci_transmit_chars(struct sci_port *port) { - int count, i; - int txroom; + unsigned int count, i; + unsigned int txroom; unsigned long flags; unsigned short status; unsigned short ctrl; @@ -645,7 +726,7 @@ static inline void sci_receive_chars(struct sci_port *port, struct pt_regs *regs) { - int i, count; + int count; struct tty_struct *tty; int copied=0; unsigned short status; @@ -655,6 +736,7 @@ return; tty = port->gs.tty; + while (1) { if (port->type == PORT_SCIF) { #if defined(CONFIG_CPU_SUBTYPE_SH7300) @@ -666,34 +748,41 @@ count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0; } - /* Don't copy more bytes than there is room for in the buffer */ - if (tty->flip.count + count > TTY_FLIPBUF_SIZE) - count = TTY_FLIPBUF_SIZE - tty->flip.count; + /* we must clear RDF or we get stuck in the interrupt for ever */ + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); /* If for any reason we can't copy more data, we're done! */ if (count == 0) break; if (port->type == PORT_SCI) { - tty->flip.char_buf_ptr[0] = sci_in(port, SCxRDR); - tty->flip.flag_buf_ptr[0] = TTY_NORMAL; + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr++ = sci_in(port, SCxRDR); + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + tty->flip.count++; + port->icount.rx++; + copied++; + count--; + } } else { - for (i=0; i 0 && tty->flip.count < TTY_FLIPBUF_SIZE){ char c = sci_in(port, SCxRDR); status = sci_in(port, SCxSR); + #if defined(__SH3__) /* Skip "chars" during break */ if (port->break_flag) { if ((c == 0) && (status & SCxSR_FER(port))) { - count--; i--; + count--; continue; } /* Nonzero => end-of-break */ dprintk("scif: debounce<%02x>\n", c); port->break_flag = 0; if (STEPFN(c)) { - count--; i--; + count--; continue; } } @@ -706,7 +795,7 @@ handle_sysrq(c, regs, NULL, NULL); break_pressed = 0; - count--; i--; + count--; continue; } else if (c != 0) { break_pressed = 0; @@ -715,29 +804,31 @@ #endif /* CONFIG_SERIAL_CONSOLE && CONFIG_MAGIC_SYSRQ */ /* Store data and status */ - tty->flip.char_buf_ptr[i] = c; + *tty->flip.char_buf_ptr++ = c; + if (status&SCxSR_FER(port)) { - tty->flip.flag_buf_ptr[i] = TTY_FRAME; + *tty->flip.flag_buf_ptr++ = TTY_FRAME; dprintk("sci: frame error\n"); } else if (status&SCxSR_PER(port)) { - tty->flip.flag_buf_ptr[i] = TTY_PARITY; + *tty->flip.flag_buf_ptr++ = TTY_PARITY; dprintk("sci: parity error\n"); } else { - tty->flip.flag_buf_ptr[i] = TTY_NORMAL; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; } + tty->flip.count++; + port->icount.rx++; + copied++; + count--; } } - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - - /* Update the kernel buffer end */ - tty->flip.count += count; - tty->flip.char_buf_ptr += count; - tty->flip.flag_buf_ptr += count; - - copied += count; - port->icount.rx += count; + /* drop any remaining chars, we are full */ + if (count > 0) { + /* force an overrun error on last received char */ + tty->flip.flag_buf_ptr[TTY_FLIPBUF_SIZE - 1] = TTY_OVERRUN; + while (count-- > 0) + (void) sci_in(port, SCxRDR); + } } if (copied) @@ -821,9 +912,11 @@ *tty->flip.flag_buf_ptr++ = TTY_BREAK; dprintk("sci: BREAK detected\n"); } +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) break_continue: +#endif -#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_ST40) +#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined (CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40) /* XXX: Handle SCIF overrun error */ if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { sci_out(port, SCLSR, 0); @@ -994,6 +1087,15 @@ static int sci_get_CD(void * ptr) { /* If you have signal for CD (Carrier Detect), please change here. */ + +#if defined(CONFIG_SH_SECUREEDGE5410) + struct sci_port *port = ptr; + + if (port == &sci_ports[0] || port == &sci_ports[1]) + if ((sci_getsignals(port) & TIOCM_CAR) == 0) + return 0; +#endif + return 1; } @@ -1409,6 +1511,18 @@ (port->type == PORT_SCI) ? "SCI" : "SCIF"); } +#if defined(CONFIG_SH_SECUREEDGE5410) + init_timer(&sci_timer_struct); + sci_timer_struct.function = sci_timer; + sci_timer_struct.data = 0; + sci_timer_struct.expires = jiffies + HZ/25; + add_timer(&sci_timer_struct); + + j = SECUREEDGE_READ_IOPORT(); + sci_dcdstatus[0] = !(j & 0x10); + sci_dcdstatus[1] = !(j & 0x1); +#endif + sci_init_drivers(); #ifdef CONFIG_SH_STANDARD_BIOS @@ -1425,6 +1539,9 @@ void cleanup_module(void) { +#if defined(CONFIG_SH_SECUREEDGE5410) + del_timer(&sci_timer_struct); +#endif tty_unregister_driver(&sci_driver); tty_unregister_driver(&sci_callout_driver); } @@ -1495,6 +1612,9 @@ case 115200: cflag |= B115200; break; + case 230400: + cflag |= B230400; + break; case 9600: default: cflag |= B9600; diff -urN linux-2.4.22-bk23/drivers/char/sh-sci.h linux-2.4.22-bk24/drivers/char/sh-sci.h --- linux-2.4.22-bk23/drivers/char/sh-sci.h 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.22-bk24/drivers/char/sh-sci.h 2003-09-23 03:07:15.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: sh-sci.h,v 1.1.1.1.2.3 2003/07/16 18:46:06 yoshii Exp $ +/* $Id: sh-sci.h,v 1.1.1.1.2.5 2003/09/03 08:37:41 kkojima Exp $ * * linux/drivers/char/sh-sci.h * @@ -72,6 +72,15 @@ 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) # define SCI_AND_SCIF +#elif defined(CONFIG_CPU_SUBTYPE_SH4_202) +# define SCI_NPORTS 1 +# define SCI_INIT { \ + { {}, PORT_SCIF, 0xFFE80000, SH4_SCIF_IRQS, sci_init_pins_scif } \ +} +# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +# define SCIF_ONLY #elif defined(CONFIG_CPU_SUBTYPE_ST40) # define SCI_NPORTS 2 # define SCI_INIT { \ @@ -210,29 +219,6 @@ /* Generic serial flags */ #define SCI_RX_THROTTLE 0x0000001 -/* generic serial tty */ -#define O_OTHER(tty) \ - ((O_OLCUC(tty)) ||\ - (O_ONLCR(tty)) ||\ - (O_OCRNL(tty)) ||\ - (O_ONOCR(tty)) ||\ - (O_ONLRET(tty)) ||\ - (O_OFILL(tty)) ||\ - (O_OFDEL(tty)) ||\ - (O_NLDLY(tty)) ||\ - (O_CRDLY(tty)) ||\ - (O_TABDLY(tty)) ||\ - (O_BSDLY(tty)) ||\ - (O_VTDLY(tty)) ||\ - (O_FFDLY(tty))) - -#define I_OTHER(tty) \ - ((I_INLCR(tty)) ||\ - (I_IGNCR(tty)) ||\ - (I_ICRNL(tty)) ||\ - (I_IUCLC(tty)) ||\ - (L_ISIG(tty))) - #define SCI_MAGIC 0xbabeface /* @@ -370,7 +356,7 @@ return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */ return 1; } -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH4_202) static inline int sci_rxd_in(struct sci_port *port) { #ifndef SCIF_ONLY diff -urN linux-2.4.22-bk23/drivers/net/net_init.c linux-2.4.22-bk24/drivers/net/net_init.c --- linux-2.4.22-bk23/drivers/net/net_init.c 2003-09-23 03:06:55.000000000 -0700 +++ linux-2.4.22-bk24/drivers/net/net_init.c 2003-09-23 03:07:17.000000000 -0700 @@ -420,7 +420,7 @@ dev->hard_header_len = ETH_HLEN; dev->mtu = 1500; /* eth_mtu */ dev->addr_len = ETH_ALEN; - dev->tx_queue_len = 100; /* Ethernet wants good queues */ + dev->tx_queue_len = 1000; /* Ethernet wants good queues */ memset(dev->broadcast,0xFF, ETH_ALEN); diff -urN linux-2.4.22-bk23/fs/proc/array.c linux-2.4.22-bk24/fs/proc/array.c --- linux-2.4.22-bk23/fs/proc/array.c 2003-06-13 07:51:37.000000000 -0700 +++ linux-2.4.22-bk24/fs/proc/array.c 2003-09-23 03:07:18.000000000 -0700 @@ -70,6 +70,7 @@ #include #include #include +#include #include #include @@ -511,179 +512,96 @@ size, resident, share, trs, lrs, drs, dt); } -/* - * The way we support synthetic files > 4K - * - without storing their contents in some buffer and - * - without walking through the entire synthetic file until we reach the - * position of the requested data - * is to cleverly encode the current position in the file's f_pos field. - * There is no requirement that a read() call which returns `count' bytes - * of data increases f_pos by exactly `count'. - * - * This idea is Linus' one. Bruno implemented it. - */ - -/* - * For the /proc//maps file, we use fixed length records, each containing - * a single line. - * - * f_pos = (number of the vma in the task->mm->mmap list) * PAGE_SIZE - * + (index into the line) - */ -/* for systems with sizeof(void*) == 4: */ -#define MAPS_LINE_FORMAT4 "%08lx-%08lx %s %08lx %s %lu" -#define MAPS_LINE_MAX4 49 /* sum of 8 1 8 1 4 1 8 1 5 1 10 1 */ - -/* for systems with sizeof(void*) == 8: */ -#define MAPS_LINE_FORMAT8 "%016lx-%016lx %s %016lx %s %lu" -#define MAPS_LINE_MAX8 73 /* sum of 16 1 16 1 4 1 16 1 5 1 10 1 */ - -#define MAPS_LINE_FORMAT (sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8) -#define MAPS_LINE_MAX (sizeof(void*) == 4 ? MAPS_LINE_MAX4 : MAPS_LINE_MAX8) - -static int proc_pid_maps_get_line (char *buf, struct vm_area_struct *map) -{ - /* produce the next line */ - char *line; - char str[5]; - int flags; - kdev_t dev; - unsigned long ino; +static int show_map(struct seq_file *m, void *v) +{ + struct vm_area_struct *map = v; + struct file *file = map->vm_file; + int flags = map->vm_flags; + unsigned long ino = 0; + dev_t dev = 0; int len; - flags = map->vm_flags; - - str[0] = flags & VM_READ ? 'r' : '-'; - str[1] = flags & VM_WRITE ? 'w' : '-'; - str[2] = flags & VM_EXEC ? 'x' : '-'; - str[3] = flags & VM_MAYSHARE ? 's' : 'p'; - str[4] = 0; - - dev = 0; - ino = 0; - if (map->vm_file != NULL) { - dev = map->vm_file->f_dentry->d_inode->i_dev; - ino = map->vm_file->f_dentry->d_inode->i_ino; - line = d_path(map->vm_file->f_dentry, - map->vm_file->f_vfsmnt, - buf, PAGE_SIZE); - if (IS_ERR(line)) - return PTR_ERR(line); - buf[PAGE_SIZE-1] = '\n'; - line -= MAPS_LINE_MAX; - if(line < buf) - line = buf; - } else - line = buf; - - len = sprintf(line, - MAPS_LINE_FORMAT, - map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT, - kdevname(dev), ino); - - if(map->vm_file) { - int i; - for(i = len; i < MAPS_LINE_MAX; i++) - line[i] = ' '; - len = buf + PAGE_SIZE - line; - memmove(buf, line, len); - } else - line[len++] = '\n'; - return len; + if (file) { + struct inode *inode = map->vm_file->f_dentry->d_inode; + dev = kdev_t_to_nr(inode->i_sb->s_dev); + ino = inode->i_ino; + } + + seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n", + map->vm_start, + map->vm_end, + flags & VM_READ ? 'r' : '-', + flags & VM_WRITE ? 'w' : '-', + flags & VM_EXEC ? 'x' : '-', + flags & VM_MAYSHARE ? 's' : 'p', + map->vm_pgoff << PAGE_SHIFT, + MAJOR(dev), MINOR(dev), ino, &len); + + if (map->vm_file) { + len = 25 + sizeof(void*) * 6 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c", len, ' '); + seq_path(m, file->f_vfsmnt, file->f_dentry, " \t\n\\"); + } + seq_putc(m, '\n'); + return 0; } -ssize_t proc_pid_read_maps (struct task_struct *task, struct file * file, char * buf, - size_t count, loff_t *ppos) +static void *m_start(struct seq_file *m, loff_t *pos) { + struct task_struct *task = m->private; struct mm_struct *mm; struct vm_area_struct * map; - char *tmp, *kbuf; - long retval; - int off, lineno, loff; - - /* reject calls with out of range parameters immediately */ - retval = 0; - if (*ppos > LONG_MAX) - goto out; - if (count == 0) - goto out; - off = (long)*ppos; - /* - * We might sleep getting the page, so get it first. - */ - retval = -ENOMEM; - kbuf = (char*)__get_free_page(GFP_KERNEL); - if (!kbuf) - goto out; - - tmp = (char*)__get_free_page(GFP_KERNEL); - if (!tmp) - goto out_free1; + loff_t l = *pos; task_lock(task); mm = task->mm; if (mm) atomic_inc(&mm->mm_users); task_unlock(task); - retval = 0; + if (!mm) - goto out_free2; + return NULL; down_read(&mm->mmap_sem); map = mm->mmap; - lineno = 0; - loff = 0; - if (count > PAGE_SIZE) - count = PAGE_SIZE; - while (map) { - int len; - if (off > PAGE_SIZE) { - off -= PAGE_SIZE; - goto next; - } - len = proc_pid_maps_get_line(tmp, map); - if (len < 0) - goto out_unlock; - len -= off; - if (len > 0) { - if (retval+len > count) { - /* only partial line transfer possible */ - len = count - retval; - /* save the offset where the next read - * must start */ - loff = len+off; - } - memcpy(kbuf+retval, tmp+off, len); - retval += len; - } - off = 0; -next: - if (!loff) - lineno++; - if (retval >= count) - break; - if (loff) BUG(); + while (l-- && map) map = map->vm_next; + if (!map) { + up_read(&mm->mmap_sem); + mmput(mm); } + return map; +} -out_unlock: - up_read(&mm->mmap_sem); - mmput(mm); +static void m_stop(struct seq_file *m, void *v) +{ + struct vm_area_struct *map = v; + if (map) { + struct mm_struct *mm = map->vm_mm; + up_read(&mm->mmap_sem); + mmput(mm); + } +} - if (retval > count) BUG(); - if (copy_to_user(buf, kbuf, retval)) - retval = -EFAULT; - else - *ppos = (lineno << PAGE_SHIFT) + loff; - -out_free2: - free_page((unsigned long)tmp); -out_free1: - free_page((unsigned long)kbuf); -out: - return retval; +static void *m_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct vm_area_struct *map = v; + (*pos)++; + if (map->vm_next) + return map->vm_next; + m_stop(m, v); + return NULL; } +struct seq_operations proc_pid_maps_op = { + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_map +}; + #ifdef CONFIG_SMP int proc_pid_cpu(struct task_struct *task, char * buffer) { diff -urN linux-2.4.22-bk23/fs/proc/base.c linux-2.4.22-bk24/fs/proc/base.c --- linux-2.4.22-bk23/fs/proc/base.c 2003-08-25 04:44:43.000000000 -0700 +++ linux-2.4.22-bk24/fs/proc/base.c 2003-09-23 03:07:18.000000000 -0700 @@ -36,7 +36,6 @@ #define fake_ino(pid,ino) (((pid)<<16)|(ino)) -ssize_t proc_pid_read_maps(struct task_struct*,struct file*,char*,size_t,loff_t*); int proc_pid_stat(struct task_struct*,char*); int proc_pid_status(struct task_struct*,char*); int proc_pid_statm(struct task_struct*,char*); @@ -269,19 +268,23 @@ return proc_check_root(inode); } -static ssize_t pid_maps_read(struct file * file, char * buf, - size_t count, loff_t *ppos) +extern struct seq_operations proc_pid_maps_op; +static int maps_open(struct inode *inode, struct file *file) { - struct inode * inode = file->f_dentry->d_inode; struct task_struct *task = inode->u.proc_i.task; - ssize_t res; - - res = proc_pid_read_maps(task, file, buf, count, ppos); - return res; + int ret = seq_open(file, &proc_pid_maps_op); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = task; + } + return ret; } static struct file_operations proc_maps_operations = { - read: pid_maps_read, + .open = maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, }; extern struct seq_operations mounts_op; diff -urN linux-2.4.22-bk23/include/asm-sh/checksum.h linux-2.4.22-bk24/include/asm-sh/checksum.h --- linux-2.4.22-bk23/include/asm-sh/checksum.h 2001-09-08 12:29:09.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh/checksum.h 2003-09-23 03:07:18.000000000 -0700 @@ -169,7 +169,6 @@ } #define _HAVE_ARCH_IPV6_CSUM -#ifdef CONFIG_IPV6 static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr, __u32 len, @@ -205,7 +204,6 @@ return csum_fold(sum); } -#endif /* * Copy and checksum to user diff -urN linux-2.4.22-bk23/include/asm-sh/delay.h linux-2.4.22-bk24/include/asm-sh/delay.h --- linux-2.4.22-bk23/include/asm-sh/delay.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh/delay.h 2003-09-23 03:07:18.000000000 -0700 @@ -8,6 +8,7 @@ */ extern void __bad_udelay(void); +extern void __bad_ndelay(void); extern void __udelay(unsigned long usecs); extern void __ndelay(unsigned long nsecs); diff -urN linux-2.4.22-bk23/include/asm-sh/div64.h linux-2.4.22-bk24/include/asm-sh/div64.h --- linux-2.4.22-bk23/include/asm-sh/div64.h 2000-03-05 09:33:55.000000000 -0800 +++ linux-2.4.22-bk24/include/asm-sh/div64.h 2003-09-23 03:07:18.000000000 -0700 @@ -1,10 +1,20 @@ #ifndef __ASM_SH_DIV64 #define __ASM_SH_DIV64 +extern u64 __xdiv64_32(u64 n, u32 d); + #define do_div(n,base) ({ \ -int __res; \ -__res = ((unsigned long) n) % (unsigned) base; \ -n = ((unsigned long) n) / (unsigned) base; \ +u64 __n = (n), __q; \ +u32 __base = (base); \ +u32 __res; \ +if ((__n >> 32) == 0) { \ + __res = ((unsigned long) __n) % (unsigned) __base; \ + (n) = ((unsigned long) __n) / (unsigned) __base; \ +} else { \ + __q = __xdiv64_32(__n, __base); \ + __res = __n - __q * __base; \ + (n) = __q; \ +} \ __res; }) #endif /* __ASM_SH_DIV64 */ diff -urN linux-2.4.22-bk23/include/asm-sh/io.h linux-2.4.22-bk24/include/asm-sh/io.h --- linux-2.4.22-bk23/include/asm-sh/io.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh/io.h 2003-09-23 03:07:18.000000000 -0700 @@ -135,6 +135,8 @@ # include # elif defined(CONFIG_SH_SECUREEDGE5410) # include +# elif defined(CONFIG_SH_SH4202_MICRODEV) +# include # elif defined(CONFIG_SH_UNKNOWN) # include # else diff -urN linux-2.4.22-bk23/include/asm-sh/io_microdev.h linux-2.4.22-bk24/include/asm-sh/io_microdev.h --- linux-2.4.22-bk23/include/asm-sh/io_microdev.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/asm-sh/io_microdev.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,82 @@ +/* + * linux/include/asm-sh/io_microdev.h + * + * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com) + * + * IO functions for the SuperH SH4-202 MicroDev board. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + */ + + +#ifndef _ASM_SH_IO_MICRODEV_H +#define _ASM_SH_IO_MICRODEV_H + +#include + +extern unsigned long microdev_isa_port2addr(unsigned long offset); + +extern unsigned char microdev_inb(unsigned long port); +extern unsigned short microdev_inw(unsigned long port); +extern unsigned int microdev_inl(unsigned long port); + +extern void microdev_outb(unsigned char value, unsigned long port); +extern void microdev_outw(unsigned short value, unsigned long port); +extern void microdev_outl(unsigned int value, unsigned long port); + +extern unsigned char microdev_inb_p(unsigned long port); +extern unsigned short microdev_inw_p(unsigned long port); +extern unsigned int microdev_inl_p(unsigned long port); + +extern void microdev_outb_p(unsigned char value, unsigned long port); +extern void microdev_outw_p(unsigned short value, unsigned long port); +extern void microdev_outl_p(unsigned int value, unsigned long port); + +extern void microdev_insb(unsigned long port, void *addr, unsigned long count); +extern void microdev_insw(unsigned long port, void *addr, unsigned long count); +extern void microdev_insl(unsigned long port, void *addr, unsigned long count); + +extern void microdev_outsb(unsigned long port, const void *addr, unsigned long count); +extern void microdev_outsw(unsigned long port, const void *addr, unsigned long count); +extern void microdev_outsl(unsigned long port, const void *addr, unsigned long count); + +#ifdef __WANT_IO_DEF + +# define __inb microdev_inb +# define __inw microdev_inw +# define __inl microdev_inl +# define __outb microdev_outb +# define __outw microdev_outw +# define __outl microdev_outl + +# define __inb_p microdev_inb_p +# define __inw_p microdev_inw_p +# define __inl_p microdev_inl_p +# define __outb_p microdev_outb_p +# define __outw_p microdev_outw_p +# define __outl_p microdev_outl_p + +# define __insb microdev_insb +# define __insw microdev_insw +# define __insl microdev_insl +# define __outsb microdev_outsb +# define __outsw microdev_outsw +# define __outsl microdev_outsl + +# define __readb generic_readb +# define __readw generic_readw +# define __readl generic_readl +# define __writeb generic_writeb +# define __writew generic_writew +# define __writel generic_writel + +# define __isa_port2addr microdev_isa_port2addr +# define __ioremap generic_ioremap +# define __iounmap generic_iounmap + +#endif + +#endif /* _ASM_SH_IO_MICRODEV_H */ + diff -urN linux-2.4.22-bk23/include/asm-sh/irq.h linux-2.4.22-bk24/include/asm-sh/irq.h --- linux-2.4.22-bk23/include/asm-sh/irq.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh/irq.h 2003-09-23 03:07:18.000000000 -0700 @@ -86,7 +86,7 @@ #define IRDA_IPR_POS 2 #define IRDA_PRIORITY 3 #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_ST40) + defined(CONFIG_CPU_SUBTYPE_ST40) || defined(CONFIG_CPU_SUBTYPE_SH4_202) #define SCIF_ERI_IRQ 40 #define SCIF_RXI_IRQ 41 #define SCIF_BRI_IRQ 42 @@ -127,6 +127,8 @@ # define ONCHIP_NR_IRQS 48 // Actually 44 # elif defined(CONFIG_CPU_SUBTYPE_SH7751) # define ONCHIP_NR_IRQS 72 +# elif defined(CONFIG_CPU_SUBTYPE_SH4_202) +# define ONCHIP_NR_IRQS 72 # elif defined(CONFIG_CPU_SUBTYPE_ST40STB1) # define ONCHIP_NR_IRQS 144 # elif defined(CONFIG_CPU_SUBTYPE_ST40GX1) @@ -275,7 +277,7 @@ #endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 */ #if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_ST40) + defined(CONFIG_CPU_SUBTYPE_ST40) || defined(CONFIG_CPU_SUBTYPE_SH4_202) #define INTC_ICR 0xffd00000 #define INTC_ICR_NMIL (1<<15) #define INTC_ICR_MAI (1<<14) diff -urN linux-2.4.22-bk23/include/asm-sh/irq_microdev.h linux-2.4.22-bk24/include/asm-sh/irq_microdev.h --- linux-2.4.22-bk23/include/asm-sh/irq_microdev.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/asm-sh/irq_microdev.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,38 @@ +/* + * linux/include/asm-sh/irq_microdev.h + * + * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com) + * + * IRQ functions for the SuperH SH4-202 MicroDev board. + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + */ + + +#ifndef _ASM_SH_IRQ_MICRODEV_H +#define _ASM_SH_IRQ_MICRODEV_H + +extern void __init init_microdev_irq(void); + + + /* + * The following are useful macros for manipulating the + * interrupt controller (INTC) on the CPU-board FPGA. + * It should be noted that there is an INTC on the FPGA, + * and a seperate INTC on the SH4-202 core - these are + * two different things, both of which need to be prorammed + * to correctly route - unfortunately, they have the + * same name and abbreviations! + */ +#define MICRODEV_FPGA_INTC_BASE 0xa6110000ul /* INTC base address on CPU-board FPGA */ +#define MICRODEV_FPGA_INTENB_REG (MICRODEV_FPGA_INTC_BASE+0ul) /* Interrupt Enable Register on INTC on CPU-board FPGA */ +#define MICRODEV_FPGA_INTDSB_REG (MICRODEV_FPGA_INTC_BASE+8ul) /* Interrupt Disable Register on INTC on CPU-board FPGA */ +#define MICRODEV_FPGA_INTC_MASK(n) (1ul<<(n)) /* Interupt mask to enable/disable INTC in CPU-board FPGA */ +#define MICRODEV_FPGA_INTPRI_REG(n) (MICRODEV_FPGA_INTC_BASE+0x10+((n)/8)*8)/* Interrupt Priority Register on INTC on CPU-board FPGA */ +#define MICRODEV_FPGA_INTPRI_LEVEL(n,x) ((x)<<(((n)%8)*4)) /* MICRODEV_FPGA_INTPRI_LEVEL(int_number, int_level) */ +#define MICRODEV_FPGA_INTPRI_MASK(n) (MICRODEV_FPGA_INTPRI_LEVEL((n),0xful)) /* Interrupt Priority Mask on INTC on CPU-board FPGA */ + + +#endif /* _ASM_SH_IRQ_MICRODEV_H */ diff -urN linux-2.4.22-bk23/include/asm-sh/machvec.h linux-2.4.22-bk24/include/asm-sh/machvec.h --- linux-2.4.22-bk23/include/asm-sh/machvec.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh/machvec.h 2003-09-23 03:07:18.000000000 -0700 @@ -81,6 +81,7 @@ unsigned int mv_hw_7751se: 1; unsigned int mv_hw_adx : 1; unsigned int mv_hw_snapgear : 1; + unsigned int mv_hw_sh4202_microdev : 1; }; extern struct sh_machine_vector sh_mv; @@ -102,6 +103,7 @@ #define MACH_7751SE (sh_mv.mv_hw_7751se) #define MACH_ADX (sh_mv.mv_hw_adx) #define MACH_SNAPGEAR (sh_mv.mv_snapgear) +#define MACH_SH4202_MICRODEV (sh_mv.mv_hw_sh4202_microdev) #else # ifdef CONFIG_SH_SOLUTION_ENGINE # define MACH_SE 1 @@ -183,6 +185,11 @@ # else # define MACH_SNAPGEAR 0 # endif +# ifdef CONFIG_SH_SH4202_MICRODEV +# define MACH_SH4202_MICRODEV 1 +# else +# define MACH_SH4202_MICRODEV 0 +# endif #endif #endif /* _ASM_SH_MACHVEC_H */ diff -urN linux-2.4.22-bk23/include/asm-sh/processor.h linux-2.4.22-bk24/include/asm-sh/processor.h --- linux-2.4.22-bk23/include/asm-sh/processor.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh/processor.h 2003-09-23 03:07:18.000000000 -0700 @@ -23,8 +23,9 @@ enum cpu_type { CPU_SH7708, /* Represents 7707, 7708, 7708S, 7708R, 7709 */ CPU_SH7729, /* Represents 7709A, 7729 */ - CPU_SH7750, /* Represents 7750, 7751 */ + CPU_SH7750, /* Represents 7750, 7751 */ CPU_ST40, /* Represents ST40STB1 and ST40GX1 */ + CPU_SH4202, CPU_SH_NONE }; diff -urN linux-2.4.22-bk23/include/asm-sh64/processor.h linux-2.4.22-bk24/include/asm-sh64/processor.h --- linux-2.4.22-bk23/include/asm-sh64/processor.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh64/processor.h 2003-09-23 03:07:18.000000000 -0700 @@ -113,11 +113,12 @@ #ifndef __ASSEMBLY__ /* - * FPU structure and data + * FPU structure and data : require 8-byte alignment as we need to access it + with fld.p, fst.p */ struct sh_fpu_hard_struct { - unsigned long long fp_regs[32]; + unsigned long fp_regs[64]; unsigned int fpscr; /* long status; * software status information */ }; @@ -135,6 +136,9 @@ union sh_fpu_union { struct sh_fpu_hard_struct hard; // struct sh_fpu_soft_struct soft; + /* 'hard' itself only produces 32 bit alignment, yet we need + to access it using 64 bit load/store as well. */ + unsigned long long alignment_dummy; }; struct thread_struct { diff -urN linux-2.4.22-bk23/include/asm-sh64/registers.h linux-2.4.22-bk24/include/asm-sh64/registers.h --- linux-2.4.22-bk23/include/asm-sh64/registers.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/asm-sh64/registers.h 2003-09-23 03:07:18.000000000 -0700 @@ -188,6 +188,7 @@ ** defines below are used only in .../arch/sh5/kernel/fpu.c */ # define __f(x) __str(fr##x) +# define __p(x) __str(fp##x) # define __d(x) __str(dr##x) /* diff -urN linux-2.4.22-bk23/include/linux/in.h linux-2.4.22-bk24/include/linux/in.h --- linux-2.4.22-bk23/include/linux/in.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/linux/in.h 2003-09-23 03:07:18.000000000 -0700 @@ -41,6 +41,7 @@ IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ IPPROTO_AH = 51, /* Authentication Header protocol */ IPPROTO_COMP = 108, /* Compression Header protocol */ + IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */ IPPROTO_RAW = 255, /* Raw IP packets */ IPPROTO_MAX diff -urN linux-2.4.22-bk23/include/linux/ipv6.h linux-2.4.22-bk24/include/linux/ipv6.h --- linux-2.4.22-bk23/include/linux/ipv6.h 2003-09-23 03:06:57.000000000 -0700 +++ linux-2.4.22-bk24/include/linux/ipv6.h 2003-09-23 03:07:18.000000000 -0700 @@ -100,6 +100,52 @@ struct in6_addr daddr; }; +/* + * This structure contains configuration options per IPv6 link. + */ +struct ipv6_devconf { + __s32 forwarding; + __s32 hop_limit; + __s32 mtu6; + __s32 accept_ra; + __s32 accept_redirects; + __s32 autoconf; + __s32 dad_transmits; + __s32 rtr_solicits; + __s32 rtr_solicit_interval; + __s32 rtr_solicit_delay; +#ifdef CONFIG_IPV6_PRIVACY + __s32 use_tempaddr; + __s32 temp_valid_lft; + __s32 temp_prefered_lft; + __s32 regen_max_retry; + __s32 max_desync_factor; +#endif + void *sysctl; +}; + +/* index values for the variables in ipv6_devconf */ +enum { + DEVCONF_FORWARDING = 0, + DEVCONF_HOPLIMIT, + DEVCONF_MTU6, + DEVCONF_ACCEPT_RA, + DEVCONF_ACCEPT_REDIRECTS, + DEVCONF_AUTOCONF, + DEVCONF_DAD_TRANSMITS, + DEVCONF_RTR_SOLICITS, + DEVCONF_RTR_SOLICIT_INTERVAL, + DEVCONF_RTR_SOLICIT_DELAY, +#ifdef CONFIG_IPV6_PRIVACY + DEVCONF_USE_TEMPADDR, + DEVCONF_TEMP_VALID_LFT, + DEVCONF_TEMP_PREFERED_LFT, + DEVCONF_REGEN_MAX_RETRY, + DEVCONF_MAX_DESYNC_FACTOR, +#endif + DEVCONF_MAX +}; + #ifdef __KERNEL__ /* diff -urN linux-2.4.22-bk23/include/linux/net.h linux-2.4.22-bk24/include/linux/net.h --- linux-2.4.22-bk23/include/linux/net.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/linux/net.h 2003-09-23 03:07:18.000000000 -0700 @@ -141,6 +141,7 @@ const struct iovec * iov, long count, long size); extern struct socket *sockfd_lookup(int fd, int *err); +extern int sock_map_fd(struct socket *sock); extern int net_ratelimit(void); extern unsigned long net_random(void); extern void net_srandom(unsigned long); diff -urN linux-2.4.22-bk23/include/linux/rtnetlink.h linux-2.4.22-bk24/include/linux/rtnetlink.h --- linux-2.4.22-bk23/include/linux/rtnetlink.h 2003-09-23 03:06:57.000000000 -0700 +++ linux-2.4.22-bk24/include/linux/rtnetlink.h 2003-09-23 03:07:18.000000000 -0700 @@ -445,10 +445,12 @@ #define IFLA_MASTER IFLA_MASTER IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ #define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO }; -#define IFLA_MAX IFLA_WIRELESS +#define IFLA_MAX IFLA_PROTINFO #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) @@ -482,6 +484,18 @@ for IPIP tunnels, when route to endpoint is allowed to change) */ +/* Subtype attributes for IFLA_PROTINFO */ +enum +{ + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ +}; + +#define IFLA_INET6_MAX IFLA_INET6_MCAST + /***************************************************************** * Traffic control messages. ****/ diff -urN linux-2.4.22-bk23/include/linux/sctp.h linux-2.4.22-bk24/include/linux/sctp.h --- linux-2.4.22-bk23/include/linux/sctp.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/linux/sctp.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,588 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2002 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * Various protocol defined structures. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Xingang Guo + * randall@sctp.chicago.il.us + * kmorneau@cisco.com + * qxie1@email.mot.com + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ +#ifndef __LINUX_SCTP_H__ +#define __LINUX_SCTP_H__ + +#include /* We need in_addr. */ +#include /* We need in6_addr. */ + + +/* Section 3.1. SCTP Common Header Format */ +typedef struct sctphdr { + __u16 source; + __u16 dest; + __u32 vtag; + __u32 checksum; +} sctp_sctphdr_t __attribute__((packed)); + +/* Section 3.2. Chunk Field Descriptions. */ +typedef struct sctp_chunkhdr { + __u8 type; + __u8 flags; + __u16 length; +} sctp_chunkhdr_t __attribute__((packed)); + + +/* Section 3.2. Chunk Type Values. + * [Chunk Type] identifies the type of information contained in the Chunk + * Value field. It takes a value from 0 to 254. The value of 255 is + * reserved for future use as an extension field. + */ +typedef enum { + SCTP_CID_DATA = 0, + SCTP_CID_INIT = 1, + SCTP_CID_INIT_ACK = 2, + SCTP_CID_SACK = 3, + SCTP_CID_HEARTBEAT = 4, + SCTP_CID_HEARTBEAT_ACK = 5, + SCTP_CID_ABORT = 6, + SCTP_CID_SHUTDOWN = 7, + SCTP_CID_SHUTDOWN_ACK = 8, + SCTP_CID_ERROR = 9, + SCTP_CID_COOKIE_ECHO = 10, + SCTP_CID_COOKIE_ACK = 11, + SCTP_CID_ECN_ECNE = 12, + SCTP_CID_ECN_CWR = 13, + SCTP_CID_SHUTDOWN_COMPLETE = 14, + + /* Use hex, as defined in ADDIP sec. 3.1 */ + SCTP_CID_ASCONF = 0xC1, + SCTP_CID_ASCONF_ACK = 0x80, +} sctp_cid_t; /* enum */ + + +/* Section 3.2 + * Chunk Types are encoded such that the highest-order two bits specify + * the action that must be taken if the processing endpoint does not + * recognize the Chunk Type. + */ +typedef enum { + SCTP_CID_ACTION_DISCARD = 0x00, + SCTP_CID_ACTION_DISCARD_ERR = 0x40, + SCTP_CID_ACTION_SKIP = 0x80, + SCTP_CID_ACTION_SKIP_ERR = 0xc0, +} sctp_cid_action_t; + +enum { SCTP_CID_ACTION_MASK = 0xc0, }; + +/* This flag is used in Chunk Flags for ABORT and SHUTDOWN COMPLETE. + * + * 3.3.7 Abort Association (ABORT) (6): + * The T bit is set to 0 if the sender had a TCB that it destroyed. + * If the sender did not have a TCB it should set this bit to 1. + */ +enum { SCTP_CHUNK_FLAG_T = 0x01 }; + +/* + * Set the T bit + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type = 14 |Reserved |T| Length = 4 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Chunk Flags: 8 bits + * + * Reserved: 7 bits + * Set to 0 on transmit and ignored on receipt. + * + * T bit: 1 bit + * The T bit is set to 0 if the sender had a TCB that it destroyed. If + * the sender did NOT have a TCB it should set this bit to 1. + * + * Note: Special rules apply to this chunk for verification, please + * see Section 8.5.1 for details. + */ + +#define sctp_test_T_bit(c) ((c)->chunk_hdr->flags & SCTP_CHUNK_FLAG_T) + +/* RFC 2960 + * Section 3.2.1 Optional/Variable-length Parmaeter Format. + */ + +typedef struct sctp_paramhdr { + __u16 type; + __u16 length; +} sctp_paramhdr_t __attribute((packed)); + +typedef enum { + + /* RFC 2960 Section 3.3.5 */ + SCTP_PARAM_HEARTBEAT_INFO = __constant_htons(1), + /* RFC 2960 Section 3.3.2.1 */ + SCTP_PARAM_IPV4_ADDRESS = __constant_htons(5), + SCTP_PARAM_IPV6_ADDRESS = __constant_htons(6), + SCTP_PARAM_STATE_COOKIE = __constant_htons(7), + SCTP_PARAM_UNRECOGNIZED_PARAMETERS = __constant_htons(8), + SCTP_PARAM_COOKIE_PRESERVATIVE = __constant_htons(9), + SCTP_PARAM_HOST_NAME_ADDRESS = __constant_htons(11), + SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12), + SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000), + + /* Add-IP Extension. Section 3.2 */ + SCTP_PARAM_ADD_IP = __constant_htons(0xc001), + SCTP_PARAM_DEL_IP = __constant_htons(0xc002), + SCTP_PARAM_ERR_CAUSE = __constant_htons(0xc003), + SCTP_PARAM_SET_PRIMARY = __constant_htons(0xc004), + SCTP_PARAM_SUCCESS_REPORT = __constant_htons(0xc005), + SCTP_PARAM_ADAPTION_LAYER_IND = __constant_htons(0xc006), + +} sctp_param_t; /* enum */ + + +/* RFC 2960 Section 3.2.1 + * The Parameter Types are encoded such that the highest-order two bits + * specify the action that must be taken if the processing endpoint does + * not recognize the Parameter Type. + * + */ +typedef enum { + SCTP_PARAM_ACTION_DISCARD = __constant_htons(0x0000), + SCTP_PARAM_ACTION_DISCARD_ERR = __constant_htons(0x4000), + SCTP_PARAM_ACTION_SKIP = __constant_htons(0x8000), + SCTP_PARAM_ACTION_SKIP_ERR = __constant_htons(0xc000), +} sctp_param_action_t; + +enum { SCTP_PARAM_ACTION_MASK = __constant_htons(0xc000), }; + +/* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */ + +typedef struct sctp_datahdr { + __u32 tsn; + __u16 stream; + __u16 ssn; + __u32 ppid; + __u8 payload[0]; +} sctp_datahdr_t __attribute__((packed)); + +typedef struct sctp_data_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_datahdr_t data_hdr; +} sctp_data_chunk_t __attribute__((packed)); + +/* DATA Chuck Specific Flags */ +enum { + SCTP_DATA_MIDDLE_FRAG = 0x00, + SCTP_DATA_LAST_FRAG = 0x01, + SCTP_DATA_FIRST_FRAG = 0x02, + SCTP_DATA_NOT_FRAG = 0x03, + SCTP_DATA_UNORDERED = 0x04, +}; +enum { SCTP_DATA_FRAG_MASK = 0x03, }; + + +/* RFC 2960 Section 3.3.2 Initiation (INIT) (1) + * + * This chunk is used to initiate a SCTP association between two + * endpoints. + */ +typedef struct sctp_inithdr { + __u32 init_tag; + __u32 a_rwnd; + __u16 num_outbound_streams; + __u16 num_inbound_streams; + __u32 initial_tsn; + __u8 params[0]; +} sctp_inithdr_t __attribute__((packed)); + +typedef struct sctp_init_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_inithdr_t init_hdr; +} sctp_init_chunk_t __attribute__((packed)); + + +/* Section 3.3.2.1. IPv4 Address Parameter (5) */ +typedef struct sctp_ipv4addr_param { + sctp_paramhdr_t param_hdr; + struct in_addr addr; +} sctp_ipv4addr_param_t __attribute__((packed)); + +/* Section 3.3.2.1. IPv6 Address Parameter (6) */ +typedef struct sctp_ipv6addr_param { + sctp_paramhdr_t param_hdr; + struct in6_addr addr; +} sctp_ipv6addr_param_t __attribute__((packed)); + +/* Section 3.3.2.1 Cookie Preservative (9) */ +typedef struct sctp_cookie_preserve_param { + sctp_paramhdr_t param_hdr; + uint32_t lifespan_increment; +} sctp_cookie_preserve_param_t __attribute__((packed)); + +/* Section 3.3.2.1 Host Name Address (11) */ +typedef struct sctp_hostname_param { + sctp_paramhdr_t param_hdr; + uint8_t hostname[0]; +} sctp_hostname_param_t __attribute__((packed)); + +/* Section 3.3.2.1 Supported Address Types (12) */ +typedef struct sctp_supported_addrs_param { + sctp_paramhdr_t param_hdr; + uint16_t types[0]; +} sctp_supported_addrs_param_t __attribute__((packed)); + +/* Appendix A. ECN Capable (32768) */ +typedef struct sctp_ecn_capable_param { + sctp_paramhdr_t param_hdr; +} sctp_ecn_capable_param_t __attribute__((packed)); + + + +/* RFC 2960. Section 3.3.3 Initiation Acknowledgement (INIT ACK) (2): + * The INIT ACK chunk is used to acknowledge the initiation of an SCTP + * association. + */ +typedef sctp_init_chunk_t sctp_initack_chunk_t; + +/* Section 3.3.3.1 State Cookie (7) */ +typedef struct sctp_cookie_param { + sctp_paramhdr_t p; + __u8 body[0]; +} sctp_cookie_param_t __attribute__((packed)); + +/* Section 3.3.3.1 Unrecognized Parameters (8) */ +typedef struct sctp_unrecognized_param { + sctp_paramhdr_t param_hdr; + sctp_paramhdr_t unrecognized; +} sctp_unrecognized_param_t __attribute__((packed)); + + + +/* + * 3.3.4 Selective Acknowledgement (SACK) (3): + * + * This chunk is sent to the peer endpoint to acknowledge received DATA + * chunks and to inform the peer endpoint of gaps in the received + * subsequences of DATA chunks as represented by their TSNs. + */ + +typedef struct sctp_gap_ack_block { + __u16 start; + __u16 end; +} sctp_gap_ack_block_t __attribute__((packed)); + +typedef uint32_t sctp_dup_tsn_t; + +typedef union { + sctp_gap_ack_block_t gab; + sctp_dup_tsn_t dup; +} sctp_sack_variable_t; + +typedef struct sctp_sackhdr { + __u32 cum_tsn_ack; + __u32 a_rwnd; + __u16 num_gap_ack_blocks; + __u16 num_dup_tsns; + sctp_sack_variable_t variable[0]; +} sctp_sackhdr_t __attribute__((packed)); + +typedef struct sctp_sack_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_sackhdr_t sack_hdr; +} sctp_sack_chunk_t __attribute__((packed)); + + +/* RFC 2960. Section 3.3.5 Heartbeat Request (HEARTBEAT) (4): + * + * An endpoint should send this chunk to its peer endpoint to probe the + * reachability of a particular destination transport address defined in + * the present association. + */ + +typedef struct sctp_heartbeathdr { + sctp_paramhdr_t info; +} sctp_heartbeathdr_t __attribute__((packed)); + +typedef struct sctp_heartbeat_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_heartbeathdr_t hb_hdr; +} sctp_heartbeat_chunk_t __attribute__((packed)); + + +/* For the abort and shutdown ACK we must carry the init tag in the + * common header. Just the common header is all that is needed with a + * chunk descriptor. + */ +typedef struct sctp_abort_chunk { + sctp_chunkhdr_t uh; +} sctp_abort_chunkt_t __attribute__((packed)); + + +/* For the graceful shutdown we must carry the tag (in common header) + * and the highest consecutive acking value. + */ +typedef struct sctp_shutdownhdr { + __u32 cum_tsn_ack; +} sctp_shutdownhdr_t __attribute__((packed)); + +struct sctp_shutdown_chunk_t { + sctp_chunkhdr_t chunk_hdr; + sctp_shutdownhdr_t shutdown_hdr; +} __attribute__((packed)); + + + +/* RFC 2960. Section 3.3.10 Operation Error (ERROR) (9) */ + +typedef struct sctp_errhdr { + __u16 cause; + __u16 length; + __u8 variable[0]; +} sctp_errhdr_t __attribute__((packed)); + +typedef struct sctp_operr_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_errhdr_t err_hdr; +} sctp_operr_chunk_t __attribute__((packed)); + +/* RFC 2960 3.3.10 - Operation Error + * + * Cause Code: 16 bits (unsigned integer) + * + * Defines the type of error conditions being reported. + * Cause Code + * Value Cause Code + * --------- ---------------- + * 1 Invalid Stream Identifier + * 2 Missing Mandatory Parameter + * 3 Stale Cookie Error + * 4 Out of Resource + * 5 Unresolvable Address + * 6 Unrecognized Chunk Type + * 7 Invalid Mandatory Parameter + * 8 Unrecognized Parameters + * 9 No User Data + * 10 Cookie Received While Shutting Down + */ +typedef enum { + + SCTP_ERROR_NO_ERROR = __constant_htons(0x00), + SCTP_ERROR_INV_STRM = __constant_htons(0x01), + SCTP_ERROR_MISS_PARAM = __constant_htons(0x02), + SCTP_ERROR_STALE_COOKIE = __constant_htons(0x03), + SCTP_ERROR_NO_RESOURCE = __constant_htons(0x04), + SCTP_ERROR_DNS_FAILED = __constant_htons(0x05), + SCTP_ERROR_UNKNOWN_CHUNK = __constant_htons(0x06), + SCTP_ERROR_INV_PARAM = __constant_htons(0x07), + SCTP_ERROR_UNKNOWN_PARAM = __constant_htons(0x08), + SCTP_ERROR_NO_DATA = __constant_htons(0x09), + SCTP_ERROR_COOKIE_IN_SHUTDOWN = __constant_htons(0x0a), + + + /* SCTP Implementation Guide: + * 11 Restart of an association with new addresses + * 12 User Initiated Abort + * 13 Protocol Violation + */ + + SCTP_ERROR_RESTART = __constant_htons(0x0b), + SCTP_ERROR_USER_ABORT = __constant_htons(0x0c), + SCTP_ERROR_PROTO_VIOLATION = __constant_htons(0x0d), + + /* ADDIP Section 3.3 New Error Causes + * + * Four new Error Causes are added to the SCTP Operational Errors, + * primarily for use in the ASCONF-ACK chunk. + * + * Value Cause Code + * --------- ---------------- + * 0x0100 Request to Delete Last Remaining IP Address. + * 0x0101 Operation Refused Due to Resource Shortage. + * 0x0102 Request to Delete Source IP Address. + * 0x0103 Association Aborted due to illegal ASCONF-ACK + */ + SCTP_ERROR_DEL_LAST_IP = __constant_htons(0x0100), + SCTP_ERROR_RSRC_LOW = __constant_htons(0x0101), + SCTP_ERROR_DEL_SRC_IP = __constant_htons(0x0102), + SCTP_ERROR_ASCONF_ACK = __constant_htons(0x0103), + +} sctp_error_t; + + + +/* RFC 2960. Appendix A. Explicit Congestion Notification. + * Explicit Congestion Notification Echo (ECNE) (12) + */ +typedef struct sctp_ecnehdr { + __u32 lowest_tsn; +} sctp_ecnehdr_t; + +typedef struct sctp_ecne_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_ecnehdr_t ence_hdr; +} sctp_ecne_chunk_t __attribute__((packed)); + +/* RFC 2960. Appendix A. Explicit Congestion Notification. + * Congestion Window Reduced (CWR) (13) + */ +typedef struct sctp_cwrhdr { + __u32 lowest_tsn; +} sctp_cwrhdr_t; + +typedef struct sctp_cwr_chunk { + sctp_chunkhdr_t chunk_hdr; + sctp_cwrhdr_t cwr_hdr; +} sctp_cwr_chunk_t __attribute__((packed)); + + +/* FIXME: Cleanup needs to continue below this line. */ + +/* + * ADDIP Section 3.1 New Chunk Types + */ + + +/* ADDIP Section 3.1.1 + * + * ASCONF-Request Correlation ID: 32 bits (unsigned integer) + * + * This is an opaque integer assigned by the sender to identify each + * request parameter. It is in host byte order and is only meaningful + * to the sender. The receiver of the ASCONF Chunk will copy this 32 + * bit value into the ASCONF Correlation ID field of the + * ASCONF-ACK. The sender of the ASCONF can use this same value in the + * ASCONF-ACK to find which request the response is for. + * + * ASCONF Parameter: TLV format + * + * Each Address configuration change is represented by a TLV parameter + * as defined in Section 3.2. One or more requests may be present in + * an ASCONF Chunk. + */ +typedef struct { + __u32 correlation; + sctp_paramhdr_t p; + __u8 payload[0]; +} sctpAsconfReq_t; + +/* ADDIP + * 3.1.1 Address/Stream Configuration Change Chunk (ASCONF) + * + * This chunk is used to communicate to the remote endpoint one of the + * configuration change requests that MUST be acknowledged. The + * information carried in the ASCONF Chunk uses the form of a + * Tag-Length-Value (TLV), as described in "3.2.1 + * Optional/Variable-length Parameter Format" in [RFC2960], for all + * variable parameters. + */ +typedef struct { + __u32 serial; + __u8 reserved[3]; + __u8 addr_type; + __u32 addr[4]; + sctpAsconfReq_t requests[0]; +} sctpAsconf_t; + +/* ADDIP + * 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK) + * + * ASCONF-Request Correlation ID: 32 bits (unsigned integer) + * + * This value is copied from the ASCONF Correlation ID received in the + * ASCONF Chunk. It is used by the receiver of the ASCONF-ACK to identify + * which ASCONF parameter this response is associated with. + * + * ASCONF Parameter Response : TLV format + * + * The ASCONF Parameter Response is used in the ASCONF-ACK to report + * status of ASCONF processing. By default, if a responding endpoint + * does not include any Error Cause, a success is indicated. Thus a + * sender of an ASCONF-ACK MAY indicate complete success of all TLVs in + * an ASCONF by returning only the Chunk Type, Chunk Flags, Chunk Length + * (set to 8) and the Serial Number. + */ +typedef union { + struct { + __u32 correlation; + sctp_paramhdr_t header; /* success report */ + } success; + struct { + __u32 correlation; + sctp_paramhdr_t header; /* error cause indication */ + sctp_paramhdr_t errcause; + uint8_t request[0]; /* original request from ASCONF */ + } error; +#define __correlation success.correlation +#define __header success.header +#define __cause error.errcause +#define __request error.request +} sctpAsconfAckRsp_t; + +/* ADDIP + * 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK) + * + * This chunk is used by the receiver of an ASCONF Chunk to + * acknowledge the reception. It carries zero or more results for any + * ASCONF Parameters that were processed by the receiver. + */ +typedef struct { + __u32 serial; + sctpAsconfAckRsp_t responses[0]; +} sctpAsconfAck_t; + +/********************************************************************* + * Internal structures + * + * These are data structures which never go out on the wire. + *********************************************************************/ + +/* What is this data structure for? The TLV isn't one--it is just a + * value. Perhaps this data structure ought to have a type--otherwise + * it is not unambigiously parseable. --piggy + */ +typedef struct { + struct list_head hook; + int length; /* length of the TLV */ + + /* the actually TLV to be copied into ASCONF_ACK */ + sctpAsconfAckRsp_t TLV; +} sctpAsconfAckRspNode_t; + +#endif /* __LINUX_SCTP_H__ */ diff -urN linux-2.4.22-bk23/include/linux/socket.h linux-2.4.22-bk24/include/linux/socket.h --- linux-2.4.22-bk23/include/linux/socket.h 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.22-bk24/include/linux/socket.h 2003-09-23 03:07:18.000000000 -0700 @@ -242,6 +242,7 @@ #define SOL_UDP 17 #define SOL_IPV6 41 #define SOL_ICMPV6 58 +#define SOL_SCTP 132 #define SOL_RAW 255 #define SOL_IPX 256 #define SOL_AX25 257 diff -urN linux-2.4.22-bk23/include/linux/sysctl.h linux-2.4.22-bk24/include/linux/sysctl.h --- linux-2.4.22-bk23/include/linux/sysctl.h 2003-09-23 03:06:57.000000000 -0700 +++ linux-2.4.22-bk24/include/linux/sysctl.h 2003-09-23 03:07:18.000000000 -0700 @@ -176,7 +176,8 @@ NET_TR=14, NET_DECNET=15, NET_ECONET=16, - NET_KHTTPD=17 + NET_KHTTPD=17, + NET_SCTP=18 }; /* /proc/sys/kernel/random */ @@ -509,6 +510,21 @@ NET_DECNET_DEBUG_LEVEL = 255 }; +/* /proc/sys/net/sctp */ +enum { + NET_SCTP_RTO_INITIAL = 1, + NET_SCTP_RTO_MIN = 2, + NET_SCTP_RTO_MAX = 3, + NET_SCTP_RTO_ALPHA = 4, + NET_SCTP_RTO_BETA = 5, + NET_SCTP_VALID_COOKIE_LIFE = 6, + NET_SCTP_ASSOCIATION_MAX_RETRANS = 7, + NET_SCTP_PATH_MAX_RETRANS = 8, + NET_SCTP_MAX_INIT_RETRANSMITS = 9, + NET_SCTP_HB_INTERVAL = 10, + NET_SCTP_PRESERVE_ENABLE = 11, + NET_SCTP_MAX_BURST = 12, +}; /* /proc/sys/net/khttpd/ */ enum { NET_KHTTPD_DOCROOT = 1, diff -urN linux-2.4.22-bk23/include/net/if_inet6.h linux-2.4.22-bk24/include/net/if_inet6.h --- linux-2.4.22-bk23/include/net/if_inet6.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/net/if_inet6.h 2003-09-23 03:07:18.000000000 -0700 @@ -15,6 +15,10 @@ #ifndef _NET_IF_INET6_H #define _NET_IF_INET6_H +#include + +#define IF_RA_OTHERCONF 0x80 +#define IF_RA_MANAGED 0x40 #define IF_RA_RCVD 0x20 #define IF_RS_SENT 0x10 @@ -124,22 +128,6 @@ #define IFA_SITE IPV6_ADDR_SITELOCAL #define IFA_GLOBAL 0x0000U -struct ipv6_devconf -{ - int forwarding; - int hop_limit; - int mtu6; - int accept_ra; - int accept_redirects; - int autoconf; - int dad_transmits; - int rtr_solicits; - int rtr_solicit_interval; - int rtr_solicit_delay; - - void *sysctl; -}; - struct inet6_dev { struct net_device *dev; diff -urN linux-2.4.22-bk23/include/net/inet_common.h linux-2.4.22-bk24/include/net/inet_common.h --- linux-2.4.22-bk23/include/net/inet_common.h 1999-08-23 10:01:02.000000000 -0700 +++ linux-2.4.22-bk24/include/net/inet_common.h 2003-09-23 03:07:18.000000000 -0700 @@ -43,6 +43,14 @@ extern void inet_sock_destruct(struct sock *sk); extern atomic_t inet_sock_nr; +extern int inet_bind(struct socket *sock, + struct sockaddr *uaddr, int addr_len); +extern int inet_getname(struct socket *sock, + struct sockaddr *uaddr, + int *uaddr_len, int peer); +extern int inet_ioctl(struct socket *sock, + unsigned int cmd, unsigned long arg); + #endif diff -urN linux-2.4.22-bk23/include/net/ip.h linux-2.4.22-bk24/include/net/ip.h --- linux-2.4.22-bk23/include/net/ip.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/net/ip.h 2003-09-23 03:07:18.000000000 -0700 @@ -96,7 +96,7 @@ extern int ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*)); extern int ip_do_nat(struct sk_buff *skb); extern void ip_send_check(struct iphdr *ip); -extern int ip_queue_xmit(struct sk_buff *skb); +extern int ip_queue_xmit(struct sk_buff *skb, int ipfragok); extern void ip_init(void); extern int ip_build_xmit(struct sock *sk, int getfrag (const void *, diff -urN linux-2.4.22-bk23/include/net/ipv6.h linux-2.4.22-bk24/include/net/ipv6.h --- linux-2.4.22-bk23/include/net/ipv6.h 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.22-bk24/include/net/ipv6.h 2003-09-23 03:07:18.000000000 -0700 @@ -353,6 +353,14 @@ u32 info, u8 *payload); extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info); +extern int inet6_release(struct socket *sock); +extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len); +extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer); +extern int inet6_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg); + #endif /* __KERNEL__ */ #endif /* _NET_IPV6_H */ diff -urN linux-2.4.22-bk23/include/net/sctp/command.h linux-2.4.22-bk24/include/net/sctp/command.h --- linux-2.4.22-bk23/include/net/sctp/command.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/command.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,214 @@ +/* SCTP kernel reference Implementation Copyright (C) 1999-2001 + * Cisco, Motorola, and IBM + * + * This file is part of the SCTP kernel reference Implementation + * + * These are the definitions needed for the command object. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * the SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to one of the + * following email addresses: + * + * La Monte H.P. Yarroll + * Karl Knutson + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + + +#ifndef __net_sctp_command_h__ +#define __net_sctp_command_h__ + +#include +#include + + +typedef enum { + SCTP_CMD_NOP = 0, /* Do nothing. */ + SCTP_CMD_NEW_ASOC, /* Register a new association. */ + SCTP_CMD_DELETE_TCB, /* Delete the current association. */ + SCTP_CMD_NEW_STATE, /* Enter a new state. */ + SCTP_CMD_REPORT_TSN, /* Record the arrival of a TSN. */ + SCTP_CMD_GEN_SACK, /* Send a Selective ACK (maybe). */ + SCTP_CMD_PROCESS_SACK, /* Process an inbound SACK. */ + SCTP_CMD_GEN_INIT_ACK, /* Generate an INIT ACK chunk. */ + SCTP_CMD_PEER_INIT, /* Process a INIT from the peer. */ + SCTP_CMD_GEN_COOKIE_ECHO, /* Generate a COOKIE ECHO chunk. */ + SCTP_CMD_CHUNK_ULP, /* Send a chunk to the sockets layer. */ + SCTP_CMD_EVENT_ULP, /* Send a notification to the sockets layer. */ + SCTP_CMD_REPLY, /* Send a chunk to our peer. */ + SCTP_CMD_SEND_PKT, /* Send a full packet to our peer. */ + SCTP_CMD_RETRAN, /* Mark a transport for retransmission. */ + SCTP_CMD_ECN_CE, /* Do delayed CE processing. */ + SCTP_CMD_ECN_ECNE, /* Do delayed ECNE processing. */ + SCTP_CMD_ECN_CWR, /* Do delayed CWR processing. */ + SCTP_CMD_TIMER_START, /* Start a timer. */ + SCTP_CMD_TIMER_RESTART, /* Restart a timer. */ + SCTP_CMD_TIMER_STOP, /* Stop a timer. */ + SCTP_CMD_COUNTER_RESET, /* Reset a counter. */ + SCTP_CMD_COUNTER_INC, /* Increment a counter. */ + SCTP_CMD_INIT_RESTART, /* High level, do init timer work. */ + SCTP_CMD_INIT_FAILED, /* High level, do init failure work. */ + SCTP_CMD_REPORT_DUP, /* Report a duplicate TSN. */ + SCTP_CMD_STRIKE, /* Mark a strike against a transport. */ + SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */ + SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */ + SCTP_CMD_HB_TIMER_UPDATE, /* Update a heartbeat timers. */ + SCTP_CMD_HB_TIMERS_STOP, /* Stop the heartbeat timers. */ + SCTP_CMD_TRANSPORT_RESET, /* Reset the status of a transport. */ + SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */ + SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */ + SCTP_CMD_REPORT_BAD_TAG, /* Verification tags didn't match. */ + SCTP_CMD_PROCESS_CTSN, /* Sideeffect from shutdown. */ + SCTP_CMD_ASSOC_FAILED, /* Handle association failure. */ + SCTP_CMD_DISCARD_PACKET, /* Discard the whole packet. */ + SCTP_CMD_GEN_SHUTDOWN, /* Generate a SHUTDOWN chunk. */ + SCTP_CMD_UPDATE_ASSOC, /* Update association information. */ + SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */ + SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */ + SCTP_CMD_RTO_PENDING, /* Set transport's rto_pending. */ + SCTP_CMD_PART_DELIVER, /* Partial data delivery considerations. */ + SCTP_CMD_RENEGE, /* Renege data on an association. */ + SCTP_CMD_LAST +} sctp_verb_t; + +#define SCTP_CMD_MAX (SCTP_CMD_LAST - 1) +#define SCTP_CMD_NUM_VERBS (SCTP_CMD_MAX + 1) + +/* How many commands can you put in an sctp_cmd_seq_t? + * This is a rather arbitrary number, ideally derived from a careful + * analysis of the state functions, but in reality just taken from + * thin air in the hopes othat we don't trigger a kernel panic. + */ +#define SCTP_MAX_NUM_COMMANDS 14 + +typedef union { + __s32 i32; + __u32 u32; + __u16 u16; + __u8 u8; + int error; + sctp_state_t state; + sctp_event_timeout_t to; + sctp_counter_t counter; + void *ptr; + struct sctp_chunk *chunk; + struct sctp_association *asoc; + struct sctp_transport *transport; + struct sctp_bind_addr *bp; + sctp_init_chunk_t *init; + struct sctp_ulpevent *ulpevent; + struct sctp_packet *packet; + sctp_sackhdr_t *sackh; +} sctp_arg_t; + +/* We are simulating ML type constructors here. + * + * SCTP_ARG_CONSTRUCTOR(NAME, TYPE, ELT) builds a function called + * SCTP_NAME() which takes an argument of type TYPE and returns an + * sctp_arg_t. It does this by inserting the sole argument into the + * ELT union element of a local sctp_arg_t. + * + * E.g., SCTP_ARG_CONSTRUCTOR(I32, __s32, i32) builds SCTP_I32(arg), + * which takes an __s32 and returns a sctp_arg_t containing the + * __s32. So, after foo = SCTP_I32(arg), foo.i32 == arg. + */ +static inline sctp_arg_t SCTP_NULL(void) +{ + sctp_arg_t retval; retval.ptr = NULL; return retval; +} +static inline sctp_arg_t SCTP_NOFORCE(void) +{ + sctp_arg_t retval; retval.i32 = 0; return retval; +} +static inline sctp_arg_t SCTP_FORCE(void) +{ + sctp_arg_t retval; retval.i32 = 1; return retval; +} + +#define SCTP_ARG_CONSTRUCTOR(name, type, elt) \ +static inline sctp_arg_t \ +SCTP_## name (type arg) \ +{ sctp_arg_t retval; retval.elt = arg; return retval; } + +SCTP_ARG_CONSTRUCTOR(I32, __s32, i32) +SCTP_ARG_CONSTRUCTOR(U32, __u32, u32) +SCTP_ARG_CONSTRUCTOR(U16, __u16, u16) +SCTP_ARG_CONSTRUCTOR(U8, __u8, u8) +SCTP_ARG_CONSTRUCTOR(ERROR, int, error) +SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state) +SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter) +SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) +SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr) +SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk) +SCTP_ARG_CONSTRUCTOR(ASOC, struct sctp_association *, asoc) +SCTP_ARG_CONSTRUCTOR(TRANSPORT, struct sctp_transport *, transport) +SCTP_ARG_CONSTRUCTOR(BA, struct sctp_bind_addr *, bp) +SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init) +SCTP_ARG_CONSTRUCTOR(ULPEVENT, struct sctp_ulpevent *, ulpevent) +SCTP_ARG_CONSTRUCTOR(PACKET, struct sctp_packet *, packet) +SCTP_ARG_CONSTRUCTOR(SACKH, sctp_sackhdr_t *, sackh) + +typedef struct { + sctp_arg_t obj; + sctp_verb_t verb; +} sctp_cmd_t; + +typedef struct { + sctp_cmd_t cmds[SCTP_MAX_NUM_COMMANDS]; + __u8 next_free_slot; + __u8 next_cmd; +} sctp_cmd_seq_t; + + +/* Create a new sctp_command_sequence. + * Return NULL if creating a new sequence fails. + */ +sctp_cmd_seq_t *sctp_new_cmd_seq(int gfp); + +/* Initialize a block of memory as a command sequence. + * Return 0 if the initialization fails. + */ +int sctp_init_cmd_seq(sctp_cmd_seq_t *seq); + +/* Add a command to an sctp_cmd_seq_t. + * Return 0 if the command sequence is full. + * + * Use the SCTP_* constructors defined by SCTP_ARG_CONSTRUCTOR() above + * to wrap data which goes in the obj argument. + */ +int sctp_add_cmd(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj); + +/* Rewind an sctp_cmd_seq_t to iterate from the start. + * Return 0 if the rewind fails. + */ +int sctp_rewind_sequence(sctp_cmd_seq_t *seq); + +/* Return the next command structure in an sctp_cmd_seq. + * Return NULL at the end of the sequence. + */ +sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq); + +/* Dispose of a command sequence. */ +void sctp_free_cmd_seq(sctp_cmd_seq_t *seq); + +#endif /* __net_sctp_command_h__ */ + diff -urN linux-2.4.22-bk23/include/net/sctp/compat.h linux-2.4.22-bk24/include/net/sctp/compat.h --- linux-2.4.22-bk23/include/net/sctp/compat.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/compat.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,102 @@ +/* SCTP kernel reference Implementation + * + * Copyright (c) 2003 Hewlett-Packard Company + * + * This file is part of the SCTP kernel reference Implementation + * + * This header represents the structures and constants needed to backport + * lksctp from Linux kernel 2.5 to 2.4 This file also has some code that + * has been taken from the source base of Linux kernel 2.5 + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef __net_sctp_compat_h__ +#define __net_sctp_compat_h__ + +#include +#include +#include +#include + +/* + * The following defines are for compatibility with 2.5 + */ +/* + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define DEFINE_SNMP_STAT(type, name) \ + type name[NR_CPUS * 2] +#define DECLARE_SNMP_STAT(type, name) \ + extern type name[] +#define SNMP_DEC_STATS(mib, field) ((mib)[2*smp_processor_id()+!in_softirq()].field--) + +#define sctp_sk(__sk) (&(((struct sock *)__sk)->tp_pinfo.af_sctp)) +#define inet_sk(__sk) (&(((struct sock *)__sk)->protinfo.af_inet)) +#define inet6_sk(__sk) (&(((struct sock *)__sk)->net_pinfo.af_inet6)) + +#define virt_addr_valid(x) VALID_PAGE(virt_to_page((x))) +#define sock_owned_by_user(sk) ((sk)->lock.users) +#define sk_set_owner(x, y) +#define __unsafe(x) MOD_INC_USE_COUNT +#define dst_pmtu(x) ((x)->pmtu) + +void sctp_hash_digest(const char *key, const int in_key_len, + const char *text, const int text_len, + __u8 *digest); +/* + * find last bit set. + */ +static __inline__ int fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + +#endif /* __net_sctp_compat_h__ */ diff -urN linux-2.4.22-bk23/include/net/sctp/constants.h linux-2.4.22-bk24/include/net/sctp/constants.h --- linux-2.4.22-bk23/include/net/sctp/constants.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/constants.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,439 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001-2002 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Randall Stewart + * Ken Morneau + * Qiaobing Xie + * Xingang Guo + * Sridhar Samudrala + * Daisy Chang + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef __sctp_constants_h__ +#define __sctp_constants_h__ + +#include /* For TCP states used in sctp_sock_state_t */ +#include +#include /* For ipv6hdr. */ +#include + +/* Value used for stream negotiation. */ +enum { SCTP_MAX_STREAM = 0xffff }; +enum { SCTP_DEFAULT_OUTSTREAMS = 10 }; +enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; + +/* Define the amount of space to reserve for SCTP, IP, LL. + * There is a little bit of waste that we are always allocating + * for ipv6 headers, but this seems worth the simplicity. + */ + +#define SCTP_IP_OVERHEAD ((sizeof(struct sctphdr)\ + + sizeof(struct ipv6hdr)\ + + MAX_HEADER)) + +/* Since CIDs are sparse, we need all four of the following + * symbols. CIDs are dense through SCTP_CID_BASE_MAX. + */ +#define SCTP_CID_BASE_MAX SCTP_CID_SHUTDOWN_COMPLETE +#define SCTP_CID_MAX SCTP_CID_ASCONF_ACK + +#define SCTP_NUM_BASE_CHUNK_TYPES (SCTP_CID_BASE_MAX + 1) +#define SCTP_NUM_CHUNK_TYPES (SCTP_NUM_BASE_CHUNKTYPES + 2) + + +/* These are the different flavours of event. */ +typedef enum { + + SCTP_EVENT_T_CHUNK = 1, + SCTP_EVENT_T_TIMEOUT, + SCTP_EVENT_T_OTHER, + SCTP_EVENT_T_PRIMITIVE + +} sctp_event_t; + +#define SCTP_EVENT_T_MAX SCTP_EVENT_T_PRIMITIVE +#define SCTP_EVENT_T_NUM (SCTP_EVENT_T_MAX + 1) + +/* As a convenience for the state machine, we append SCTP_EVENT_* and + * SCTP_ULP_* to the list of possible chunks. + */ + +typedef enum { + SCTP_EVENT_TIMEOUT_NONE = 0, + SCTP_EVENT_TIMEOUT_T1_COOKIE, + SCTP_EVENT_TIMEOUT_T1_INIT, + SCTP_EVENT_TIMEOUT_T2_SHUTDOWN, + SCTP_EVENT_TIMEOUT_T3_RTX, + SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD, + SCTP_EVENT_TIMEOUT_HEARTBEAT, + SCTP_EVENT_TIMEOUT_SACK, + SCTP_EVENT_TIMEOUT_AUTOCLOSE, +} sctp_event_timeout_t; + +#define SCTP_EVENT_TIMEOUT_MAX SCTP_EVENT_TIMEOUT_AUTOCLOSE +#define SCTP_NUM_TIMEOUT_TYPES (SCTP_EVENT_TIMEOUT_MAX + 1) + +typedef enum { + SCTP_EVENT_NO_PENDING_TSN = 0, +} sctp_event_other_t; + +#define SCTP_EVENT_OTHER_MAX SCTP_EVENT_NO_PENDING_TSN +#define SCTP_NUM_OTHER_TYPES (SCTP_EVENT_OTHER_MAX + 1) + +/* These are primitive requests from the ULP. */ +typedef enum { + SCTP_PRIMITIVE_ASSOCIATE = 0, + SCTP_PRIMITIVE_SHUTDOWN, + SCTP_PRIMITIVE_ABORT, + SCTP_PRIMITIVE_SEND, + SCTP_PRIMITIVE_REQUESTHEARTBEAT, +} sctp_event_primitive_t; + +#define SCTP_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_REQUESTHEARTBEAT +#define SCTP_NUM_PRIMITIVE_TYPES (SCTP_EVENT_PRIMITIVE_MAX + 1) + +/* We define here a utility type for manipulating subtypes. + * The subtype constructors all work like this: + * + * sctp_subtype_t foo = SCTP_ST_CHUNK(SCTP_CID_INIT); + */ + +typedef union { + sctp_cid_t chunk; + sctp_event_timeout_t timeout; + sctp_event_other_t other; + sctp_event_primitive_t primitive; +} sctp_subtype_t; + +#define SCTP_SUBTYPE_CONSTRUCTOR(_name, _type, _elt) \ +static inline sctp_subtype_t \ +SCTP_ST_## _name (_type _arg) \ +{ sctp_subtype_t _retval; _retval._elt = _arg; return _retval; } + +SCTP_SUBTYPE_CONSTRUCTOR(CHUNK, sctp_cid_t, chunk) +SCTP_SUBTYPE_CONSTRUCTOR(TIMEOUT, sctp_event_timeout_t, timeout) +SCTP_SUBTYPE_CONSTRUCTOR(OTHER, sctp_event_other_t, other) +SCTP_SUBTYPE_CONSTRUCTOR(PRIMITIVE, sctp_event_primitive_t, primitive) + + +#define sctp_chunk_is_control(a) (a->chunk_hdr->type != SCTP_CID_DATA) +#define sctp_chunk_is_data(a) (a->chunk_hdr->type == SCTP_CID_DATA) + +/* Calculate the actual data size in a data chunk */ +#define SCTP_DATA_SNDSIZE(c) ((int)((unsigned long)(c->chunk_end)\ + - (unsigned long)(c->chunk_hdr)\ + - sizeof(sctp_data_chunk_t))) + +/* This is a table of printable names of sctp_param_t's. */ +extern const char *sctp_param_tbl[]; + + +#define SCTP_MAX_ERROR_CAUSE SCTP_ERROR_NONEXIST_IP +#define SCTP_NUM_ERROR_CAUSE 10 + +/* Internal error codes */ +typedef enum { + + SCTP_IERROR_NO_ERROR = 0, + SCTP_IERROR_BASE = 1000, + SCTP_IERROR_NO_COOKIE, + SCTP_IERROR_BAD_SIG, + SCTP_IERROR_STALE_COOKIE, + SCTP_IERROR_NOMEM, + SCTP_IERROR_MALFORMED, + SCTP_IERROR_BAD_TAG, + SCTP_IERROR_BIG_GAP, + SCTP_IERROR_DUP_TSN, + +} sctp_ierror_t; + + + +/* SCTP state defines for internal state machine */ +typedef enum { + + SCTP_STATE_EMPTY = 0, + SCTP_STATE_CLOSED = 1, + SCTP_STATE_COOKIE_WAIT = 2, + SCTP_STATE_COOKIE_ECHOED = 3, + SCTP_STATE_ESTABLISHED = 4, + SCTP_STATE_SHUTDOWN_PENDING = 5, + SCTP_STATE_SHUTDOWN_SENT = 6, + SCTP_STATE_SHUTDOWN_RECEIVED = 7, + SCTP_STATE_SHUTDOWN_ACK_SENT = 8, + +} sctp_state_t; + +#define SCTP_STATE_MAX SCTP_STATE_SHUTDOWN_ACK_SENT +#define SCTP_STATE_NUM_STATES (SCTP_STATE_MAX + 1) + +/* These are values for sk->state. + * For a UDP-style SCTP socket, the states are defined as follows + * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to + * accept new associations, but it can initiate the creation of new ones. + * - A socket in SCTP_SS_LISTENING state indicates that it is willing to + * accept new associations and can initiate the creation of new ones. + * - A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off + * socket with one association. + * For a TCP-style SCTP socket, the states are defined as follows + * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to + * accept new associations, but it can initiate the creation of new ones. + * - A socket in SCTP_SS_LISTENING state indicates that it is willing to + * accept new associations, but cannot initiate the creation of new ones. + * - A socket in SCTP_SS_ESTABLISHED state indicates that it has a single + * association. + */ +typedef enum { + SCTP_SS_CLOSED = TCP_CLOSE, + SCTP_SS_LISTENING = TCP_LISTEN, + SCTP_SS_ESTABLISHING = TCP_SYN_SENT, + SCTP_SS_ESTABLISHED = TCP_ESTABLISHED, + SCTP_SS_DISCONNECTING = TCP_CLOSING, +} sctp_sock_state_t; + +/* These functions map various type to printable names. */ +const char *sctp_cname(const sctp_subtype_t); /* chunk types */ +const char *sctp_oname(const sctp_subtype_t); /* other events */ +const char *sctp_tname(const sctp_subtype_t); /* timeouts */ +const char *sctp_pname(const sctp_subtype_t); /* primitives */ + +/* This is a table of printable names of sctp_state_t's. */ +extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[]; + +/* SCTP reachability state for each address */ +#define SCTP_ADDR_NOHB 4 +#define SCTP_ADDR_REACHABLE 2 +#define SCTP_ADDR_NOT_REACHABLE 1 + +/* Maximum chunk length considering padding requirements. */ +enum { SCTP_MAX_CHUNK_LEN = ((1<<16) - sizeof(__u32)) }; + +/* Encourage Cookie-Echo bundling by pre-fragmenting chunks a little + * harder (until reaching ESTABLISHED state). + */ +enum { SCTP_ARBITRARY_COOKIE_ECHO_LEN = 200 }; + +/* Guess at how big to make the TSN mapping array. + * We guarantee that we can handle at least this big a gap between the + * cumulative ACK and the highest TSN. In practice, we can often + * handle up to twice this value. + * + * NEVER make this more than 32767 (2^15-1). The Gap Ack Blocks in a + * SACK (see section 3.3.4) are only 16 bits, so 2*SCTP_TSN_MAP_SIZE + * must be less than 65535 (2^16 - 1), or we will have overflow + * problems creating SACK's. + */ +#define SCTP_TSN_MAP_SIZE 2048 +#define SCTP_TSN_MAX_GAP 65535 + +/* We will not record more than this many duplicate TSNs between two + * SACKs. The minimum PMTU is 576. Remove all the headers and there + * is enough room for 131 duplicate reports. Round down to the + * nearest power of 2. + */ +enum { SCTP_MIN_PMTU = 576 }; +enum { SCTP_MAX_DUP_TSNS = 128 }; + +typedef enum { + SCTP_COUNTER_INIT_ERROR, +} sctp_counter_t; + +/* How many counters does an association need? */ +#define SCTP_NUMBER_COUNTERS 5 + +/* Here we define the default timers. */ + +/* cookie timer def = ? seconds */ +#define SCTP_DEFAULT_TIMEOUT_T1_COOKIE (3 * HZ) + +/* init timer def = 3 seconds */ +#define SCTP_DEFAULT_TIMEOUT_T1_INIT (3 * HZ) + +/* shutdown timer def = 300 ms */ +#define SCTP_DEFAULT_TIMEOUT_T2_SHUTDOWN ((300 * HZ) / 1000) + +/* 0 seconds + RTO */ +#define SCTP_DEFAULT_TIMEOUT_HEARTBEAT (10 * HZ) + +/* recv timer def = 200ms (in usec) */ +#define SCTP_DEFAULT_TIMEOUT_SACK ((200 * HZ) / 1000) +#define SCTP_DEFAULT_TIMEOUT_SACK_MAX ((500 * HZ) / 1000) /* 500 ms */ + +/* RTO.Initial - 3 seconds + * RTO.Min - 1 second + * RTO.Max - 60 seconds + * RTO.Alpha - 1/8 + * RTO.Beta - 1/4 + */ +#define SCTP_RTO_INITIAL (3 * HZ) +#define SCTP_RTO_MIN (1 * HZ) +#define SCTP_RTO_MAX (60 * HZ) + +#define SCTP_RTO_ALPHA 3 /* 1/8 when converted to right shifts. */ +#define SCTP_RTO_BETA 2 /* 1/4 when converted to right shifts. */ + +/* Maximum number of new data packets that can be sent in a burst. */ +#define SCTP_MAX_BURST 4 + +#define SCTP_CLOCK_GRANULARITY 1 /* 1 jiffy */ + +#define SCTP_DEF_MAX_INIT 6 +#define SCTP_DEF_MAX_SEND 10 + +#define SCTP_DEFAULT_COOKIE_LIFE_SEC 60 /* seconds */ +#define SCTP_DEFAULT_COOKIE_LIFE_USEC 0 /* microseconds */ + +#define SCTP_DEFAULT_MINWINDOW 1500 /* default minimum rwnd size */ +#define SCTP_DEFAULT_MAXWINDOW 32768 /* default rwnd size */ +#define SCTP_DEFAULT_MAXSEGMENT 1500 /* MTU size, this is the limit + * to which we will raise the P-MTU. + */ +#define SCTP_DEFAULT_MINSEGMENT 512 /* MTU size ... if no mtu disc */ +#define SCTP_HOW_MANY_SECRETS 2 /* How many secrets I keep */ +#define SCTP_HOW_LONG_COOKIE_LIVE 3600 /* How many seconds the current + * secret will live? + */ +#define SCTP_SECRET_SIZE 32 /* Number of octets in a 256 bits. */ + +#define SCTP_SIGNATURE_SIZE 20 /* size of a SLA-1 signature */ + +#define SCTP_COOKIE_MULTIPLE 32 /* Pad out our cookie to make our hash + * functions simpler to write. + */ + +#if defined (CONFIG_SCTP_HMAC_MD5) +#define SCTP_COOKIE_HMAC_ALG "md5" +#elif defined (CONFIG_SCTP_HMAC_SHA1) +#define SCTP_COOKIE_HMAC_ALG "sha1" +#else +#define SCTP_COOKIE_HMAC_ALG NULL +#endif + +/* These return values describe the success or failure of a number of + * routines which form the lower interface to SCTP_outqueue. + */ +typedef enum { + SCTP_XMIT_OK, + SCTP_XMIT_PMTU_FULL, + SCTP_XMIT_RWND_FULL, + SCTP_XMIT_MUST_FRAG, + SCTP_XMIT_NAGLE_DELAY, +} sctp_xmit_t; + +/* These are the commands for manipulating transports. */ +typedef enum { + SCTP_TRANSPORT_UP, + SCTP_TRANSPORT_DOWN, +} sctp_transport_cmd_t; + +/* These are the address scopes defined mainly for IPv4 addresses + * based on draft of SCTP IPv4 scoping . + * These scopes are hopefully generic enough to be used on scoping both + * IPv4 and IPv6 addresses in SCTP. + * At this point, the IPv6 scopes will be mapped to these internal scopes + * as much as possible. + */ +typedef enum { + SCTP_SCOPE_GLOBAL, /* IPv4 global addresses */ + SCTP_SCOPE_PRIVATE, /* IPv4 private addresses */ + SCTP_SCOPE_LINK, /* IPv4 link local address */ + SCTP_SCOPE_LOOPBACK, /* IPv4 loopback address */ + SCTP_SCOPE_UNUSABLE, /* IPv4 unusable addresses */ +} sctp_scope_t; + +/* Based on IPv4 scoping , + * SCTP IPv4 unusable addresses: 0.0.0.0/8, 224.0.0.0/4, 198.18.0.0/24, + * 192.88.99.0/24. + * Also, RFC 8.4, non-unicast addresses are not considered valid SCTP + * addresses. + */ +#define IS_IPV4_UNUSABLE_ADDRESS(a) \ + ((INADDR_BROADCAST == *a) || \ + (MULTICAST(*a)) || \ + (((unsigned char *)(a))[0] == 0) || \ + ((((unsigned char *)(a))[0] == 198) && \ + (((unsigned char *)(a))[1] == 18) && \ + (((unsigned char *)(a))[2] == 0)) || \ + ((((unsigned char *)(a))[0] == 192) && \ + (((unsigned char *)(a))[1] == 88) && \ + (((unsigned char *)(a))[2] == 99))) + +/* IPv4 Link-local addresses: 169.254.0.0/16. */ +#define IS_IPV4_LINK_ADDRESS(a) \ + ((((unsigned char *)(a))[0] == 169) && \ + (((unsigned char *)(a))[1] == 254)) + +/* RFC 1918 "Address Allocation for Private Internets" defines the IPv4 + * private address space as the following: + * + * 10.0.0.0 - 10.255.255.255 (10/8 prefix) + * 172.16.0.0.0 - 172.31.255.255 (172.16/12 prefix) + * 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) + */ +#define IS_IPV4_PRIVATE_ADDRESS(a) \ + ((((unsigned char *)(a))[0] == 10) || \ + ((((unsigned char *)(a))[0] == 172) && \ + (((unsigned char *)(a))[1] >= 16) && \ + (((unsigned char *)(a))[1] < 32)) || \ + ((((unsigned char *)(a))[0] == 192) && \ + (((unsigned char *)(a))[1] == 168))) + +/* Flags used for the bind address copy functions. */ +#define SCTP_ADDR6_ALLOWED 0x00000001 /* IPv6 address is allowed by + local sock family */ +#define SCTP_ADDR4_PEERSUPP 0x00000002 /* IPv4 address is supported by + peer */ +#define SCTP_ADDR6_PEERSUPP 0x00000004 /* IPv6 address is supported by + peer */ + +/* Reasons to retransmit. */ +typedef enum { + SCTP_RTXR_T3_RTX, + SCTP_RTXR_FAST_RTX, + SCTP_RTXR_PMTUD, +} sctp_retransmit_reason_t; + +/* Reasons to lower cwnd. */ +typedef enum { + SCTP_LOWER_CWND_T3_RTX, + SCTP_LOWER_CWND_FAST_RTX, + SCTP_LOWER_CWND_ECNE, + SCTP_LOWER_CWND_INACTIVE, +} sctp_lower_cwnd_t; + +#endif /* __sctp_constants_h__ */ diff -urN linux-2.4.22-bk23/include/net/sctp/sctp.h linux-2.4.22-bk24/include/net/sctp/sctp.h --- linux-2.4.22-bk23/include/net/sctp/sctp.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/sctp.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,556 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * Copyright (c) 2001-2003 Intel Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * The base lksctp header. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Xingang Guo + * Jon Grimm + * Daisy Chang + * Sridhar Samudrala + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef __net_sctp_h__ +#define __net_sctp_h__ + +/* Header Strategy. + * Start getting some control over the header file depencies: + * includes + * constants + * structs + * prototypes + * macros, externs, and inlines + * + * Move test_frame specific items out of the kernel headers + * and into the test frame headers. This is not perfect in any sense + * and will continue to evolve. + */ + + +#include + +#ifdef TEST_FRAME +#undef CONFIG_PROC_FS +#undef CONFIG_SCTP_DBG_OBJCNT +#undef CONFIG_SYSCTL +#endif /* TEST_FRAME */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Set SCTP_DEBUG flag via config if not already set. */ +#ifndef SCTP_DEBUG +#ifdef CONFIG_SCTP_DBG_MSG +#define SCTP_DEBUG 1 +#else +#define SCTP_DEBUG 0 +#endif /* CONFIG_SCTP_DBG */ +#endif /* SCTP_DEBUG */ + +#ifdef CONFIG_IP_SCTP_MODULE +#define SCTP_PROTOSW_FLAG 0 +#else /* static! */ +#define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT +#endif + + +/* Certain internal static functions need to be exported when + * compiled into the test frame. + */ +#ifndef SCTP_STATIC +#define SCTP_STATIC static +#endif + +/* + * Function declarations. + */ + +/* + * sctp_protocol.c + */ +extern struct sctp_protocol sctp_proto; +extern struct sock *sctp_get_ctl_sock(void); +extern int sctp_copy_local_addr_list(struct sctp_protocol *, + struct sctp_bind_addr *, + sctp_scope_t, int gfp, int flags); +extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); +extern int sctp_register_pf(struct sctp_pf *, sa_family_t); + +/* + * sctp/socket.c + */ +extern int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb); +extern int sctp_inet_listen(struct socket *sock, int backlog); +extern void sctp_write_space(struct sock *sk); +extern unsigned int sctp_poll(struct file *file, struct socket *sock, + poll_table *wait); + +/* + * sctp/primitive.c + */ +extern int sctp_primitive_ASSOCIATE(struct sctp_association *, void *arg); +extern int sctp_primitive_SHUTDOWN(struct sctp_association *, void *arg); +extern int sctp_primitive_ABORT(struct sctp_association *, void *arg); +extern int sctp_primitive_SEND(struct sctp_association *, void *arg); +extern int sctp_primitive_REQUESTHEARTBEAT(struct sctp_association *, void *arg); + +/* + * sctp/crc32c.c + */ +extern __u32 sctp_start_cksum(__u8 *ptr, __u16 count); +extern __u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 cksum); +extern __u32 sctp_end_cksum(__u32 cksum); +extern __u32 sctp_update_copy_cksum(__u8 *, __u8 *, __u16 count, __u32 cksum); + +/* + * sctp/input.c + */ +extern int sctp_rcv(struct sk_buff *skb); +extern void sctp_v4_err(struct sk_buff *skb, u32 info); +extern void sctp_hash_established(struct sctp_association *); +extern void __sctp_hash_established(struct sctp_association *); +extern void sctp_unhash_established(struct sctp_association *); +extern void __sctp_unhash_established(struct sctp_association *); +extern void sctp_hash_endpoint(struct sctp_endpoint *); +extern void __sctp_hash_endpoint(struct sctp_endpoint *); +extern void sctp_unhash_endpoint(struct sctp_endpoint *); +extern void __sctp_unhash_endpoint(struct sctp_endpoint *); +extern struct sctp_association *__sctp_lookup_association( + const union sctp_addr *, + const union sctp_addr *, + struct sctp_transport **); +extern struct sock *sctp_err_lookup(int family, struct sk_buff *, + struct sctphdr *, struct sctp_endpoint **, + struct sctp_association **, + struct sctp_transport **); +extern void sctp_err_finish(struct sock *, struct sctp_endpoint *, + struct sctp_association *); +extern void sctp_icmp_frag_needed(struct sock *, struct sctp_association *, + struct sctp_transport *t, __u32 pmtu); + +/* + * Section: Macros, externs, and inlines + */ + + +#ifdef TEST_FRAME +#include +#else + +/* spin lock wrappers. */ +#define sctp_spin_lock_irqsave(lock, flags) spin_lock_irqsave(lock, flags) +#define sctp_spin_unlock_irqrestore(lock, flags) \ + spin_unlock_irqrestore(lock, flags) +#define sctp_local_bh_disable() local_bh_disable() +#define sctp_local_bh_enable() local_bh_enable() +#define sctp_spin_lock(lock) spin_lock(lock) +#define sctp_spin_unlock(lock) spin_unlock(lock) +#define sctp_write_lock(lock) write_lock(lock) +#define sctp_write_unlock(lock) write_unlock(lock) +#define sctp_read_lock(lock) read_lock(lock) +#define sctp_read_unlock(lock) read_unlock(lock) + +/* sock lock wrappers. */ +#define sctp_lock_sock(sk) lock_sock(sk) +#define sctp_release_sock(sk) release_sock(sk) +#define sctp_bh_lock_sock(sk) bh_lock_sock(sk) +#define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk) +#define SCTP_SOCK_SLEEP_PRE(sk) SOCK_SLEEP_PRE(sk) +#define SCTP_SOCK_SLEEP_POST(sk) SOCK_SLEEP_POST(sk) + +/* SCTP SNMP MIB stats handlers */ +DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics); +#define SCTP_INC_STATS(field) SNMP_INC_STATS(sctp_statistics, field) +#define SCTP_INC_STATS_BH(field) SNMP_INC_STATS_BH(sctp_statistics, field) +#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field) +#define SCTP_DEC_STATS(field) SNMP_DEC_STATS(sctp_statistics, field) + +/* Determine if this is a valid kernel address. */ +static inline int sctp_is_valid_kaddr(unsigned long addr) +{ + struct page *page; + + /* Make sure the address is not in the user address space. */ + if (addr < PAGE_OFFSET) + return 0; + + page = virt_to_page((void*)addr); + + /* Is this page valid? */ + if (!virt_addr_valid((void*)addr) || PageReserved(page)) + return 0; + + return 1; +} + +#endif /* !TEST_FRAME */ + + +/* Print debugging messages. */ +#if SCTP_DEBUG +extern int sctp_debug_flag; +#define SCTP_DEBUG_PRINTK(whatever...) \ + ((void) (sctp_debug_flag && printk(KERN_DEBUG whatever))) +#define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; } +#define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; } + +#define SCTP_ASSERT(expr, str, func) \ + if (!(expr)) { \ + SCTP_DEBUG_PRINTK("Assertion Failed: %s(%s) at %s:%s:%d\n", \ + str, (#expr), __FILE__, __FUNCTION__, __LINE__); \ + func; \ + } + +#else /* SCTP_DEBUG */ + +#define SCTP_DEBUG_PRINTK(whatever...) +#define SCTP_ENABLE_DEBUG +#define SCTP_DISABLE_DEBUG +#define SCTP_ASSERT(expr, str, func) + +#endif /* SCTP_DEBUG */ + + +/* + * Macros for keeping a global reference of object allocations. + */ +#ifdef CONFIG_SCTP_DBG_OBJCNT + +extern atomic_t sctp_dbg_objcnt_sock; +extern atomic_t sctp_dbg_objcnt_ep; +extern atomic_t sctp_dbg_objcnt_assoc; +extern atomic_t sctp_dbg_objcnt_transport; +extern atomic_t sctp_dbg_objcnt_chunk; +extern atomic_t sctp_dbg_objcnt_bind_addr; +extern atomic_t sctp_dbg_objcnt_addr; +extern atomic_t sctp_dbg_objcnt_ssnmap; + +/* Macros to atomically increment/decrement objcnt counters. */ +#define SCTP_DBG_OBJCNT_INC(name) \ +atomic_inc(&sctp_dbg_objcnt_## name) +#define SCTP_DBG_OBJCNT_DEC(name) \ +atomic_dec(&sctp_dbg_objcnt_## name) +#define SCTP_DBG_OBJCNT(name) \ +atomic_t sctp_dbg_objcnt_## name = ATOMIC_INIT(0) + +/* Macro to help create new entries in in the global array of + * objcnt counters. + */ +#define SCTP_DBG_OBJCNT_ENTRY(name) \ +{.label= #name, .counter= &sctp_dbg_objcnt_## name} + +extern void sctp_dbg_objcnt_init(void); +extern void sctp_dbg_objcnt_exit(void); + +#else + +#define SCTP_DBG_OBJCNT_INC(name) +#define SCTP_DBG_OBJCNT_DEC(name) + +static inline void sctp_dbg_objcnt_init(void) { return; } +static inline void sctp_dbg_objcnt_exit(void) { return; } + +#endif /* CONFIG_SCTP_DBG_OBJCOUNT */ + +#if defined CONFIG_SYSCTL +extern void sctp_sysctl_register(void); +extern void sctp_sysctl_unregister(void); +#else +static inline void sctp_sysctl_register(void) { return; } +static inline void sctp_sysctl_unregister(void) { return; } +#endif + +/* Size of Supported Address Parameter for 'x' address types. */ +#define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16)) + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + +extern int sctp_v6_init(void); +extern void sctp_v6_exit(void); +extern void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info); + +#else /* #ifdef defined(CONFIG_IPV6) */ + +static inline int sctp_v6_init(void) { return 0; } +static inline void sctp_v6_exit(void) { return; } + +#endif /* #if defined(CONFIG_IPV6) */ + +/* Some wrappers, in case crypto not available. */ +#if defined (CONFIG_CRYPTO_HMAC) +#define sctp_crypto_alloc_tfm crypto_alloc_tfm +#define sctp_crypto_free_tfm crypto_free_tfm +#define sctp_crypto_hmac crypto_hmac +#else +#define sctp_crypto_alloc_tfm(x...) NULL +#define sctp_crypto_free_tfm(x...) +#define sctp_crypto_hmac(x...) +#endif + + +/* Map an association to an assoc_id. */ +static inline sctp_assoc_t sctp_assoc2id(const struct sctp_association *asoc) +{ + return (sctp_assoc_t) asoc; +} + + +/* Look up the association by its id. */ +struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id); + + +/* A macro to walk a list of skbs. */ +#define sctp_skb_for_each(pos, head, tmp) \ +for (pos = (head)->next;\ + tmp = (pos)->next, pos != ((struct sk_buff *)(head));\ + pos = tmp) + + +/* A helper to append an entire skb list (list) to another (head). */ +static inline void sctp_skb_list_tail(struct sk_buff_head *list, + struct sk_buff_head *head) +{ + unsigned long flags; + + sctp_spin_lock_irqsave(&head->lock, flags); + sctp_spin_lock(&list->lock); + + list_splice((struct list_head *)list, (struct list_head *)head->prev); + + head->qlen += list->qlen; + list->qlen = 0; + + sctp_spin_unlock(&list->lock); + sctp_spin_unlock_irqrestore(&head->lock, flags); +} + +/** + * sctp_list_dequeue - remove from the head of the queue + * @list: list to dequeue from + * + * Remove the head of the list. The head item is + * returned or %NULL if the list is empty. + */ + +static inline struct list_head *sctp_list_dequeue(struct list_head *list) +{ + struct list_head *result = NULL; + + if (list->next != list) { + result = list->next; + list->next = result->next; + list->next->prev = list; + INIT_LIST_HEAD(result); + } + return result; +} + +/* Calculate the size (in bytes) occupied by the data of an iovec. */ +static inline size_t get_user_iov_size(struct iovec *iov, int iovlen) +{ + size_t retval = 0; + + for (; iovlen > 0; --iovlen) { + retval += iov->iov_len; + iov++; + } + + return retval; +} + +/* Generate a random jitter in the range of -50% ~ +50% of input RTO. */ +static inline __s32 sctp_jitter(__u32 rto) +{ + static __u32 sctp_rand; + __s32 ret; + + sctp_rand += jiffies; + sctp_rand ^= (sctp_rand << 12); + sctp_rand ^= (sctp_rand >> 20); + + /* Choose random number from 0 to rto, then move to -50% ~ +50% + * of rto. + */ + ret = sctp_rand % rto - (rto >> 1); + return ret; +} + +/* Break down data chunks at this point. */ +static inline int sctp_frag_point(int pmtu) +{ + pmtu -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk); + pmtu -= sizeof(struct sctp_sack_chunk); + + return pmtu; +} + +/* Walk through a list of TLV parameters. Don't trust the + * individual parameter lengths and instead depend on + * the chunk length to indicate when to stop. Make sure + * there is room for a param header too. + */ +#define sctp_walk_params(pos, chunk, member)\ +_sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member) + +#define _sctp_walk_params(pos, chunk, end, member)\ +for (pos.v = chunk->member;\ + pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\ + pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)); \ + pos.v += WORD_ROUND(ntohs(pos.p->length))) + +/* Round an int up to the next multiple of 4. */ +#define WORD_ROUND(s) (((s)+3)&~3) + +/* Make a new instance of type. */ +#define t_new(type, flags) (type *)kmalloc(sizeof(type), flags) + +/* Compare two timevals. */ +#define tv_lt(s, t) \ + (s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec)) + +/* Stolen from net/profile.h. Using it from there is more grief than + * it is worth. + */ +static inline void tv_add(const struct timeval *entered, struct timeval *leaved) +{ + time_t usecs = leaved->tv_usec + entered->tv_usec; + time_t secs = leaved->tv_sec + entered->tv_sec; + + if (usecs >= 1000000) { + usecs -= 1000000; + secs++; + } + leaved->tv_sec = secs; + leaved->tv_usec = usecs; +} + + +/* External references. */ + +extern struct proto sctp_prot; +extern struct proc_dir_entry *proc_net_sctp; +extern void sctp_put_port(struct sock *sk); + +/* Static inline functions. */ + +/* Return the SCTP protocol structure. */ +static inline struct sctp_protocol *sctp_get_protocol(void) +{ + return &sctp_proto; +} + +/* Convert from an IP version number to an Address Family symbol. */ +static inline int ipver2af(__u8 ipver) +{ + switch (ipver) { + case 4: + return AF_INET; + case 6: + return AF_INET6; + default: + return 0; + }; +} + +/* Perform some sanity checks. */ +static inline int sctp_sanity_check(void) +{ + SCTP_ASSERT(sizeof(struct sctp_ulpevent) <= + sizeof(((struct sk_buff *)0)->cb), + "SCTP: ulpevent does not fit in skb!\n", return 0); + + return 1; +} + +/* Warning: The following hash functions assume a power of two 'size'. */ +/* This is the hash function for the SCTP port hash table. */ +static inline int sctp_phashfn(__u16 lport) +{ + struct sctp_protocol *sctp_proto = sctp_get_protocol(); + return (lport & (sctp_proto->port_hashsize - 1)); +} + +/* This is the hash function for the endpoint hash table. */ +static inline int sctp_ep_hashfn(__u16 lport) +{ + struct sctp_protocol *sctp_proto = sctp_get_protocol(); + return (lport & (sctp_proto->ep_hashsize - 1)); +} + +/* This is the hash function for the association hash table. */ +static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport) +{ + struct sctp_protocol *sctp_proto = sctp_get_protocol(); + int h = (lport << 16) + rport; + h ^= h>>8; + return (h & (sctp_proto->assoc_hashsize - 1)); +} + +/* This is the hash function for the association hash table. This is + * not used yet, but could be used as a better hash function when + * we have a vtag. + */ +static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag) +{ + struct sctp_protocol *sctp_proto = sctp_get_protocol(); + int h = (lport << 16) + rport; + h ^= vtag; + return (h & (sctp_proto->assoc_hashsize-1)); +} + +#endif /* __net_sctp_h__ */ diff -urN linux-2.4.22-bk23/include/net/sctp/sla1.h linux-2.4.22-bk24/include/net/sctp/sla1.h --- linux-2.4.22-bk23/include/net/sctp/sla1.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/sla1.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,80 @@ +/* SCTP reference Implementation + * Copyright (C) 1999 Cisco, Inc. + * Copyright (C) 1999 Motorola, Inc. + * + * This file originates from Randy Stewart's SCTP reference Implementation. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Randy Stewart + * Ken Morneau + * Qiaobing Xie + */ + +#ifndef __SLA1_h__ +#define __SLA1_h__ + +struct SLA_1_Context { + unsigned int A; + unsigned int B; + unsigned int C; + unsigned int D; + unsigned int E; + unsigned int H0; + unsigned int H1; + unsigned int H2; + unsigned int H3; + unsigned int H4; + unsigned int words[80]; + unsigned int TEMP; + + /* block I am collecting to process */ + char SLAblock[64]; + + /* collected so far */ + int howManyInBlock; + unsigned int runningTotal; +}; + + +#define F1(B,C,D) (((B & C) | ((~B) & D))) /* 0 <= t <= 19 */ +#define F2(B,C,D) (B ^ C ^ D) /* 20 <= t <= 39 */ +#define F3(B,C,D) ((B & C) | (B & D) | (C & D)) /* 40 <= t <= 59 */ +#define F4(B,C,D) (B ^ C ^ D) /*600 <= t <= 79 */ +/* circular shift */ + +#define CSHIFT(A,B) ((B << A) | (B >> (32-A))) + +#define K1 0x5a827999 /* 0 <= t <= 19 */ +#define K2 0x6ed9eba1 /* 20 <= t <= 39 */ +#define K3 0x8f1bbcdc /* 40 <= t <= 59 */ +#define K4 0xca62c1d6 /* 60 <= t <= 79 */ + +#define H0INIT 0x67452301 +#define H1INIT 0xefcdab89 +#define H2INIT 0x98badcfe +#define H3INIT 0x10325476 +#define H4INIT 0xc3d2e1f0 + +extern void SLA1_Init(struct SLA_1_Context *); +extern void SLA1_Process(struct SLA_1_Context *, const unsigned char *, int); +extern void SLA1_Final(struct SLA_1_Context *, unsigned char *); + +#endif diff -urN linux-2.4.22-bk23/include/net/sctp/sm.h linux-2.4.22-bk24/include/net/sctp/sm.h --- linux-2.4.22-bk23/include/net/sctp/sm.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/sm.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,480 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001-2002 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * This file is part of the implementation of the add-IP extension, + * based on June 29, 2001, + * for the SCTP kernel reference Implementation. + * + * These are definitions needed by the state machine. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email addresses: + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Xingang Guo + * Jon Grimm + * Dajiang Zhang + * Sridhar Samudrala + * Daisy Chang + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + + +#include +#include +#include +#include +#include +#include + +#ifndef __sctp_sm_h__ +#define __sctp_sm_h__ + +/* + * Possible values for the disposition are: + */ +typedef enum { + SCTP_DISPOSITION_DISCARD, /* No further processing. */ + SCTP_DISPOSITION_CONSUME, /* Process return values normally. */ + SCTP_DISPOSITION_NOMEM, /* We ran out of memory--recover. */ + SCTP_DISPOSITION_DELETE_TCB, /* Close the association. */ + SCTP_DISPOSITION_ABORT, /* Close the association NOW. */ + SCTP_DISPOSITION_VIOLATION, /* The peer is misbehaving. */ + SCTP_DISPOSITION_NOT_IMPL, /* This entry is not implemented. */ + SCTP_DISPOSITION_ERROR, /* This is plain old user error. */ + SCTP_DISPOSITION_BUG, /* This is a bug. */ +} sctp_disposition_t; + +typedef struct { + int name; + int action; +} sctp_sm_command_t; + +typedef sctp_disposition_t (sctp_state_fn_t) (const struct sctp_endpoint *, + const struct sctp_association *, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *); +typedef void (sctp_timer_event_t) (unsigned long); +typedef struct { + sctp_state_fn_t *fn; + char *name; +} sctp_sm_table_entry_t; + +/* A naming convention of "sctp_sf_xxx" applies to all the state functions + * currently in use. + */ + +/* Prototypes for generic state functions. */ +sctp_state_fn_t sctp_sf_not_impl; +sctp_state_fn_t sctp_sf_bug; + +/* Prototypes for gener timer state functions. */ +sctp_state_fn_t sctp_sf_timer_ignore; + +/* Prototypes for chunk state functions. */ +sctp_state_fn_t sctp_sf_do_9_1_abort; +sctp_state_fn_t sctp_sf_cookie_wait_abort; +sctp_state_fn_t sctp_sf_cookie_echoed_abort; +sctp_state_fn_t sctp_sf_shutdown_pending_abort; +sctp_state_fn_t sctp_sf_shutdown_sent_abort; +sctp_state_fn_t sctp_sf_shutdown_ack_sent_abort; +sctp_state_fn_t sctp_sf_do_5_1B_init; +sctp_state_fn_t sctp_sf_do_5_1C_ack; +sctp_state_fn_t sctp_sf_do_5_1D_ce; +sctp_state_fn_t sctp_sf_do_5_1E_ca; +sctp_state_fn_t sctp_sf_do_4_C; +sctp_state_fn_t sctp_sf_eat_data_6_2; +sctp_state_fn_t sctp_sf_eat_data_fast_4_4; +sctp_state_fn_t sctp_sf_eat_sack_6_2; +sctp_state_fn_t sctp_sf_tabort_8_4_8; +sctp_state_fn_t sctp_sf_operr_notify; +sctp_state_fn_t sctp_sf_t1_timer_expire; +sctp_state_fn_t sctp_sf_t2_timer_expire; +sctp_state_fn_t sctp_sf_t5_timer_expire; +sctp_state_fn_t sctp_sf_sendbeat_8_3; +sctp_state_fn_t sctp_sf_beat_8_3; +sctp_state_fn_t sctp_sf_backbeat_8_3; +sctp_state_fn_t sctp_sf_do_9_2_final; +sctp_state_fn_t sctp_sf_do_9_2_shutdown; +sctp_state_fn_t sctp_sf_do_ecn_cwr; +sctp_state_fn_t sctp_sf_do_ecne; +sctp_state_fn_t sctp_sf_ootb; +sctp_state_fn_t sctp_sf_shut_8_4_5; +sctp_state_fn_t sctp_sf_pdiscard; +sctp_state_fn_t sctp_sf_violation; +sctp_state_fn_t sctp_sf_discard_chunk; +sctp_state_fn_t sctp_sf_do_5_2_1_siminit; +sctp_state_fn_t sctp_sf_do_5_2_2_dupinit; +sctp_state_fn_t sctp_sf_do_5_2_4_dupcook; +sctp_state_fn_t sctp_sf_unk_chunk; +sctp_state_fn_t sctp_sf_do_8_5_1_E_sa; +sctp_state_fn_t sctp_sf_cookie_echoed_err; +sctp_state_fn_t sctp_sf_do_5_2_6_stale; + +/* Prototypes for primitive event state functions. */ +sctp_state_fn_t sctp_sf_do_prm_asoc; +sctp_state_fn_t sctp_sf_do_prm_send; +sctp_state_fn_t sctp_sf_do_9_2_prm_shutdown; +sctp_state_fn_t sctp_sf_cookie_wait_prm_shutdown; +sctp_state_fn_t sctp_sf_cookie_echoed_prm_shutdown; +sctp_state_fn_t sctp_sf_do_9_1_prm_abort; +sctp_state_fn_t sctp_sf_cookie_wait_prm_abort; +sctp_state_fn_t sctp_sf_cookie_echoed_prm_abort; +sctp_state_fn_t sctp_sf_shutdown_pending_prm_abort; +sctp_state_fn_t sctp_sf_shutdown_sent_prm_abort; +sctp_state_fn_t sctp_sf_shutdown_ack_sent_prm_abort; +sctp_state_fn_t sctp_sf_error_closed; +sctp_state_fn_t sctp_sf_error_shutdown; +sctp_state_fn_t sctp_sf_ignore_primitive; +sctp_state_fn_t sctp_sf_do_prm_requestheartbeat; + +/* Prototypes for other event state functions. */ +sctp_state_fn_t sctp_sf_do_9_2_start_shutdown; +sctp_state_fn_t sctp_sf_do_9_2_shutdown_ack; +sctp_state_fn_t sctp_sf_ignore_other; + +/* Prototypes for timeout event state functions. */ +sctp_state_fn_t sctp_sf_do_6_3_3_rtx; +sctp_state_fn_t sctp_sf_do_6_2_sack; +sctp_state_fn_t sctp_sf_autoclose_timer_expire; + + +/* These are state functions which are either obsolete or not in use yet. + * If any of these functions needs to be revived, it should be renamed with + * the "sctp_sf_xxx" prefix, and be moved to the above prototype groups. + */ + +/* Prototypes for chunk state functions. Not in use. */ +sctp_state_fn_t sctp_sf_do_9_2_reshutack; +sctp_state_fn_t sctp_sf_do_9_2_reshut; +sctp_state_fn_t sctp_sf_do_9_2_shutack; + +sctp_state_fn_t lucky; +sctp_state_fn_t other_stupid; + +/* Prototypes for timeout event state functions. Not in use. */ +sctp_state_fn_t sctp_do_4_2_reinit; +sctp_state_fn_t sctp_do_4_3_reecho; +sctp_state_fn_t sctp_do_9_2_reshut; +sctp_state_fn_t sctp_do_9_2_reshutack; +sctp_state_fn_t sctp_do_8_3_hb_err; +sctp_state_fn_t sctp_heartoff; + +/* Prototypes for addip related state functions. Not in use. */ +sctp_state_fn_t sctp_addip_do_asconf; +sctp_state_fn_t sctp_addip_do_asconf_ack; + +/* Prototypes for utility support functions. */ +__u8 sctp_get_chunk_type(struct sctp_chunk *chunk); +sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, + sctp_state_t state, + sctp_subtype_t event_subtype); +int sctp_chunk_iif(const struct sctp_chunk *); +struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *, + struct sctp_chunk *, + int gfp); +__u32 sctp_generate_verification_tag(void); +void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag); + +/* Prototypes for chunk-building functions. */ +sctp_chunk_t *sctp_make_init(const struct sctp_association *, + const sctp_bind_addr_t *, + int gfp, int vparam_len); +sctp_chunk_t *sctp_make_init_ack(const struct sctp_association *, + const sctp_chunk_t *, + const int gfp, + const int unkparam_len); +sctp_chunk_t *sctp_make_cookie_echo(const struct sctp_association *, + const sctp_chunk_t *); +sctp_chunk_t *sctp_make_cookie_ack(const struct sctp_association *, + const sctp_chunk_t *); +sctp_chunk_t *sctp_make_cwr(const struct sctp_association *, + const __u32 lowest_tsn, + const sctp_chunk_t *); +sctp_chunk_t *sctp_make_datafrag(struct sctp_association *, + const struct sctp_sndrcvinfo *sinfo, + int len, const __u8 *data, + __u8 flags, __u16 ssn); +sctp_chunk_t * sctp_make_datafrag_empty(struct sctp_association *, + const struct sctp_sndrcvinfo *sinfo, + int len, const __u8 flags, + __u16 ssn); +sctp_chunk_t *sctp_make_data(struct sctp_association *, + const struct sctp_sndrcvinfo *sinfo, + int len, const __u8 *data); +sctp_chunk_t *sctp_make_data_empty(struct sctp_association *, + const struct sctp_sndrcvinfo *, int len); +sctp_chunk_t *sctp_make_ecne(const struct sctp_association *, + const __u32); +sctp_chunk_t *sctp_make_sack(const struct sctp_association *); +sctp_chunk_t *sctp_make_shutdown(const struct sctp_association *asoc); +sctp_chunk_t *sctp_make_shutdown_ack(const struct sctp_association *asoc, + const sctp_chunk_t *); +sctp_chunk_t *sctp_make_shutdown_complete(const struct sctp_association *, + const sctp_chunk_t *); +void sctp_init_cause(sctp_chunk_t *, __u16 cause, const void *, size_t); +sctp_chunk_t *sctp_make_abort(const struct sctp_association *, + const sctp_chunk_t *, + const size_t hint); +sctp_chunk_t *sctp_make_abort_no_data(const struct sctp_association *, + const sctp_chunk_t *, + __u32 tsn); +sctp_chunk_t *sctp_make_abort_user(const struct sctp_association *, + const sctp_chunk_t *, + const struct msghdr *); +sctp_chunk_t *sctp_make_heartbeat(const struct sctp_association *, + const struct sctp_transport *, + const void *payload, + const size_t paylen); +sctp_chunk_t *sctp_make_heartbeat_ack(const struct sctp_association *, + const sctp_chunk_t *, + const void *payload, + const size_t paylen); +sctp_chunk_t *sctp_make_op_error(const struct sctp_association *, + const sctp_chunk_t *chunk, + __u16 cause_code, + const void *payload, + size_t paylen); +void sctp_chunk_assign_tsn(sctp_chunk_t *); +void sctp_chunk_assign_ssn(sctp_chunk_t *); +int sctp_datachunks_from_user(struct sctp_association *, + const struct sctp_sndrcvinfo *, + struct msghdr *, int len, + struct sk_buff_head *); + + +/* Prototypes for statetable processing. */ + +int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *, + struct sctp_association *asoc, + void *event_arg, + int gfp); + +int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int gfp); + +/* 2nd level prototypes */ +int +sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *retval, + int gfp); + + +int sctp_gen_sack(struct sctp_association *, int force, sctp_cmd_seq_t *); +void sctp_do_TSNdup(struct sctp_association *, sctp_chunk_t *, long gap); + +void sctp_generate_t3_rtx_event(unsigned long peer); +void sctp_generate_heartbeat_event(unsigned long peer); + +sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *); +struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *, + const struct sctp_association *, + struct sctp_chunk *chunk, + const void *payload, + size_t paylen); +struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *, + const struct sctp_chunk *); +void sctp_ootb_pkt_free(struct sctp_packet *); + +sctp_cookie_param_t * +sctp_pack_cookie(const struct sctp_endpoint *, const struct sctp_association *, + const struct sctp_chunk *, int *cookie_len, + const __u8 *, int addrs_len); +struct sctp_association *sctp_unpack_cookie(const struct sctp_endpoint *, + const struct sctp_association *, + sctp_chunk_t *, int gfp, int *err, + sctp_chunk_t **err_chk_p); +int sctp_addip_addr_config(struct sctp_association *, sctp_param_t, + struct sockaddr_storage*, int); +void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + sctp_chunk_t *err_chunk); + +/* 3rd level prototypes */ +__u32 sctp_generate_tag(const struct sctp_endpoint *); +__u32 sctp_generate_tsn(const struct sctp_endpoint *); + +/* 4th level prototypes */ +void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *, + __u16 port, int iif); +int sctp_addr2sockaddr(const union sctp_params, union sctp_addr *); +int sockaddr2sctp_addr(const union sctp_addr *, sctp_addr_param_t *); + +/* Extern declarations for major data structures. */ +sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t); +extern sctp_sm_table_entry_t +primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES]; +extern sctp_sm_table_entry_t +other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES]; +extern sctp_sm_table_entry_t +timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES]; +extern sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES]; + +/* These are some handy utility macros... */ + + +/* Get the size of a DATA chunk payload. */ +static inline __u16 sctp_data_size(sctp_chunk_t *chunk) +{ + __u16 size; + + size = ntohs(chunk->chunk_hdr->length); + size -= sizeof(sctp_data_chunk_t); + + return size; +} + +/* Compare two TSNs */ + +/* RFC 1982 - Serial Number Arithmetic + * + * 2. Comparison + * Then, s1 is said to be equal to s2 if and only if i1 is equal to i2, + * in all other cases, s1 is not equal to s2. + * + * s1 is said to be less than s2 if, and only if, s1 is not equal to s2, + * and + * + * (i1 < i2 and i2 - i1 < 2^(SERIAL_BITS - 1)) or + * (i1 > i2 and i1 - i2 > 2^(SERIAL_BITS - 1)) + * + * s1 is said to be greater than s2 if, and only if, s1 is not equal to + * s2, and + * + * (i1 < i2 and i2 - i1 > 2^(SERIAL_BITS - 1)) or + * (i1 > i2 and i1 - i2 < 2^(SERIAL_BITS - 1)) + */ + +/* + * RFC 2960 + * 1.6 Serial Number Arithmetic + * + * Comparisons and arithmetic on TSNs in this document SHOULD use Serial + * Number Arithmetic as defined in [RFC1982] where SERIAL_BITS = 32. + */ + +enum { + TSN_SIGN_BIT = (1<<31) +}; + +static inline int TSN_lt(__u32 s, __u32 t) +{ + return (((s) - (t)) & TSN_SIGN_BIT); +} + +static inline int TSN_lte(__u32 s, __u32 t) +{ + return (((s) == (t)) || (((s) - (t)) & TSN_SIGN_BIT)); +} + +/* Compare two SSNs */ + +/* + * RFC 2960 + * 1.6 Serial Number Arithmetic + * + * Comparisons and arithmetic on Stream Sequence Numbers in this document + * SHOULD use Serial Number Arithmetic as defined in [RFC1982] where + * SERIAL_BITS = 16. + */ +enum { + SSN_SIGN_BIT = (1<<15) +}; + +static inline int SSN_lt(__u16 s, __u16 t) +{ + return (((s) - (t)) & SSN_SIGN_BIT); +} + +static inline int SSN_lte(__u16 s, __u16 t) +{ + return (((s) == (t)) || (((s) - (t)) & SSN_SIGN_BIT)); +} + +/* Run sctp_add_cmd() generating a BUG() if there is a failure. */ +static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) +{ + if (unlikely(!sctp_add_cmd(seq, verb, obj))) + BUG(); +} + +/* Check VTAG of the packet matches the sender's own tag OR its peer's + * tag and the T bit is set in the Chunk Flags. + */ +static inline int +sctp_vtag_verify_either(const sctp_chunk_t *chunk, + const struct sctp_association *asoc) +{ + /* RFC 2960 Section 8.5.1, sctpimpguide-06 Section 2.13.2 + * + * B) The receiver of a ABORT shall accept the packet if the + * Verification Tag field of the packet matches its own tag OR it + * is set to its peer's tag and the T bit is set in the Chunk + * Flags. Otherwise, the receiver MUST silently discard the packet + * and take no further action. + * + * (C) The receiver of a SHUTDOWN COMPLETE shall accept the + * packet if the Verification Tag field of the packet + * matches its own tag OR it is set to its peer's tag and + * the T bit is set in the Chunk Flags. Otherwise, the + * receiver MUST silently discard the packet and take no + * further action.... + * + */ + if ((ntohl(chunk->sctp_hdr->vtag) == asoc->c.my_vtag) || + (sctp_test_T_bit(chunk) && (ntohl(chunk->sctp_hdr->vtag) + == asoc->c.peer_vtag))) { + return 1; + } + + return 0; +} + +#endif /* __sctp_sm_h__ */ diff -urN linux-2.4.22-bk23/include/net/sctp/structs.h linux-2.4.22-bk24/include/net/sctp/structs.h --- linux-2.4.22-bk23/include/net/sctp/structs.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/structs.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,1631 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001-2003 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email addresses: + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Randall Stewart + * Ken Morneau + * Qiaobing Xie + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Xingang Guo + * Hui Huang + * Sridhar Samudrala + * Daisy Chang + * Dajiang Zhang + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef __sctp_structs_h__ +#define __sctp_structs_h__ + +#include /* We get struct timespec. */ +#include /* linux/in.h needs this!! */ +#include /* We get struct sockaddr_in. */ +#include /* We get struct in6_addr */ +#include /* We get MAXHOSTNAMELEN. */ +#include /* This gets us atomic counters. */ +#include /* We need sk_buff_head. */ +#include /* We need sctp* header structs. */ +#include + +/* A convenience structure for handling sockaddr structures. + * We should wean ourselves off this. + */ +union sctp_addr { + struct sockaddr_in v4; + struct sockaddr_in6 v6; + struct sockaddr sa; +}; + + +/* Forward declarations for data structures. */ +struct sctp_protocol; +struct sctp_endpoint; +struct sctp_association; +struct sctp_transport; +struct sctp_packet; +struct sctp_chunk; +struct sctp_inq; +struct sctp_outq; +struct sctp_bind_addr; +struct sctp_ulpq; +struct sctp_opt; +struct sctp_ep_common; +struct sctp_ssnmap; + +typedef struct sctp_chunk sctp_chunk_t; +typedef struct sctp_bind_addr sctp_bind_addr_t; + +#include +#include +#include + +/* Structures useful for managing bind/connect. */ + +typedef struct sctp_bind_bucket { + unsigned short port; + unsigned short fastreuse; + struct sctp_bind_bucket *next; + struct sctp_bind_bucket **pprev; + struct sock *sk; +} sctp_bind_bucket_t; + +typedef struct sctp_bind_hashbucket { + spinlock_t lock; + struct sctp_bind_bucket *chain; +} sctp_bind_hashbucket_t; + +/* Used for hashing all associations. */ +typedef struct sctp_hashbucket { + rwlock_t lock; + struct sctp_ep_common *chain; +} sctp_hashbucket_t __attribute__((__aligned__(8))); + + +/* The SCTP protocol structure. */ +struct sctp_protocol { + /* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values + * + * The following protocol parameters are RECOMMENDED: + * + * RTO.Initial - 3 seconds + * RTO.Min - 1 second + * RTO.Max - 60 seconds + * RTO.Alpha - 1/8 (3 when converted to right shifts.) + * RTO.Beta - 1/4 (2 when converted to right shifts.) + */ + __u32 rto_initial; + __u32 rto_min; + __u32 rto_max; + + /* Note: rto_alpha and rto_beta are really defined as inverse + * powers of two to facilitate integer operations. + */ + int rto_alpha; + int rto_beta; + + /* Max.Burst - 4 */ + int max_burst; + + /* Valid.Cookie.Life - 60 seconds */ + int valid_cookie_life; + + /* Whether Cookie Preservative is enabled(1) or not(0) */ + int cookie_preserve_enable; + + /* Association.Max.Retrans - 10 attempts + * Path.Max.Retrans - 5 attempts (per destination address) + * Max.Init.Retransmits - 8 attempts + */ + int max_retrans_association; + int max_retrans_path; + int max_retrans_init; + + /* HB.interval - 30 seconds */ + int hb_interval; + + /* The following variables are implementation specific. */ + + /* Default initialization values to be applied to new associations. */ + __u16 max_instreams; + __u16 max_outstreams; + + /* This is a list of groups of functions for each address + * family that we support. + */ + struct list_head address_families; + + /* This is the hash of all endpoints. */ + int ep_hashsize; + sctp_hashbucket_t *ep_hashbucket; + + /* This is the hash of all associations. */ + int assoc_hashsize; + sctp_hashbucket_t *assoc_hashbucket; + + /* This is the sctp port control hash. */ + int port_hashsize; + int port_rover; + spinlock_t port_alloc_lock; /* Protects port_rover. */ + sctp_bind_hashbucket_t *port_hashtable; + + /* This is the global local address list. + * We actively maintain this complete list of interfaces on + * the system by catching routing events. + * + * It is a list of struct sockaddr_storage_list. + */ + struct list_head local_addr_list; + spinlock_t local_addr_lock; +}; + + +/* + * Pointers to address related SCTP functions. + * (i.e. things that depend on the address family.) + */ +struct sctp_af { + int (*sctp_xmit) (struct sk_buff *skb, + struct sctp_transport *, + int ipfragok); + int (*setsockopt) (struct sock *sk, + int level, + int optname, + char *optval, + int optlen); + int (*getsockopt) (struct sock *sk, + int level, + int optname, + char *optval, + int *optlen); + struct dst_entry *(*get_dst) (struct sctp_association *asoc, + union sctp_addr *daddr, + union sctp_addr *saddr); + void (*get_saddr) (struct sctp_association *asoc, + struct dst_entry *dst, + union sctp_addr *daddr, + union sctp_addr *saddr); + void (*copy_addrlist) (struct list_head *, + struct net_device *); + void (*dst_saddr) (union sctp_addr *saddr, + struct dst_entry *dst, + unsigned short port); + int (*cmp_addr) (const union sctp_addr *addr1, + const union sctp_addr *addr2); + void (*addr_copy) (union sctp_addr *dst, + union sctp_addr *src); + void (*from_skb) (union sctp_addr *, + struct sk_buff *skb, + int saddr); + void (*from_sk) (union sctp_addr *, + struct sock *sk); + void (*to_sk_saddr) (union sctp_addr *, + struct sock *sk); + void (*to_sk_daddr) (union sctp_addr *, + struct sock *sk); + int (*addr_valid) (union sctp_addr *); + sctp_scope_t (*scope) (union sctp_addr *); + void (*inaddr_any) (union sctp_addr *, unsigned short); + int (*is_any) (const union sctp_addr *); + int (*available) (const union sctp_addr *); + int (*skb_iif) (const struct sk_buff *sk); + __u16 net_header_len; + int sockaddr_len; + sa_family_t sa_family; + struct list_head list; +}; + +struct sctp_af *sctp_get_af_specific(sa_family_t); +int sctp_register_af(struct sctp_af *); + +/* Protocol family functions. */ +struct sctp_pf { + void (*event_msgname)(struct sctp_ulpevent *, char *, int *); + void (*skb_msgname) (struct sk_buff *, char *, int *); + int (*af_supported) (sa_family_t); + int (*cmp_addr) (const union sctp_addr *, + const union sctp_addr *, + struct sctp_opt *); + int (*bind_verify) (struct sctp_opt *, union sctp_addr *); + int (*send_verify) (struct sctp_opt *, union sctp_addr *); + int (*supported_addrs)(const struct sctp_opt *, __u16 *); + struct sock *(*create_accept_sk) (struct sock *sk, + struct sctp_association *asoc); + struct sctp_af *af; +}; + +/* SCTP Socket type: UDP or TCP style. */ +typedef enum { + SCTP_SOCKET_UDP = 0, + SCTP_SOCKET_UDP_HIGH_BANDWIDTH, + SCTP_SOCKET_TCP +} sctp_socket_type_t; + +/* Per socket SCTP information. */ +struct sctp_opt { + /* What kind of a socket is this? */ + sctp_socket_type_t type; + + /* PF_ family specific functions. */ + struct sctp_pf *pf; + + /* Access to HMAC transform. */ + struct crypto_tfm *hmac; + + /* What is our base endpointer? */ + struct sctp_endpoint *ep; + + /* Various Socket Options. */ + __u16 default_stream; + __u32 default_ppid; + struct sctp_initmsg initmsg; + struct sctp_rtoinfo rtoinfo; + struct sctp_paddrparams paddrparam; + struct sctp_event_subscribe subscribe; + __u32 autoclose; + __u8 nodelay; + __u8 disable_fragments; + __u8 pd_mode; + + /* Receive to here while partial delivery is in effect. */ + struct sk_buff_head pd_lobby; +}; + + + +/* This is our APPLICATION-SPECIFIC state cookie. + * THIS IS NOT DICTATED BY THE SPECIFICATION. + */ +/* These are the parts of an association which we send in the cookie. + * Most of these are straight out of: + * RFC2960 12.2 Parameters necessary per association (i.e. the TCB) + * + */ + +typedef struct sctp_cookie { + /* My : Tag expected in every inbound packet and sent + * Verification: in the INIT or INIT ACK chunk. + * Tag : + */ + __u32 my_vtag; + + /* Peer's : Tag expected in every outbound packet except + * Verification: in the INIT chunk. + * Tag : + */ + __u32 peer_vtag; + + /* The rest of these are not from the spec, but really need to + * be in the cookie. + */ + + /* My Tie Tag : Assist in discovering a restarting association. */ + __u32 my_ttag; + + /* Peer's Tie Tag: Assist in discovering a restarting association. */ + __u32 peer_ttag; + + /* Number of inbound/outbound streams which are set + * and negotiated during the INIT process. + */ + __u16 sinit_num_ostreams; + __u16 sinit_max_instreams; + + /* When does this cookie expire? */ + struct timeval expiration __attribute__((packed)); + + /* This is the first sequence number I used. */ + __u32 initial_tsn; + + /* This holds the originating address of the INIT packet. */ + union sctp_addr peer_addr; + + /* This is a shim for my peer's INIT packet, followed by + * a copy of the raw address list of the association. + * The length of the raw address list is saved in the + * raw_addr_list_len field, which will be used at the time when + * the association TCB is re-constructed from the cookie. + */ + __u32 raw_addr_list_len; + sctp_init_chunk_t peer_init[0]; +} sctp_cookie_t __attribute__((aligned(4))); + + +/* The format of our cookie that we send to our peer. */ +typedef struct sctp_signed_cookie { + __u8 signature[SCTP_SECRET_SIZE]; + sctp_cookie_t c; +} sctp_signed_cookie_t; + +/* This is another convenience type to allocate memory for address + * params for the maximum size and pass such structures around + * internally. + */ +typedef union { + sctp_ipv4addr_param_t v4; + sctp_ipv6addr_param_t v6; +} sctp_addr_param_t; + +/* A convenience type to allow walking through the various + * parameters and avoid casting all over the place. + */ +union sctp_params { + void *v; + sctp_paramhdr_t *p; + sctp_cookie_preserve_param_t *life; + sctp_hostname_param_t *dns; + sctp_cookie_param_t *cookie; + sctp_supported_addrs_param_t *sat; + sctp_ipv4addr_param_t *v4; + sctp_ipv6addr_param_t *v6; + sctp_addr_param_t *addr; +}; + +/* RFC 2960. Section 3.3.5 Heartbeat. + * Heartbeat Information: variable length + * The Sender-specific Heartbeat Info field should normally include + * information about the sender's current time when this HEARTBEAT + * chunk is sent and the destination transport address to which this + * HEARTBEAT is sent (see Section 8.3). + */ +typedef struct sctp_sender_hb_info { + sctp_paramhdr_t param_hdr; + union sctp_addr daddr; + unsigned long sent_at; +} sctp_sender_hb_info_t __attribute__((packed, aligned(4))); + +/* + * RFC 2960 1.3.2 Sequenced Delivery within Streams + * + * The term "stream" is used in SCTP to refer to a sequence of user + * messages that are to be delivered to the upper-layer protocol in + * order with respect to other messages within the same stream. This is + * in contrast to its usage in TCP, where it refers to a sequence of + * bytes (in this document a byte is assumed to be eight bits). + * ... + * + * This is the structure we use to track both our outbound and inbound + * SSN, or Stream Sequence Numbers. + */ + +struct sctp_stream { + __u16 *ssn; + unsigned int len; +}; + +struct sctp_ssnmap { + struct sctp_stream in; + struct sctp_stream out; + int malloced; +}; + +struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *, __u16, __u16); +struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int gfp); +void sctp_ssnmap_free(struct sctp_ssnmap *map); +void sctp_ssnmap_clear(struct sctp_ssnmap *map); + +/* What is the current SSN number for this stream? */ +static inline __u16 sctp_ssn_peek(struct sctp_stream *stream, __u16 id) +{ + return stream->ssn[id]; +} + +/* Return the next SSN number for this stream. */ +static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id) +{ + return stream->ssn[id]++; +} + + +/* RFC2960 1.4 Key Terms + * + * o Chunk: A unit of information within an SCTP packet, consisting of + * a chunk header and chunk-specific content. + * + * As a matter of convenience, we remember the SCTP common header for + * each chunk as well as a few other header pointers... + */ +struct sctp_chunk { + /* These first three elements MUST PRECISELY match the first + * three elements of struct sk_buff. This allows us to reuse + * all the skb_* queue management functions. + */ + sctp_chunk_t *next; + sctp_chunk_t *prev; + struct sk_buff_head *list; + + /* This is our link to the per-transport transmitted list. */ + struct list_head transmitted_list; + + /* This field is used by chunks that hold fragmented data. + * For the first fragment this is the list that holds the rest of + * fragments. For the remaining fragments, this is the link to the + * frag_list maintained in the first fragment. + */ + struct list_head frag_list; + + /* This points to the sk_buff containing the actual data. */ + struct sk_buff *skb; + + /* These are the SCTP headers by reverse order in a packet. + * Note that some of these may happen more than once. In that + * case, we point at the "current" one, whatever that means + * for that level of header. + */ + + /* We point this at the FIRST TLV parameter to chunk_hdr. */ + union sctp_params param_hdr; + union { + __u8 *v; + sctp_datahdr_t *data_hdr; + sctp_inithdr_t *init_hdr; + sctp_sackhdr_t *sack_hdr; + sctp_heartbeathdr_t *hb_hdr; + sctp_sender_hb_info_t *hbs_hdr; + sctp_shutdownhdr_t *shutdown_hdr; + sctp_signed_cookie_t *cookie_hdr; + sctp_ecnehdr_t *ecne_hdr; + sctp_cwrhdr_t *ecn_cwr_hdr; + sctp_errhdr_t *err_hdr; + } subh; + + __u8 *chunk_end; + + sctp_chunkhdr_t *chunk_hdr; + + sctp_sctphdr_t *sctp_hdr; + + /* This needs to be recoverable for SCTP_SEND_FAILED events. */ + struct sctp_sndrcvinfo sinfo; + + /* Which association does this belong to? */ + struct sctp_association *asoc; + + /* What endpoint received this chunk? */ + struct sctp_ep_common *rcvr; + + /* We fill this in if we are calculating RTT. */ + unsigned long sent_at; + + __u8 rtt_in_progress; /* Is this chunk used for RTT calculation? */ + __u8 num_times_sent; /* How man times did we send this? */ + __u8 has_tsn; /* Does this chunk have a TSN yet? */ + __u8 has_ssn; /* Does this chunk have a SSN yet? */ + __u8 singleton; /* Was this the only chunk in the packet? */ + __u8 end_of_packet; /* Was this the last chunk in the packet? */ + __u8 ecn_ce_done; /* Have we processed the ECN CE bit? */ + __u8 pdiscard; /* Discard the whole packet now? */ + __u8 tsn_gap_acked; /* Is this chunk acked by a GAP ACK? */ + __u8 fast_retransmit; /* Is this chunk fast retransmitted? */ + __u8 tsn_missing_report; /* Data chunk missing counter. */ + + /* What is the origin IP address for this chunk? */ + union sctp_addr source; + /* Destination address for this chunk. */ + union sctp_addr dest; + + /* For an inbound chunk, this tells us where it came from. + * For an outbound chunk, it tells us where we'd like it to + * go. It is NULL if we have no preference. + */ + struct sctp_transport *transport; +}; + +sctp_chunk_t *sctp_make_chunk(const struct sctp_association *, __u8 type, + __u8 flags, int size); +void sctp_free_chunk(sctp_chunk_t *); +void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data); +sctp_chunk_t *sctp_chunkify(struct sk_buff *, const struct sctp_association *, + struct sock *); +void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *); +const union sctp_addr *sctp_source(const sctp_chunk_t *chunk); + +/* This is a structure for holding either an IPv6 or an IPv4 address. */ +/* sin_family -- AF_INET or AF_INET6 + * sin_port -- ordinary port number + * sin_addr -- cast to either (struct in_addr) or (struct in6_addr) + */ +struct sockaddr_storage_list { + struct list_head list; + union sctp_addr a; +}; + +typedef sctp_chunk_t *(sctp_packet_phandler_t)(struct sctp_association *); + +/* This structure holds lists of chunks as we are assembling for + * transmission. + */ +struct sctp_packet { + /* These are the SCTP header values (host order) for the packet. */ + __u16 source_port; + __u16 destination_port; + __u32 vtag; + + /* This contains the payload chunks. */ + struct sk_buff_head chunks; + /* This is the total size of all chunks INCLUDING padding. */ + size_t size; + + /* The packet is destined for this transport address. + * The function we finally use to pass down to the next lower + * layer lives in the transport structure. + */ + struct sctp_transport *transport; + + /* Allow a callback for getting a high priority chunk + * bundled early into the packet (This is used for ECNE). + */ + sctp_packet_phandler_t *get_prepend_chunk; + + /* This packet should advertise ECN capability to the network + * via the ECT bit. + */ + char ecn_capable; + + /* This packet contains a COOKIE-ECHO chunk. */ + char has_cookie_echo; + + /* This packet containsa SACK chunk. */ + char has_sack; + + /* SCTP cannot fragment this packet. So let ip fragment it. */ + char ipfragok; + + int malloced; +}; + +typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *); +typedef int (sctp_outq_ehandler_t)(struct sctp_outq *); +typedef struct sctp_packet *(sctp_outq_ohandler_init_t) + (struct sctp_packet *, + struct sctp_transport *, + __u16 sport, + __u16 dport); +typedef struct sctp_packet *(sctp_outq_ohandler_config_t) + (struct sctp_packet *, + __u32 vtag, + int ecn_capable, + sctp_packet_phandler_t *get_prepend_chunk); +typedef sctp_xmit_t (sctp_outq_ohandler_t)(struct sctp_packet *, + sctp_chunk_t *); +typedef int (sctp_outq_ohandler_force_t)(struct sctp_packet *); + +sctp_outq_ohandler_init_t sctp_packet_init; +sctp_outq_ohandler_config_t sctp_packet_config; +sctp_outq_ohandler_t sctp_packet_append_chunk; +sctp_outq_ohandler_t sctp_packet_transmit_chunk; +sctp_outq_ohandler_force_t sctp_packet_transmit; +void sctp_packet_free(struct sctp_packet *); + + +/* This represents a remote transport address. + * For local transport addresses, we just use union sctp_addr. + * + * RFC2960 Section 1.4 Key Terms + * + * o Transport address: A Transport Address is traditionally defined + * by Network Layer address, Transport Layer protocol and Transport + * Layer port number. In the case of SCTP running over IP, a + * transport address is defined by the combination of an IP address + * and an SCTP port number (where SCTP is the Transport protocol). + * + * RFC2960 Section 7.1 SCTP Differences from TCP Congestion control + * + * o The sender keeps a separate congestion control parameter set for + * each of the destination addresses it can send to (not each + * source-destination pair but for each destination). The parameters + * should decay if the address is not used for a long enough time + * period. + * + */ +struct sctp_transport { + /* A list of transports. */ + struct list_head transports; + + /* Reference counting. */ + atomic_t refcnt; + int dead; + + /* This is the peer's IP address and port. */ + union sctp_addr ipaddr; + + /* These are the functions we call to handle LLP stuff. */ + struct sctp_af *af_specific; + + /* Which association do we belong to? */ + struct sctp_association *asoc; + + /* RFC2960 + * + * 12.3 Per Transport Address Data + * + * For each destination transport address in the peer's + * address list derived from the INIT or INIT ACK chunk, a + * number of data elements needs to be maintained including: + */ + __u32 rtt; /* This is the most recent RTT. */ + + /* RTO : The current retransmission timeout value. */ + __u32 rto; + + /* RTTVAR : The current RTT variation. */ + __u32 rttvar; + + /* SRTT : The current smoothed round trip time. */ + __u32 srtt; + + /* RTO-Pending : A flag used to track if one of the DATA + * chunks sent to this address is currently being + * used to compute a RTT. If this flag is 0, + * the next DATA chunk sent to this destination + * should be used to compute a RTT and this flag + * should be set. Every time the RTT + * calculation completes (i.e. the DATA chunk + * is SACK'd) clear this flag. + */ + int rto_pending; + + /* + * These are the congestion stats. + */ + /* cwnd : The current congestion window. */ + __u32 cwnd; /* This is the actual cwnd. */ + + /* ssthresh : The current slow start threshold value. */ + __u32 ssthresh; + + /* partial : The tracking method for increase of cwnd when in + * bytes acked : congestion avoidance mode (see Section 6.2.2) + */ + __u32 partial_bytes_acked; + + /* Data that has been sent, but not acknowledged. */ + __u32 flight_size; + + /* PMTU : The current known path MTU. */ + __u32 pmtu; + + /* Destination */ + struct dst_entry *dst; + /* Source address. */ + union sctp_addr saddr; + + /* When was the last time(in jiffies) that a data packet was sent on + * this transport? This is used to adjust the cwnd when the transport + * becomes inactive. + */ + unsigned long last_time_used; + + /* Heartbeat interval: The endpoint sends out a Heartbeat chunk to + * the destination address every heartbeat interval. + */ + int hb_interval; + + /* When was the last time (in jiffies) that we heard from this + * transport? We use this to pick new active and retran paths. + */ + unsigned long last_time_heard; + + /* Last time(in jiffies) when cwnd is reduced due to the congestion + * indication based on ECNE chunk. + */ + unsigned long last_time_ecne_reduced; + + /* active : The current active state of this destination, + * : i.e. DOWN, UP, etc. + */ + int active; + + /* hb_allowed : The current heartbeat state of this destination, + * : i.e. ALLOW-HB, NO-HEARTBEAT, etc. + */ + int hb_allowed; + + /* These are the error stats for this destination. */ + + /* Error count : The current error count for this destination. */ + unsigned short error_count; + + /* Error : Current error threshold for this destination + * Threshold : i.e. what value marks the destination down if + * : errorCount reaches this value. + */ + unsigned short error_threshold; + + /* This is the max_retrans value for the transport and will + * be initialized to proto.max_retrans.path. This can be changed + * using SCTP_SET_PEER_ADDR_PARAMS socket option. + */ + int max_retrans; + + /* We use this name for debugging output... */ + char *debug_name; + + /* Per : A timer used by each destination. + * Destination : + * Timer : + * + * [Everywhere else in the text this is called T3-rtx. -ed] + */ + struct timer_list T3_rtx_timer; + + /* Heartbeat timer is per destination. */ + struct timer_list hb_timer; + + /* Since we're using per-destination retransmission timers + * (see above), we're also using per-destination "transmitted" + * queues. This probably ought to be a private struct + * accessible only within the outqueue, but it's not, yet. + */ + struct list_head transmitted; + + /* We build bundle-able packets for this transport here. */ + struct sctp_packet packet; + + /* This is the list of transports that have chunks to send. */ + struct list_head send_ready; + + int malloced; /* Is this structure kfree()able? */ +}; + +struct sctp_transport *sctp_transport_new(const union sctp_addr *, int); +struct sctp_transport *sctp_transport_init(struct sctp_transport *, + const union sctp_addr *, int); +void sctp_transport_set_owner(struct sctp_transport *, + struct sctp_association *); +void sctp_transport_route(struct sctp_transport *, union sctp_addr *, + struct sctp_opt *); +void sctp_transport_pmtu(struct sctp_transport *); +void sctp_transport_free(struct sctp_transport *); +void sctp_transport_destroy(struct sctp_transport *); +void sctp_transport_reset_timers(struct sctp_transport *); +void sctp_transport_hold(struct sctp_transport *); +void sctp_transport_put(struct sctp_transport *); +void sctp_transport_update_rto(struct sctp_transport *, __u32); +void sctp_transport_raise_cwnd(struct sctp_transport *, __u32, __u32); +void sctp_transport_lower_cwnd(struct sctp_transport *, sctp_lower_cwnd_t); +unsigned long sctp_transport_timeout(struct sctp_transport *); + + +/* This is the structure we use to queue packets as they come into + * SCTP. We write packets to it and read chunks from it. + */ +struct sctp_inq { + /* This is actually a queue of sctp_chunk each + * containing a partially decoded packet. + */ + struct sk_buff_head in; + /* This is the packet which is currently off the in queue and is + * being worked on through the inbound chunk processing. + */ + sctp_chunk_t *in_progress; + + /* This is the delayed task to finish delivering inbound + * messages. + */ + struct tq_struct immediate; + + int malloced; /* Is this structure kfree()able? */ +}; + +struct sctp_inq *sctp_inq_new(void); +void sctp_inq_init(struct sctp_inq *); +void sctp_inq_free(struct sctp_inq *); +void sctp_inq_push(struct sctp_inq *, sctp_chunk_t *packet); +struct sctp_chunk *sctp_inq_pop(struct sctp_inq *); +void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *); + +/* This is the structure we use to hold outbound chunks. You push + * chunks in and they automatically pop out the other end as bundled + * packets (it calls (*output_handler)()). + * + * This structure covers sections 6.3, 6.4, 6.7, 6.8, 6.10, 7., 8.1, + * and 8.2 of the v13 draft. + * + * It handles retransmissions. The connection to the timeout portion + * of the state machine is through sctp_..._timeout() and timeout_handler. + * + * If you feed it SACKs, it will eat them. + * + * If you give it big chunks, it will fragment them. + * + * It assigns TSN's to data chunks. This happens at the last possible + * instant before transmission. + * + * When free()'d, it empties itself out via output_handler(). + */ +struct sctp_outq { + struct sctp_association *asoc; + + /* Data pending that has never been transmitted. */ + struct sk_buff_head out; + + unsigned out_qlen; /* Total length of queued data chunks. */ + + /* Error of send failed, may used in SCTP_SEND_FAILED event. */ + unsigned error; + + /* These are control chunks we want to send. */ + struct sk_buff_head control; + + /* These are chunks that have been sacked but are above the + * CTSN, or cumulative tsn ack point. + */ + struct list_head sacked; + + /* Put chunks on this list to schedule them for + * retransmission. + */ + struct list_head retransmit; + + /* Call these functions to send chunks down to the next lower + * layer. This is always sctp_packet, but we separate the two + * structures to make testing simpler. + */ + sctp_outq_ohandler_init_t *init_output; + sctp_outq_ohandler_config_t *config_output; + sctp_outq_ohandler_t *append_output; + sctp_outq_ohandler_t *build_output; + sctp_outq_ohandler_force_t *force_output; + + /* How many unackd bytes do we have in-flight? */ + __u32 outstanding_bytes; + + /* Is this structure empty? */ + int empty; + + /* Are we kfree()able? */ + int malloced; +}; + +struct sctp_outq *sctp_outq_new(struct sctp_association *); +void sctp_outq_init(struct sctp_association *, struct sctp_outq *); +void sctp_outq_teardown(struct sctp_outq *); +void sctp_outq_free(struct sctp_outq*); +int sctp_outq_tail(struct sctp_outq *, sctp_chunk_t *chunk); +int sctp_outq_flush(struct sctp_outq *, int); +int sctp_outq_sack(struct sctp_outq *, sctp_sackhdr_t *); +int sctp_outq_is_empty(const struct sctp_outq *); +int sctp_outq_set_output_handlers(struct sctp_outq *, + sctp_outq_ohandler_init_t init, + sctp_outq_ohandler_config_t config, + sctp_outq_ohandler_t append, + sctp_outq_ohandler_t build, + sctp_outq_ohandler_force_t force); +void sctp_outq_restart(struct sctp_outq *); +void sctp_retransmit(struct sctp_outq *, struct sctp_transport *, + sctp_retransmit_reason_t); +void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8); + + +/* These bind address data fields common between endpoints and associations */ +struct sctp_bind_addr { + + /* RFC 2960 12.1 Parameters necessary for the SCTP instance + * + * SCTP Port: The local SCTP port number the endpoint is + * bound to. + */ + __u16 port; + + /* RFC 2960 12.1 Parameters necessary for the SCTP instance + * + * Address List: The list of IP addresses that this instance + * has bound. This information is passed to one's + * peer(s) in INIT and INIT ACK chunks. + */ + struct list_head address_list; + + int malloced; /* Are we kfree()able? */ +}; + +sctp_bind_addr_t *sctp_bind_addr_new(int gfp_mask); +void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port); +void sctp_bind_addr_free(sctp_bind_addr_t *); +int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src, + sctp_scope_t scope, int gfp,int flags); +int sctp_add_bind_addr(sctp_bind_addr_t *, union sctp_addr *, + int gfp); +int sctp_del_bind_addr(sctp_bind_addr_t *, union sctp_addr *); +int sctp_bind_addr_match(sctp_bind_addr_t *, const union sctp_addr *, + struct sctp_opt *); +union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp, + int *addrs_len, int gfp); +int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len, + __u16 port, int gfp); + +sctp_scope_t sctp_scope(const union sctp_addr *); +int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope); +int sctp_is_any(const union sctp_addr *addr); +int sctp_addr_is_valid(const union sctp_addr *addr); + + +/* What type of endpoint? */ +typedef enum { + SCTP_EP_TYPE_SOCKET, + SCTP_EP_TYPE_ASSOCIATION, +} sctp_endpoint_type_t; + +/* + * A common base class to bridge the implmentation view of a + * socket (usually listening) endpoint versus an association's + * local endpoint. + * This common structure is useful for several purposes: + * 1) Common interface for lookup routines. + * a) Subfunctions work for either endpoint or association + * b) Single interface to lookup allows hiding the lookup lock rather + * than acquiring it externally. + * 2) Common interface for the inbound chunk handling/state machine. + * 3) Common object handling routines for reference counting, etc. + * 4) Disentangle association lookup from endpoint lookup, where we + * do not have to find our endpoint to find our association. + * + */ + +struct sctp_ep_common { + /* Fields to help us manage our entries in the hash tables. */ + struct sctp_ep_common *next; + struct sctp_ep_common **pprev; + int hashent; + + /* Runtime type information. What kind of endpoint is this? */ + sctp_endpoint_type_t type; + + /* Some fields to help us manage this object. + * refcnt - Reference count access to this object. + * dead - Do not attempt to use this object. + * malloced - Do we need to kfree this object? + */ + atomic_t refcnt; + char dead; + char malloced; + + /* What socket does this endpoint belong to? */ + struct sock *sk; + + /* This is where we receive inbound chunks. */ + struct sctp_inq inqueue; + + /* This substructure includes the defining parameters of the + * endpoint: + * bind_addr.port is our shared port number. + * bind_addr.address_list is our set of local IP addresses. + */ + sctp_bind_addr_t bind_addr; + + /* Protection during address list comparisons. */ + rwlock_t addr_lock; +}; + + +/* RFC Section 1.4 Key Terms + * + * o SCTP endpoint: The logical sender/receiver of SCTP packets. On a + * multi-homed host, an SCTP endpoint is represented to its peers as a + * combination of a set of eligible destination transport addresses to + * which SCTP packets can be sent and a set of eligible source + * transport addresses from which SCTP packets can be received. + * All transport addresses used by an SCTP endpoint must use the + * same port number, but can use multiple IP addresses. A transport + * address used by an SCTP endpoint must not be used by another + * SCTP endpoint. In other words, a transport address is unique + * to an SCTP endpoint. + * + * From an implementation perspective, each socket has one of these. + * A TCP-style socket will have exactly one association on one of + * these. An UDP-style socket will have multiple associations hanging + * off one of these. + */ + +struct sctp_endpoint { + /* Common substructure for endpoint and association. */ + struct sctp_ep_common base; + + /* Associations: A list of current associations and mappings + * to the data consumers for each association. This + * may be in the form of a hash table or other + * implementation dependent structure. The data + * consumers may be process identification + * information such as file descriptors, named pipe + * pointer, or table pointers dependent on how SCTP + * is implemented. + */ + /* This is really a list of struct sctp_association entries. */ + struct list_head asocs; + + /* Secret Key: A secret key used by this endpoint to compute + * the MAC. This SHOULD be a cryptographic quality + * random number with a sufficient length. + * Discussion in [RFC1750] can be helpful in + * selection of the key. + */ + __u8 secret_key[SCTP_HOW_MANY_SECRETS][SCTP_SECRET_SIZE]; + int current_key; + int last_key; + int key_changed_at; + + /* Default timeouts. */ + int timeouts[SCTP_NUM_TIMEOUT_TYPES]; + + /* Various thresholds. */ + + /* Name for debugging output... */ + char *debug_name; +}; + +/* Recover the outter endpoint structure. */ +static inline struct sctp_endpoint *sctp_ep(struct sctp_ep_common *base) +{ + struct sctp_endpoint *ep; + + ep = container_of(base, struct sctp_endpoint, base); + return ep; +} + +/* These are function signatures for manipulating endpoints. */ +struct sctp_endpoint *sctp_endpoint_new(struct sock *, int); +struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *, + struct sock *, int gfp); +void sctp_endpoint_free(struct sctp_endpoint *); +void sctp_endpoint_put(struct sctp_endpoint *); +void sctp_endpoint_hold(struct sctp_endpoint *); +void sctp_endpoint_add_asoc(struct sctp_endpoint *, struct sctp_association *); +struct sctp_association *sctp_endpoint_lookup_assoc( + const struct sctp_endpoint *ep, + const union sctp_addr *paddr, + struct sctp_transport **); +int sctp_endpoint_is_peeled_off(struct sctp_endpoint *, + const union sctp_addr *); +struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *, + const union sctp_addr *); +int sctp_has_association(const union sctp_addr *laddr, + const union sctp_addr *paddr); + +int sctp_verify_init(const struct sctp_association *asoc, sctp_cid_t, + sctp_init_chunk_t *peer_init, struct sctp_chunk *chunk, + struct sctp_chunk **err_chunk); +int sctp_process_init(struct sctp_association *, sctp_cid_t cid, + const union sctp_addr *peer, + sctp_init_chunk_t *init, int gfp); +int sctp_process_param(struct sctp_association *, union sctp_params param, + const union sctp_addr *from, int gfp); +__u32 sctp_generate_tag(const struct sctp_endpoint *); +__u32 sctp_generate_tsn(const struct sctp_endpoint *); + + +/* RFC2960 + * + * 12. Recommended Transmission Control Block (TCB) Parameters + * + * This section details a recommended set of parameters that should + * be contained within the TCB for an implementation. This section is + * for illustrative purposes and should not be deemed as requirements + * on an implementation or as an exhaustive list of all parameters + * inside an SCTP TCB. Each implementation may need its own additional + * parameters for optimization. + */ + + +/* Here we have information about each individual association. */ +struct sctp_association { + + /* A base structure common to endpoint and association. + * In this context, it represents the associations's view + * of the local endpoint of the association. + */ + struct sctp_ep_common base; + + /* Associations on the same socket. */ + struct list_head asocs; + + /* This is a signature that lets us know that this is a + * struct sctp_association data structure. Used for mapping an + * association id to an association. + */ + __u32 eyecatcher; + + /* This is our parent endpoint. */ + struct sctp_endpoint *ep; + + /* These are those association elements needed in the cookie. */ + sctp_cookie_t c; + + /* This is all information about our peer. */ + struct { + /* rwnd + * + * Peer Rwnd : Current calculated value of the peer's rwnd. + */ + __u32 rwnd; + + /* transport_addr_list + * + * Peer : A list of SCTP transport addresses that the + * Transport : peer is bound to. This information is derived + * Address : from the INIT or INIT ACK and is used to + * List : associate an inbound packet with a given + * : association. Normally this information is + * : hashed or keyed for quick lookup and access + * : of the TCB. + * + * It is a list of SCTP_transport's. + */ + struct list_head transport_addr_list; + + /* port + * The transport layer port number. + */ + __u16 port; + + /* primary_path + * + * Primary : This is the current primary destination + * Path : transport address of the peer endpoint. It + * : may also specify a source transport address + * : on this endpoint. + * + * All of these paths live on transport_addr_list. + * + * At the bakeoffs, we discovered that the intent of + * primaryPath is that it only changes when the ULP + * asks to have it changed. We add the activePath to + * designate the connection we are currently using to + * transmit new data and most control chunks. + */ + struct sctp_transport *primary_path; + + /* Cache the primary path address here, when we + * need a an address for msg_name. + */ + union sctp_addr primary_addr; + + /* active_path + * The path that we are currently using to + * transmit new data and most control chunks. + */ + struct sctp_transport *active_path; + + /* retran_path + * + * RFC2960 6.4 Multi-homed SCTP Endpoints + * ... + * Furthermore, when its peer is multi-homed, an + * endpoint SHOULD try to retransmit a chunk to an + * active destination transport address that is + * different from the last destination address to + * which the DATA chunk was sent. + */ + struct sctp_transport *retran_path; + + /* Pointer to last transport I have sent on. */ + struct sctp_transport *last_sent_to; + + /* This is the last transport I have received DATA on. */ + struct sctp_transport *last_data_from; + + /* + * Mapping An array of bits or bytes indicating which out of + * Array order TSN's have been received (relative to the + * Last Rcvd TSN). If no gaps exist, i.e. no out of + * order packets have been received, this array + * will be set to all zero. This structure may be + * in the form of a circular buffer or bit array. + * + * Last Rcvd : This is the last TSN received in + * TSN : sequence. This value is set initially by + * : taking the peer's Initial TSN, received in + * : the INIT or INIT ACK chunk, and subtracting + * : one from it. + * + * Throughout most of the specification this is called the + * "Cumulative TSN ACK Point". In this case, we + * ignore the advice in 12.2 in favour of the term + * used in the bulk of the text. This value is hidden + * in tsn_map--we get it by calling sctp_tsnmap_get_ctsn(). + */ + struct sctp_tsnmap tsn_map; + __u8 _map[sctp_tsnmap_storage_size(SCTP_TSN_MAP_SIZE)]; + + /* Do we need to sack the peer? */ + __u8 sack_needed; + /* These are capabilities which our peer advertised. */ + __u8 ecn_capable; /* Can peer do ECN? */ + __u8 ipv4_address; /* Peer understands IPv4 addresses? */ + __u8 ipv6_address; /* Peer understands IPv6 addresses? */ + __u8 hostname_address;/* Peer understands DNS addresses? */ + sctp_inithdr_t i; + int cookie_len; + void *cookie; + + /* ADDIP Extention (ADDIP) --xguo */ + /* minus 1 (ADDIP sec. 4.2 C1) */ + __u32 addip_serial; + } peer; + + /* State : A state variable indicating what state the + * : association is in, i.e. COOKIE-WAIT, + * : COOKIE-ECHOED, ESTABLISHED, SHUTDOWN-PENDING, + * : SHUTDOWN-SENT, SHUTDOWN-RECEIVED, SHUTDOWN-ACK-SENT. + * + * Note: No "CLOSED" state is illustrated since if a + * association is "CLOSED" its TCB SHOULD be removed. + * + * In this implementation we DO have a CLOSED + * state which is used during initiation and shutdown. + * + * State takes values from SCTP_STATE_*. + */ + sctp_state_t state; + + /* When did we enter this state? */ + int state_timestamp; + + /* The cookie life I award for any cookie. */ + struct timeval cookie_life; + + /* Overall : The overall association error count. + * Error Count : [Clear this any time I get something.] + */ + int overall_error_count; + + /* Overall : The threshold for this association that if + * Error : the Overall Error Count reaches will cause + * Threshold : this association to be torn down. + */ + int overall_error_threshold; + + /* These are the association's initial, max, and min RTO values. + * These values will be initialized by system defaults, but can + * be modified via the SCTP_RTOINFO socket option. + */ + __u32 rto_initial; + __u32 rto_max; + __u32 rto_min; + + /* Maximum number of new data packets that can be sent in a burst. */ + int max_burst; + + /* This is the max_retrans value for the association. This value will + * be initialized initialized from system defaults, but can be + * modified by the SCTP_ASSOCINFO socket option. + */ + int max_retrans; + + /* Maximum number of times the endpoint will retransmit INIT */ + __u16 max_init_attempts; + + /* How many times have we resent an INIT? */ + __u16 init_retries; + + /* The largest timeout or RTO value to use in attempting an INIT */ + __u16 max_init_timeo; + + + int timeouts[SCTP_NUM_TIMEOUT_TYPES]; + struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES]; + + /* Transport to which SHUTDOWN chunk was last sent. */ + struct sctp_transport *shutdown_last_sent_to; + + /* Next TSN : The next TSN number to be assigned to a new + * : DATA chunk. This is sent in the INIT or INIT + * : ACK chunk to the peer and incremented each + * : time a DATA chunk is assigned a TSN + * : (normally just prior to transmit or during + * : fragmentation). + */ + __u32 next_tsn; + + /* + * Last Rcvd : This is the last TSN received in sequence. This value + * TSN : is set initially by taking the peer's Initial TSN, + * : received in the INIT or INIT ACK chunk, and + * : subtracting one from it. + * + * Most of RFC 2960 refers to this as the Cumulative TSN Ack Point. + */ + + __u32 ctsn_ack_point; + + /* Highest TSN that is acknowledged by incoming SACKs. */ + __u32 highest_sacked; + + /* The number of unacknowledged data chunks. Reported through + * the SCTP_STATUS sockopt. + */ + __u16 unack_data; + + /* This is the association's receive buffer space. This value is used + * to set a_rwnd field in an INIT or a SACK chunk. + */ + __u32 rwnd; + + /* This is the last advertised value of rwnd over a SACK chunk. */ + __u32 a_rwnd; + + /* Number of bytes by which the rwnd has slopped. The rwnd is allowed + * to slop over a maximum of the association's frag_point. + */ + __u32 rwnd_over; + + /* This is the sndbuf size in use for the association. + * This corresponds to the sndbuf size for the association, + * as specified in the sk->sndbuf. + */ + int sndbuf_used; + + /* This is the wait queue head for send requests waiting on + * the association sndbuf space. + */ + wait_queue_head_t wait; + + /* Association : The smallest PMTU discovered for all of the + * PMTU : peer's transport addresses. + */ + __u32 pmtu; + + /* The message size at which SCTP fragmentation will occur. */ + __u32 frag_point; + + /* Ack State : This flag indicates if the next received + * : packet is to be responded to with a + * : SACK. This is initializedto 0. When a packet + * : is received it is incremented. If this value + * : reaches 2 or more, a SACK is sent and the + * : value is reset to 0. Note: This is used only + * : when no DATA chunks are received out of + * : order. When DATA chunks are out of order, + * : SACK's are not delayed (see Section 6). + */ + /* Do we need to send an ack? + * When counters[SctpCounterAckState] is above 1 we do! + */ + int counters[SCTP_NUMBER_COUNTERS]; + + struct { + __u16 stream; + __u16 flags; + __u32 ppid; + __u32 context; + __u32 timetolive; + } defaults; + + /* This tracks outbound ssn for a given stream. */ + struct sctp_ssnmap *ssnmap; + + /* All outbound chunks go through this structure. */ + struct sctp_outq outqueue; + + /* A smart pipe that will handle reordering and fragmentation, + * as well as handle passing events up to the ULP. + */ + struct sctp_ulpq ulpq; + + /* Need to send an ECNE Chunk? */ + int need_ecne; + + /* Last TSN that caused an ECNE Chunk to be sent. */ + __u32 last_ecne_tsn; + + /* Last TSN that caused a CWR Chunk to be sent. */ + __u32 last_cwr_tsn; + + /* How many duplicated TSNs have we seen? */ + int numduptsns; + + /* Number of seconds of idle time before an association is closed. */ + __u32 autoclose; + + /* Name for debugging output... */ + char *debug_name; + + /* These are to support + * "SCTP Extensions for Dynamic Reconfiguration of IP Addresses + * and Enforcement of Flow and Message Limits" + * + * or "ADDIP" for short. + */ + + /* Is the ADDIP extension enabled for this association? */ + int addip_enable; + + /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks + * + * R1) One and only one ASCONF Chunk MAY be in transit and + * unacknowledged at any one time. If a sender, after sending + * an ASCONF chunk, decides it needs to transfer another + * ASCONF Chunk, it MUST wait until the ASCONF-ACK Chunk + * returns from the previous ASCONF Chunk before sending a + * subsequent ASCONF. Note this restriction binds each side, + * so at any time two ASCONF may be in-transit on any given + * association (one sent from each endpoint). + * + * [This is our one-and-only-one ASCONF in flight. If we do + * not have an ASCONF in flight, this is NULL.] + */ + sctp_chunk_t *addip_last_asconf; + + /* ADDIP Section 4.2 Upon reception of an ASCONF Chunk. + * + * IMPLEMENTATION NOTE: As an optimization a receiver may wish + * to save the last ASCONF-ACK for some predetermined period + * of time and instead of re-processing the ASCONF (with the + * same serial number) it may just re-transmit the + * ASCONF-ACK. It may wish to use the arrival of a new serial + * number to discard the previously saved ASCONF-ACK or any + * other means it may choose to expire the saved ASCONF-ACK. + * + * [This is our saved ASCONF-ACK. We invalidate it when a new + * ASCONF serial number arrives.] + */ + sctp_chunk_t *addip_last_asconf_ack; + + /* These ASCONF chunks are waiting to be sent. + * + * These chunaks can't be pushed to outqueue until receiving + * ASCONF_ACK for the previous ASCONF indicated by + * addip_last_asconf, so as to guarantee that only one ASCONF + * is in flight at any time. + * + * ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks + * + * In defining the ASCONF Chunk transfer procedures, it is + * essential that these transfers MUST NOT cause congestion + * within the network. To achieve this, we place these + * restrictions on the transfer of ASCONF Chunks: + * + * R1) One and only one ASCONF Chunk MAY be in transit and + * unacknowledged at any one time. If a sender, after sending + * an ASCONF chunk, decides it needs to transfer another + * ASCONF Chunk, it MUST wait until the ASCONF-ACK Chunk + * returns from the previous ASCONF Chunk before sending a + * subsequent ASCONF. Note this restriction binds each side, + * so at any time two ASCONF may be in-transit on any given + * association (one sent from each endpoint). + * + * + * [I really think this is EXACTLY the sort of intelligence + * which already resides in sctp_outq. Please move this + * queue and its supporting logic down there. --piggy] + */ + struct sk_buff_head addip_chunks; + + /* ADDIP Section 4.1 ASCONF Chunk Procedures + * + * A2) A serial number should be assigned to the Chunk. The + * serial number should be a monotonically increasing + * number. All serial numbers are defined to be initialized at + * the start of the association to the same value as the + * Initial TSN. + * + * [and] + * + * ADDIP + * 3.1.1 Address/Stream Configuration Change Chunk (ASCONF) + * + * Serial Number : 32 bits (unsigned integer) + * + * This value represents a Serial Number for the ASCONF + * Chunk. The valid range of Serial Number is from 0 to + * 4294967295 (2**32 - 1). Serial Numbers wrap back to 0 + * after reaching 4294967295. + */ + __u32 addip_serial; + + /* Is it a temporary association? */ + __u8 temp; +}; + + +/* An eyecatcher for determining if we are really looking at an + * association data structure. + */ +enum { + SCTP_ASSOC_EYECATCHER = 0xa550c123, +}; + +/* Recover the outter association structure. */ +static inline struct sctp_association *sctp_assoc(struct sctp_ep_common *base) +{ + struct sctp_association *asoc; + + asoc = container_of(base, struct sctp_association, base); + return asoc; +} + +/* These are function signatures for manipulating associations. */ + + +struct sctp_association * +sctp_association_new(const struct sctp_endpoint *, const struct sock *, + sctp_scope_t scope, int gfp); +struct sctp_association * +sctp_association_init(struct sctp_association *, const struct sctp_endpoint *, + const struct sock *, sctp_scope_t scope, + int gfp); +void sctp_association_free(struct sctp_association *); +void sctp_association_put(struct sctp_association *); +void sctp_association_hold(struct sctp_association *); + +struct sctp_transport *sctp_assoc_choose_shutdown_transport( + struct sctp_association *); +void sctp_assoc_update_retran_path(struct sctp_association *); +struct sctp_transport *sctp_assoc_lookup_paddr(const struct sctp_association *, + const union sctp_addr *); +struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *, + const union sctp_addr *address, + const int gfp); +void sctp_assoc_control_transport(struct sctp_association *, + struct sctp_transport *, + sctp_transport_cmd_t, sctp_sn_error_t); +struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *, __u32); +struct sctp_transport *sctp_assoc_is_match(struct sctp_association *, + const union sctp_addr *, + const union sctp_addr *); +void sctp_assoc_migrate(struct sctp_association *, struct sock *); +void sctp_assoc_update(struct sctp_association *old, + struct sctp_association *new); + +__u32 sctp_association_get_next_tsn(struct sctp_association *); +__u32 sctp_association_get_tsn_block(struct sctp_association *, int); + +void sctp_assoc_sync_pmtu(struct sctp_association *); +void sctp_assoc_rwnd_increase(struct sctp_association *, int); +void sctp_assoc_rwnd_decrease(struct sctp_association *, int); +void sctp_assoc_set_primary(struct sctp_association *, + struct sctp_transport *); +int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *, int); +int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, + sctp_cookie_t *, int gfp); + +int sctp_cmp_addr_exact(const union sctp_addr *ss1, + const union sctp_addr *ss2); +sctp_chunk_t *sctp_get_ecne_prepend(struct sctp_association *asoc); +sctp_chunk_t *sctp_get_no_prepend(struct sctp_association *asoc); + +/* A convenience structure to parse out SCTP specific CMSGs. */ +typedef struct sctp_cmsgs { + struct sctp_initmsg *init; + struct sctp_sndrcvinfo *info; +} sctp_cmsgs_t; + +/* Structure for tracking memory objects */ +typedef struct { + char *label; + atomic_t *counter; +} sctp_dbg_objcnt_entry_t; + +#endif /* __sctp_structs_h__ */ diff -urN linux-2.4.22-bk23/include/net/sctp/tsnmap.h linux-2.4.22-bk24/include/net/sctp/tsnmap.h --- linux-2.4.22-bk23/include/net/sctp/tsnmap.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/tsnmap.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,186 @@ +/* SCTP kernel reference Implementation Copyright (C) 1999-2001 + * Cisco, Motorola, Intel, and International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These are the definitions needed for the tsnmap type. The tsnmap is used + * to track out of order TSNs received. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * La Monte H.P. Yarroll + * Karl Knutson + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ +#include + +#ifndef __sctp_tsnmap_h__ +#define __sctp_tsnmap_h__ + +/* RFC 2960 12.2 Parameters necessary per association (i.e. the TCB) + * Mapping An array of bits or bytes indicating which out of + * Array order TSN's have been received (relative to the + * Last Rcvd TSN). If no gaps exist, i.e. no out of + * order packets have been received, this array + * will be set to all zero. This structure may be + * in the form of a circular buffer or bit array. + */ +struct sctp_tsnmap { + /* This array counts the number of chunks with each TSN. + * It points at one of the two buffers with which we will + * ping-pong between. + */ + __u8 *tsn_map; + + /* This marks the tsn which overflows the tsn_map, when the + * cumulative ack point reaches this point we know we can switch + * maps (tsn_map and overflow_map swap). + */ + __u32 overflow_tsn; + + /* This is the overflow array for tsn_map. + * It points at one of the other ping-pong buffers. + */ + __u8 *overflow_map; + + /* This is the TSN at tsn_map[0]. */ + __u32 base_tsn; + + /* Last Rcvd : This is the last TSN received in + * TSN : sequence. This value is set initially by + * : taking the peer's Initial TSN, received in + * : the INIT or INIT ACK chunk, and subtracting + * : one from it. + * + * Throughout most of the specification this is called the + * "Cumulative TSN ACK Point". In this case, we + * ignore the advice in 12.2 in favour of the term + * used in the bulk of the text. + */ + __u32 cumulative_tsn_ack_point; + + /* This is the minimum number of TSNs we can track. This corresponds + * to the size of tsn_map. Note: the overflow_map allows us to + * potentially track more than this quantity. + */ + __u16 len; + + /* This is the highest TSN we've marked. */ + __u32 max_tsn_seen; + + /* Data chunks pending receipt. used by SCTP_STATUS sockopt */ + __u16 pending_data; + + /* We record duplicate TSNs here. We clear this after + * every SACK. Store up to SCTP_MAX_DUP_TSNS worth of + * information. + */ + __u32 dup_tsns[SCTP_MAX_DUP_TSNS]; + __u16 num_dup_tsns; + + int malloced; + + __u8 raw_map[0]; +}; + +struct sctp_tsnmap_iter { + __u32 start; +}; + +/* Create a new tsnmap. */ +struct sctp_tsnmap *sctp_tsnmap_new(__u16 len, __u32 init_tsn, int gfp); + +/* Dispose of a tsnmap. */ +void sctp_tsnmap_free(struct sctp_tsnmap *); + +/* This macro assists in creation of external storage for variable length + * internal buffers. We double allocate so the overflow map works. + */ +#define sctp_tsnmap_storage_size(count) (sizeof(__u8) * (count) * 2) + +/* Initialize a block of memory as a tsnmap. */ +struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *, __u16 len, + __u32 initial_tsn); + +/* Test the tracking state of this TSN. + * Returns: + * 0 if the TSN has not yet been seen + * >0 if the TSN has been seen (duplicate) + * <0 if the TSN is invalid (too large to track) + */ +int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn); + +/* Mark this TSN as seen. */ +void sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn); + +/* Retrieve the Cumulative TSN ACK Point. */ +__u32 sctp_tsnmap_get_ctsn(const struct sctp_tsnmap *); + +/* Retrieve the highest TSN we've seen. */ +__u32 sctp_tsnmap_get_max_tsn_seen(const struct sctp_tsnmap *); + +/* How many Duplicate TSNs are stored? */ +static inline __u16 sctp_tsnmap_num_dups(struct sctp_tsnmap *map) +{ + return map->num_dup_tsns; +} + +/* Return pointer to duplicate tsn array as needed by SACK. */ +static inline __u32 *sctp_tsnmap_get_dups(struct sctp_tsnmap *map) +{ + map->num_dup_tsns = 0; + return map->dup_tsns; +} + +/* Mark a duplicate TSN. Note: limit the storage of duplicate TSN + * information. + */ +static inline void sctp_tsnmap_mark_dup(struct sctp_tsnmap *map, __u32 tsn) +{ + if (map->num_dup_tsns < SCTP_MAX_DUP_TSNS) + map->dup_tsns[map->num_dup_tsns++] = htonl(tsn); +} + +/* Renege a TSN that was seen. */ +void sctp_tsnmap_renege(struct sctp_tsnmap *, __u32 tsn); + +/* Is there a gap in the TSN map? */ +int sctp_tsnmap_has_gap(const struct sctp_tsnmap *); + +/* Initialize a gap ack block interator from user-provided memory. */ +void sctp_tsnmap_iter_init(const struct sctp_tsnmap *, + struct sctp_tsnmap_iter *); + +/* Get the next gap ack blocks. We return 0 if there are no more + * gap ack blocks. + */ +int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *, + struct sctp_tsnmap_iter *,__u16 *start, __u16 *end); + +#endif /* __sctp_tsnmap_h__ */ diff -urN linux-2.4.22-bk23/include/net/sctp/ulpevent.h linux-2.4.22-bk24/include/net/sctp/ulpevent.h --- linux-2.4.22-bk23/include/net/sctp/ulpevent.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/ulpevent.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,156 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * These are the definitions needed for the sctp_ulpevent type. The + * sctp_ulpevent type is used to carry information from the state machine + * upwards to the ULP. + * + * This file is part of the SCTP kernel reference Implementation + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * La Monte H.P. Yarroll + * Karl Knutson + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef __sctp_ulpevent_h__ +#define __sctp_ulpevent_h__ + +#include + +/* A structure to carry information to the ULP (e.g. Sockets API) */ +/* Warning: This sits inside an skb.cb[] area. Be very careful of + * growing this structure as it is at the maximum limit now. + */ +struct sctp_ulpevent { + struct sctp_sndrcvinfo sndrcvinfo; + int msg_flags; + int iif; +}; +#define event_asoc sndrcvinfo.sinfo_assoc_id + +/* Retrieve the skb this event sits inside of. */ +static inline struct sk_buff *sctp_event2skb(struct sctp_ulpevent *ev) +{ + return container_of((void *)ev, struct sk_buff, cb); +} + +/* Retrieve & cast the event sitting inside the skb. */ +static inline struct sctp_ulpevent *sctp_skb2event(struct sk_buff *skb) +{ + return (struct sctp_ulpevent *)skb->cb; +} + +struct sctp_ulpevent *sctp_ulpevent_new(int size, int flags, int gfp); +struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *, int flags); +void sctp_ulpevent_free(struct sctp_ulpevent *); +int sctp_ulpevent_is_notification(const struct sctp_ulpevent *); + +struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( + const struct sctp_association *asoc, + __u16 flags, + __u16 state, + __u16 error, + __u16 outbound, + __u16 inbound, + int gfp); + +struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change( + const struct sctp_association *asoc, + const struct sockaddr_storage *aaddr, + int flags, + int state, + int error, + int gfp); + +struct sctp_ulpevent *sctp_ulpevent_make_remote_error( + const struct sctp_association *asoc, + struct sctp_chunk *chunk, + __u16 flags, + int gfp); +struct sctp_ulpevent *sctp_ulpevent_make_send_failed( + const struct sctp_association *asoc, + struct sctp_chunk *chunk, + __u16 flags, + __u32 error, + int gfp); + +struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event( + const struct sctp_association *asoc, + __u16 flags, + int gfp); + +struct sctp_ulpevent *sctp_ulpevent_make_pdapi( + const struct sctp_association *asoc, + __u32 indication, int gfp); + +struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc, + struct sctp_chunk *chunk, + int gfp); + +void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, + struct msghdr *); +__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event); + +/* Is this event type enabled? */ +static inline int sctp_ulpevent_type_enabled(__u16 sn_type, + struct sctp_event_subscribe *mask) +{ + char *amask = (char *) mask; + return amask[sn_type - SCTP_SN_TYPE_BASE]; +} + +/* Given an event subscription, is this event enabled? */ +static inline int sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event, + struct sctp_event_subscribe *mask) +{ + __u16 sn_type; + int enabled = 1; + + if (sctp_ulpevent_is_notification(event)) { + sn_type = sctp_ulpevent_get_notification_type(event); + enabled = sctp_ulpevent_type_enabled(sn_type, mask); + } + return enabled; +} + +#endif /* __sctp_ulpevent_h__ */ + + + + + + + diff -urN linux-2.4.22-bk23/include/net/sctp/ulpqueue.h linux-2.4.22-bk24/include/net/sctp/ulpqueue.h --- linux-2.4.22-bk23/include/net/sctp/ulpqueue.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/ulpqueue.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,89 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * These are the definitions needed for the sctp_ulpq type. The + * sctp_ulpq is the interface between the Upper Layer Protocol, or ULP, + * and the core SCTP state machine. This is the component which handles + * reassembly and ordering. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * the SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email addresses: + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * La Monte H.P. Yarroll + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef __sctp_ulpqueue_h__ +#define __sctp_ulpqueue_h__ + +/* A structure to carry information to the ULP (e.g. Sockets API) */ +struct sctp_ulpq { + char malloced; + char pd_mode; + struct sctp_association *asoc; + struct sk_buff_head reasm; + struct sk_buff_head lobby; +}; + +/* Prototypes. */ +struct sctp_ulpq *sctp_ulpq_new(struct sctp_association *asoc, int gfp); +struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *, + struct sctp_association *); +void sctp_ulpq_free(struct sctp_ulpq *); + +/* Add a new DATA chunk for processing. */ +int sctp_ulpq_tail_data(struct sctp_ulpq *, struct sctp_chunk *, int); + +/* Add a new event for propagation to the ULP. */ +int sctp_ulpq_tail_event(struct sctp_ulpq *, struct sctp_ulpevent *ev); + +/* Renege previously received chunks. */ +void sctp_ulpq_renege(struct sctp_ulpq *, struct sctp_chunk *, int); + +/* Perform partial delivery. */ +void sctp_ulpq_partial_delivery(struct sctp_ulpq *, struct sctp_chunk *, int); + +/* Abort the partial delivery. */ +void sctp_ulpq_abort_pd(struct sctp_ulpq *, int); + +/* Clear the partial data delivery condition on this socket. */ +int sctp_clear_pd(struct sock *sk); + +#endif /* __sctp_ulpqueue_h__ */ + + + + + + + diff -urN linux-2.4.22-bk23/include/net/sctp/user.h linux-2.4.22-bk24/include/net/sctp/user.h --- linux-2.4.22-bk23/include/net/sctp/user.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/include/net/sctp/user.h 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,628 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * This header represents the structures and constants needed to support + * the SCTP Extension to the Sockets API. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * R. Stewart + * K. Morneau + * Q. Xie + * Karl Knutson + * Jon Grimm + * Daisy Chang + * + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ +#include +#include + +#ifndef __net_sctp_user_h__ +#define __net_sctp_user_h__ + +struct sctp_association; +typedef struct sctp_association * sctp_assoc_t; + +/* The following symbols come from the Sockets API Extensions for + * SCTP . + */ +enum sctp_optname { + SCTP_RTOINFO, +#define SCTP_RTOINFO SCTP_RTOINFO + SCTP_ASSOCRTXINFO, +#define SCTP_ASSOCRTXINFO SCTP_ASSOCRTXINFO + SCTP_INITMSG, +#define SCTP_INITMSG SCTP_INITMSG + SCTP_AUTO_CLOSE, +#define SCTP_AUTO_CLOSE SCTP_AUTO_CLOSE + SCTP_SET_PRIMARY_ADDR, +#define SCTP_SET_PRIMARY_ADDR SCTP_SET_PRIMARY_ADDR + SCTP_SET_PEER_PRIMARY_ADDR, +#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR + SCTP_SET_ADAPTATION_LAYER, +#define SCTP_SET_ADAPTATION_LAYER SCTP_SET_ADAPTATION_LAYER + SCTP_SET_STREAM_TIMEOUTS, +#define SCTP_SET_STREAM_TIMEOUTS SCTP_SET_STREAM_TIMEOUTS + SCTP_DISABLE_FRAGMENTS, +#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS + SCTP_SET_PEER_ADDR_PARAMS, +#define SCTP_SET_PEER_ADDR_PARAMS SCTP_SET_PEER_ADDR_PARAMS + SCTP_GET_PEER_ADDR_PARAMS, +#define SCTP_GET_PEER_ADDR_PARAMS SCTP_GET_PEER_ADDR_PARAMS + SCTP_STATUS, +#define SCTP_STATUS SCTP_STATUS + SCTP_GET_PEER_ADDR_INFO, +#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO + SCTP_SET_EVENTS, +#define SCTP_SET_EVENTS SCTP_SET_EVENTS + SCTP_AUTOCLOSE, +#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE + SCTP_SET_DEFAULT_SEND_PARAM, +#define SCTP_SET_DEFAULT_SEND_PARAM SCTP_SET_DEFAULT_SEND_PARAM + + SCTP_SOCKOPT_DEBUG_NAME = 42, /* FIXME */ +#define SCTP_SOCKOPT_DEBUG_NAME SCTP_SOCKOPT_DEBUG_NAME + + SCTP_SOCKOPT_BINDX_ADD, /* BINDX requests for adding addresses. */ +#define SCTP_SOCKOPT_BINDX_ADD SCTP_SOCKOPT_BINDX_ADD + SCTP_SOCKOPT_BINDX_REM, /* BINDX requests for removing addresses. */ +#define SCTP_SOCKOPT_BINDX_REM SCTP_SOCKOPT_BINDX_REM + SCTP_SOCKOPT_PEELOFF, /* peel off association. */ +#define SCTP_SOCKOPT_PEELOFF SCTP_SOCKOPT_PEELOFF + SCTP_GET_PEER_ADDRS_NUM, /* Get number of peer addresss. */ +#define SCTP_GET_PEER_ADDRS_NUM SCTP_GET_PEER_ADDRS_NUM + SCTP_GET_PEER_ADDRS, /* Get all peer addresss. */ +#define SCTP_GET_PEER_ADDRS SCTP_GET_PEER_ADDRS + SCTP_GET_LOCAL_ADDRS_NUM, /* Get number of local addresss. */ +#define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM + SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */ +#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS + SCTP_NODELAY, /* Get/set nodelay option. */ +#define SCTP_NODELAY SCTP_NODELAY +}; + + +/* + * 5.2 SCTP msg_control Structures + * + * A key element of all SCTP-specific socket extensions is the use of + * ancillary data to specify and access SCTP-specific data via the + * struct msghdr's msg_control member used in sendmsg() and recvmsg(). + * Fine-grained control over initialization and sending parameters are + * handled with ancillary data. + * + * Each ancillary data item is preceeded by a struct cmsghdr (see + * Section 5.1), which defines the function and purpose of the data + * contained in in the cmsg_data[] member. + */ + +/* + * 5.2.1 SCTP Initiation Structure (SCTP_INIT) + * + * This cmsghdr structure provides information for initializing new + * SCTP associations with sendmsg(). The SCTP_INITMSG socket option + * uses this same data structure. This structure is not used for + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg + * + */ +struct sctp_initmsg { + __u16 sinit_num_ostreams; + __u16 sinit_max_instreams; + __u16 sinit_max_attempts; + __u16 sinit_max_init_timeo; +}; + + +/* + * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * This cmsghdr structure specifies SCTP options for sendmsg() and + * describes SCTP header information about a received message through + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo + * + */ +struct sctp_sndrcvinfo { + __u16 sinfo_stream; + __u16 sinfo_ssn; + __u16 sinfo_flags; + __u32 sinfo_ppid; + __u32 sinfo_context; + __u32 sinfo_timetolive; + __u32 sinfo_tsn; + __u32 sinfo_cumtsn; + sctp_assoc_t sinfo_assoc_id; +}; + +/* + * sinfo_flags: 16 bits (unsigned integer) + * + * This field may contain any of the following flags and is composed of + * a bitwise OR of these values. + */ + +enum sctp_sinfo_flags { + MSG_UNORDERED = 1, /* Send/receive message unordered. */ + MSG_ADDR_OVER = 2, /* Override the primary destination. */ + MSG_ABORT=4, /* Send an ABORT message to the peer. */ + /* MSG_EOF is already defined per socket.h */ +}; + + +typedef union { + __u8 raw; + struct sctp_initmsg init; + struct sctp_sndrcvinfo sndrcv; +} sctp_cmsg_data_t; + +/* These are cmsg_types. */ +typedef enum sctp_cmsg_type { + SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ + SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ +} sctp_cmsg_t; + + +/* + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * Communication notifications inform the ULP that an SCTP association + * has either begun or ended. The identifier for a new association is + * provided by this notificaion. The notification information has the + * following format: + * + */ + +struct sctp_assoc_change { + __u16 sac_type; + __u16 sac_flags; + __u32 sac_length; + __u16 sac_state; + __u16 sac_error; + __u16 sac_outbound_streams; + __u16 sac_inbound_streams; + sctp_assoc_t sac_assoc_id; +}; + +/* + * sac_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the association. They include: + * + * Note: The following state names deviate from the API draft as + * the names clash too easily with other kernel symbols. + */ +enum sctp_sac_state { + SCTP_COMM_UP, + SCTP_COMM_LOST, + SCTP_RESTART, + SCTP_SHUTDOWN_COMP, + SCTP_CANT_STR_ASSOC, +}; + +/* + * 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * When a destination address on a multi-homed peer encounters a change + * an interface details event is sent. The information has the + * following structure: + */ +struct sctp_paddr_change { + __u16 spc_type; + __u16 spc_flags; + __u32 spc_length; + struct sockaddr_storage spc_aaddr; + int spc_state; + int spc_error; + sctp_assoc_t spc_assoc_id; +}; + +/* + * spc_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the address. They include: + */ +enum sctp_spc_state { + ADDRESS_AVAILABLE, + ADDRESS_UNREACHABLE, + ADDRESS_REMOVED, + ADDRESS_ADDED, + ADDRESS_MADE_PRIM, +}; + + +/* + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * A remote peer may send an Operational Error message to its peer. + * This message indicates a variety of error conditions on an + * association. The entire error TLV as it appears on the wire is + * included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP + * specification [SCTP] and any extensions for a list of possible + * error formats. SCTP error TLVs have the format: + */ +struct sctp_remote_error { + __u16 sre_type; + __u16 sre_flags; + __u32 sre_length; + __u16 sre_error; + __u16 sre_len; + sctp_assoc_t sre_assoc_id; + __u8 sre_data[0]; +}; + + +/* + * 5.3.1.4 SCTP_SEND_FAILED + * + * If SCTP cannot deliver a message it may return the message as a + * notification. + */ +struct sctp_send_failed { + __u16 ssf_type; + __u16 ssf_flags; + __u32 ssf_length; + __u32 ssf_error; + struct sctp_sndrcvinfo ssf_info; + sctp_assoc_t ssf_assoc_id; + __u8 ssf_data[0]; +}; + +/* + * ssf_flags: 16 bits (unsigned integer) + * + * The flag value will take one of the following values + * + * SCTP_DATA_UNSENT - Indicates that the data was never put on + * the wire. + * + * SCTP_DATA_SENT - Indicates that the data was put on the wire. + * Note that this does not necessarily mean that the + * data was (or was not) successfully delivered. + */ + +enum sctp_ssf_flags { + SCTP_DATA_UNSENT, + SCTP_DATA_SENT, +}; + +/* + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * When a peer sends a SHUTDOWN, SCTP delivers this notification to + * inform the application that it should cease sending data. + */ + +struct sctp_shutdown_event { + __u16 sse_type; + __u16 sse_flags; + __u32 sse_length; + sctp_assoc_t sse_assoc_id; +}; + +/* + * 5.3.1.6 SCTP_ADAPTION_INDICATION + * + * When a peer sends a Adaption Layer Indication parameter , SCTP + * delivers this notification to inform the application + * that of the peers requested adaption layer. + */ +struct sctp_adaption_event { + __u16 sai_type; + __u16 sai_flags; + __u32 sai_length; + __u32 sai_adaptation_bits; + sctp_assoc_t sse_assoc_id; +}; + +/* + * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT + * + * When a reciever is engaged in a partial delivery of a + * message this notification will be used to inidicate + * various events. + */ + +struct sctp_rcv_pdapi_event { + __u16 pdapi_type; + __u16 pdapi_flags; + __u32 pdapi_length; + __u32 pdapi_indication; + sctp_assoc_t pdapi_assoc_id; +}; + +enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, }; + +/* + * Described in Section 7.3 + * Ancillary Data and Notification Interest Options + */ +struct sctp_event_subscribe { + __u8 sctp_data_io_event; + __u8 sctp_association_event; + __u8 sctp_address_event; + __u8 sctp_send_failure_event; + __u8 sctp_peer_error_event; + __u8 sctp_shutdown_event; + __u8 sctp_partial_delivery_event; + __u8 sctp_adaption_layer_event; +}; + +/* + * 5.3.1 SCTP Notification Structure + * + * The notification structure is defined as the union of all + * notification types. + * + */ +union sctp_notification { + struct { + __u16 sn_type; /* Notification type. */ + __u16 sn_flags; + __u32 sn_length; + } h; + struct sctp_assoc_change sn_assoc_change; + struct sctp_paddr_change sn_padr_change; + struct sctp_remote_error sn_remote_error; + struct sctp_send_failed sn_send_failed; + struct sctp_shutdown_event sn_shutdown_event; + struct sctp_adaption_event sn_adaption_event; + struct sctp_rcv_pdapi_event sn_rcv_pdapi_event; +}; + +/* Section 5.3.1 + * All standard values for sn_type flags are greater than 2^15. + * Values from 2^15 and down are reserved. + */ + +enum sctp_sn_type { + SCTP_SN_TYPE_BASE = (1<<15), + SCTP_ASSOC_CHANGE, + SCTP_PEER_ADDR_CHANGE, + SCTP_SEND_FAILED, + SCTP_REMOTE_ERROR, + SCTP_SHUTDOWN_EVENT, + SCTP_PARTIAL_DELIVERY_EVENT, + SCTP_ADAPTION_INDICATION, +}; + +/* Notification error codes used to fill up the error fields in some + * notifications. + * SCTP_PEER_ADDRESS_CHAGE : spc_error + * SCTP_ASSOC_CHANGE : sac_error + * These names should be potentially included in the draft 04 of the SCTP + * sockets API specification. + */ +typedef enum sctp_sn_error { + SCTP_FAILED_THRESHOLD, + SCTP_RECEIVED_SACK, + SCTP_HEARTBEAT_SUCCESS, + SCTP_RESPONSE_TO_USER_REQ, + SCTP_INTERNAL_ERROR, + SCTP_SHUTDOWN_GUARD_EXPIRES, + SCTP_PEER_FAULTY, +} sctp_sn_error_t; + +/* + * + * 7.1.14 Peer Address Parameters + * + * Applications can enable or disable heartbeats for any peer address + * of an association, modify an address's heartbeat interval, force a + * heartbeat to be sent immediately, and adjust the address's maximum + * number of retransmissions sent before an address is considered + * unreachable. The following structure is used to access and modify an + * address's parameters: + */ + +struct sctp_paddrparams { + struct sockaddr_storage spp_address; + __u32 spp_hbinterval; + __u16 spp_pathmaxrxt; + sctp_assoc_t spp_assoc_id; +}; + +/* + * 7.2.2 Peer Address Information + * + * Applications can retrieve information about a specific peer address + * of an association, including its reachability state, congestion + * window, and retransmission timer values. This information is + * read-only. The following structure is used to access this + * information: + */ + +struct sctp_paddrinfo { + sctp_assoc_t spinfo_assoc_id; + struct sockaddr_storage spinfo_address; + __s32 spinfo_state; + __u32 spinfo_cwnd; + __u32 spinfo_srtt; + __u32 spinfo_rto; + __u32 spinfo_mtu; +}; + + +/* + * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO) + * + * The protocol parameters used to initialize and bound retransmission + * timeout (RTO) are tunable. See [SCTP] for more information on how + * these parameters are used in RTO calculation. The peer address + * parameter is ignored for TCP style socket. + */ + +struct sctp_rtoinfo { + __u32 srto_initial; + __u32 srto_max; + __u32 srto_min; + sctp_assoc_t srto_assoc_id; +}; + +/* + * 7.1.2 Association Retransmission Parameter (SCTP_ASSOCRTXINFO) + * + * The protocol parameter used to set the number of retransmissions + * sent before an association is considered unreachable. + * See [SCTP] for more information on how this parameter is used. The + * peer address parameter is ignored for TCP style socket. + */ + +struct sctp_assocparams { + __u16 sasoc_asocmaxrxt; + sctp_assoc_t sasoc_assoc_id; +}; + + +/* + * 7.1.9 Set Primary Address (SCTP_SET_PRIMARY_ADDR) + * + * Requests that the peer mark the enclosed address as the association + * primary. The enclosed address must be one of the association's + * locally bound addresses. The following structure is used to make a + * set primary request: + */ + +struct sctp_setprim { + struct sockaddr_storage ssp_addr; + sctp_assoc_t ssp_assoc_id; +}; + +/* + * 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. The following structure is used to + * make a set peer primary request: + */ + +struct sctp_setpeerprim { + struct sockaddr_storage sspp_addr; + sctp_assoc_t sspp_assoc_id; +}; + +/* + * 7.2.1 Association Status (SCTP_STATUS) + * + * Applications can retrieve current status information about an + * association, including association state, peer receiver window size, + * number of unacked data chunks, and number of data chunks pending + * receipt. This information is read-only. The following structure is + * used to access this information: + */ +struct sctp_status { + sctp_assoc_t sstat_assoc_id; + __s32 sstat_state; + __u32 sstat_rwnd; + __u16 sstat_unackdata; + __u16 sstat_penddata; + __u16 sstat_instrms; + __u16 sstat_outstrms; + __u32 sstat_fragmentation_point; + struct sctp_paddrinfo sstat_primary; +}; + + +/* + * 7.1.12 Set Adaption Layer Indicator + * + * Requests that the local endpoint set the specified Adaption Layer + * Indication parameter for all future + * INIT and INIT-ACK exchanges. + */ + +struct sctp_setadaption { + __u32 ssb_adaption_ind; +}; + +/* + * 7.1.12 Set default message time outs (SCTP_SET_STREAM_TIMEOUTS) + * + * This option requests that the requested stream apply a + * default time-out for messages in queue. + */ +struct sctp_setstrm_timeout { + sctp_assoc_t ssto_assoc_id; + __u32 ssto_timeout; + __u16 ssto_streamid_start; + __u16 ssto_streamid_end; +}; + +/* + * 8.3 8.5 get all peer/local addresses on a socket + * This parameter struct is for getsockopt + */ +struct sctp_getaddrs { + sctp_assoc_t assoc_id; + int addr_num; + struct sockaddr_storage *addrs; +}; + +/* These are bit fields for msghdr->msg_flags. See section 5.1. */ +/* On user space Linux, these live in as an enum. */ +enum sctp_msg_flags { + MSG_NOTIFICATION = 0x8000, +#define MSG_NOTIFICATION MSG_NOTIFICATION +}; + +/* + * 8.1 sctp_bindx() + * + * The flags parameter is formed from the bitwise OR of zero or more of the + * following currently defined flags: + */ +#define BINDX_ADD_ADDR 0x01 +#define BINDX_REM_ADDR 0x02 + +/* This is the structure that is passed as an argument(optval) to + * getsockopt(SCTP_SOCKOPT_PEELOFF). + */ +typedef struct { + sctp_assoc_t associd; + int sd; +} sctp_peeloff_arg_t; + +#endif /* __net_sctp_user_h__ */ + + + diff -urN linux-2.4.22-bk23/include/net/snmp.h linux-2.4.22-bk24/include/net/snmp.h --- linux-2.4.22-bk23/include/net/snmp.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/net/snmp.h 2003-09-23 03:07:18.000000000 -0700 @@ -212,6 +212,35 @@ unsigned long __pad[0]; } ____cacheline_aligned; +/* draft-ietf-sigtran-sctp-mib-07.txt */ +struct sctp_mib +{ + unsigned long SctpCurrEstab; + unsigned long SctpActiveEstabs; + unsigned long SctpPassiveEstabs; + unsigned long SctpAborteds; + unsigned long SctpShutdowns; + unsigned long SctpOutOfBlues; + unsigned long SctpChecksumErrors; + unsigned long SctpOutCtrlChunks; + unsigned long SctpOutOrderChunks; + unsigned long SctpOutUnorderChunks; + unsigned long SctpInCtrlChunks; + unsigned long SctpInOrderChunks; + unsigned long SctpInUnorderChunks; + unsigned long SctpFragUsrMsgs; + unsigned long SctpReasmUsrMsgs; + unsigned long SctpOutSCTPPacks; + unsigned long SctpInSCTPPacks; + unsigned long SctpRtoAlgorithm; + unsigned long SctpRtoMin; + unsigned long SctpRtoMax; + unsigned long SctpRtoInitial; + unsigned long SctpValCookieLife; + unsigned long SctpMaxInitRetr; + unsigned long __pad[0]; +}; + struct linux_mib { unsigned long SyncookiesSent; diff -urN linux-2.4.22-bk23/include/net/sock.h linux-2.4.22-bk24/include/net/sock.h --- linux-2.4.22-bk23/include/net/sock.h 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.22-bk24/include/net/sock.h 2003-09-23 03:07:18.000000000 -0700 @@ -49,6 +49,9 @@ #include #endif #include /* struct tcphdr */ +#if defined(CONFIG_IP_SCTP) || defined (CONFIG_IP_SCTP_MODULE) +#include /* struct sctp_opt */ +#endif #include #include /* struct sk_buff */ @@ -586,6 +589,9 @@ union { struct tcp_opt af_tcp; +#if defined(CONFIG_IP_SCTP) || defined (CONFIG_IP_SCTP_MODULE) + struct sctp_opt af_sctp; +#endif #if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE) struct raw_opt tp_raw4; #endif diff -urN linux-2.4.22-bk23/include/net/tcp.h linux-2.4.22-bk24/include/net/tcp.h --- linux-2.4.22-bk23/include/net/tcp.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/include/net/tcp.h 2003-09-23 03:07:18.000000000 -0700 @@ -555,7 +555,8 @@ */ struct tcp_func { - int (*queue_xmit) (struct sk_buff *skb); + int (*queue_xmit) (struct sk_buff *skb, + int ipfragok); void (*send_check) (struct sock *sk, struct tcphdr *th, diff -urN linux-2.4.22-bk23/kernel/ksyms.c linux-2.4.22-bk24/kernel/ksyms.c --- linux-2.4.22-bk23/kernel/ksyms.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/kernel/ksyms.c 2003-09-23 03:07:18.000000000 -0700 @@ -513,6 +513,9 @@ EXPORT_SYMBOL(seq_release); EXPORT_SYMBOL(seq_read); EXPORT_SYMBOL(seq_lseek); +EXPORT_SYMBOL(single_open); +EXPORT_SYMBOL(single_release); +EXPORT_SYMBOL(seq_release_private); /* Program loader interfaces */ EXPORT_SYMBOL(setup_arg_pages); diff -urN linux-2.4.22-bk23/net/Config.in linux-2.4.22-bk24/net/Config.in --- linux-2.4.22-bk23/net/Config.in 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/Config.in 2003-09-23 03:07:18.000000000 -0700 @@ -29,6 +29,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then source net/khttpd/Config.in fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + source net/sctp/Config.in + fi fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM diff -urN linux-2.4.22-bk23/net/Makefile linux-2.4.22-bk24/net/Makefile --- linux-2.4.22-bk23/net/Makefile 2003-09-23 03:06:58.000000000 -0700 +++ linux-2.4.22-bk24/net/Makefile 2003-09-23 03:07:18.000000000 -0700 @@ -7,7 +7,7 @@ O_TARGET := network.o -mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core +mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp export-objs := netsyms.o subdir-y := core ethernet @@ -19,6 +19,7 @@ subdir-$(CONFIG_NETFILTER) += ipv4/netfilter subdir-$(CONFIG_UNIX) += unix subdir-$(CONFIG_IPV6) += ipv6 +subdir-$(CONFIG_IP_SCTP) += sctp ifneq ($(CONFIG_IPV6),n) ifneq ($(CONFIG_IPV6),) diff -urN linux-2.4.22-bk23/net/atm/br2684.c linux-2.4.22-bk24/net/atm/br2684.c --- linux-2.4.22-bk23/net/atm/br2684.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/br2684.c 2003-09-23 03:07:18.000000000 -0700 @@ -16,9 +16,12 @@ #include #include #include +#include +#include #include +#include "common.h" #include "ipcommon.h" /* @@ -768,8 +771,6 @@ extern struct proc_dir_entry *atm_proc_root; /* from proc.c */ -extern int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); - /* the following avoids some spurious warnings from the compiler */ #define UNUSED __attribute__((unused)) @@ -779,14 +780,14 @@ if ((p = create_proc_entry("br2684", 0, atm_proc_root)) == NULL) return -ENOMEM; p->proc_fops = &br2684_proc_operations; - br2684_ioctl_hook = br2684_ioctl; + br2684_ioctl_set(br2684_ioctl); return 0; } static void __exit UNUSED br2684_exit(void) { struct br2684_dev *brdev; - br2684_ioctl_hook = NULL; + br2684_ioctl_set(NULL); remove_proc_entry("br2684", atm_proc_root); while (!list_empty(&br2684_devs)) { brdev = list_entry_brdev(br2684_devs.next); diff -urN linux-2.4.22-bk23/net/atm/clip.c linux-2.4.22-bk24/net/atm/clip.c --- linux-2.4.22-bk23/net/atm/clip.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/clip.c 2003-09-23 03:07:18.000000000 -0700 @@ -93,6 +93,7 @@ printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n",clip_vcc); return; } + spin_lock_bh(&entry->neigh->dev->xmit_lock); /* block clip_start_xmit() */ entry->neigh->used = jiffies; for (walk = &entry->vccs; *walk; walk = &(*walk)->next) if (*walk == clip_vcc) { @@ -102,17 +103,20 @@ clip_vcc->entry = NULL; if (clip_vcc->xoff) netif_wake_queue(entry->neigh->dev); - if (entry->vccs) return; + if (entry->vccs) + goto out; entry->expires = jiffies-1; /* force resolution or expiration */ error = neigh_update(entry->neigh,NULL,NUD_NONE,0,0); if (error) printk(KERN_CRIT "unlink_clip_vcc: " "neigh_update failed with %d\n",error); - return; + goto out; } printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc " "0x%p)\n",entry,clip_vcc); +out: + spin_unlock_bh(&entry->neigh->dev->xmit_lock); } diff -urN linux-2.4.22-bk23/net/atm/common.c linux-2.4.22-bk24/net/atm/common.c --- linux-2.4.22-bk23/net/atm/common.c 2003-09-23 03:06:58.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/common.c 2003-09-23 03:07:18.000000000 -0700 @@ -130,15 +130,35 @@ #endif #if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE) -int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); -EXPORT_SYMBOL(pppoatm_ioctl_hook); +static DECLARE_MUTEX(pppoatm_ioctl_mutex); + +static int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); + +void pppoatm_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long)) +{ + down(&pppoatm_ioctl_mutex); + pppoatm_ioctl_hook = hook; + up(&pppoatm_ioctl_mutex); +} +#ifdef CONFIG_PPPOATM_MODULE +EXPORT_SYMBOL(pppoatm_ioctl_set); +#endif #endif #if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE) -int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); -#endif +static DECLARE_MUTEX(br2684_ioctl_mutex); + +static int (*br2684_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); + +void br2684_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long)) +{ + down(&br2684_ioctl_mutex); + br2684_ioctl_hook = hook; + up(&br2684_ioctl_mutex); +} #ifdef CONFIG_ATM_BR2684_MODULE -EXPORT_SYMBOL(br2684_ioctl_hook); +EXPORT_SYMBOL(br2684_ioctl_set); +#endif #endif #include "resources.h" /* atm_find_dev */ @@ -564,129 +584,51 @@ } -static void copy_aal_stats(struct k_atm_aal_stats *from, - struct atm_aal_stats *to) -{ -#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) - __AAL_STAT_ITEMS -#undef __HANDLE_ITEM -} - - -static void subtract_aal_stats(struct k_atm_aal_stats *from, - struct atm_aal_stats *to) -{ -#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i) - __AAL_STAT_ITEMS -#undef __HANDLE_ITEM -} - - -static int fetch_stats(struct atm_dev *dev,struct atm_dev_stats *arg,int zero) -{ - struct atm_dev_stats tmp; - int error = 0; - - copy_aal_stats(&dev->stats.aal0,&tmp.aal0); - copy_aal_stats(&dev->stats.aal34,&tmp.aal34); - copy_aal_stats(&dev->stats.aal5,&tmp.aal5); - if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp)); - if (zero && !error) { - subtract_aal_stats(&dev->stats.aal0,&tmp.aal0); - subtract_aal_stats(&dev->stats.aal34,&tmp.aal34); - subtract_aal_stats(&dev->stats.aal5,&tmp.aal5); - } - return error ? -EFAULT : 0; -} - - -int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg) +int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { - struct atm_dev *dev; - struct list_head *p; struct atm_vcc *vcc; - int *tmp_buf, *tmp_p; - void *buf; - int error,len,size,number, ret_val; + int error; - ret_val = 0; vcc = ATM_SD(sock); switch (cmd) { case SIOCOUTQ: if (sock->state != SS_CONNECTED || - !test_bit(ATM_VF_READY,&vcc->flags)) { - ret_val = -EINVAL; + !test_bit(ATM_VF_READY, &vcc->flags)) { + error = -EINVAL; goto done; } - ret_val = put_user(vcc->sk->sndbuf- - atomic_read(&vcc->sk->wmem_alloc), - (int *) arg) ? -EFAULT : 0; + error = put_user(vcc->sk->sndbuf- + atomic_read(&vcc->sk->wmem_alloc), + (int *) arg) ? -EFAULT : 0; goto done; case SIOCINQ: { struct sk_buff *skb; if (sock->state != SS_CONNECTED) { - ret_val = -EINVAL; + error = -EINVAL; goto done; } skb = skb_peek(&vcc->sk->receive_queue); - ret_val = put_user(skb ? skb->len : 0,(int *) arg) - ? -EFAULT : 0; - goto done; - } - case ATM_GETNAMES: - if (get_user(buf, - &((struct atm_iobuf *) arg)->buffer)) { - ret_val = -EFAULT; - goto done; - } - if (get_user(len, - &((struct atm_iobuf *) arg)->length)) { - ret_val = -EFAULT; + error = put_user(skb ? skb->len : 0, + (int *) arg) ? -EFAULT : 0; goto done; } - size = 0; - spin_lock(&atm_dev_lock); - list_for_each(p, &atm_devs) - size += sizeof(int); - if (size > len) { - spin_unlock(&atm_dev_lock); - ret_val = -E2BIG; - goto done; - } - tmp_buf = kmalloc(size, GFP_ATOMIC); - if (!tmp_buf) { - spin_unlock(&atm_dev_lock); - ret_val = -ENOMEM; - goto done; - } - tmp_p = tmp_buf; - list_for_each(p, &atm_devs) { - dev = list_entry(p, struct atm_dev, dev_list); - *tmp_p++ = dev->number; - } - spin_unlock(&atm_dev_lock); - ret_val = ((copy_to_user(buf, tmp_buf, size)) || - put_user(size, &((struct atm_iobuf *) arg)->length) - ) ? -EFAULT : 0; - kfree(tmp_buf); - goto done; case SIOCGSTAMP: /* borrowed from IP */ if (!vcc->sk->stamp.tv_sec) { - ret_val = -ENOENT; + error = -ENOENT; goto done; } - ret_val = copy_to_user((void *) arg, &vcc->sk->stamp, - sizeof(struct timeval)) ? -EFAULT : 0; + error = copy_to_user((void *) arg, &vcc->sk->stamp, + sizeof(struct timeval)) ? -EFAULT : 0; goto done; case ATM_SETSC: printk(KERN_WARNING "ATM_SETSC is obsolete\n"); - ret_val = 0; + error = 0; goto done; case ATMSIGD_CTRL: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } /* @@ -697,29 +639,29 @@ * have the same privledges that /proc/kcore needs */ if (!capable(CAP_SYS_RAWIO)) { - ret_val = -EPERM; + error = -EPERM; goto done; } error = sigd_attach(vcc); - if (!error) sock->state = SS_CONNECTED; - ret_val = error; + if (!error) + sock->state = SS_CONNECTED; goto done; #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE) case SIOCMKCLIP: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_clip_ops()) { - ret_val = atm_clip_ops->clip_create(arg); + error = atm_clip_ops->clip_create(arg); if (atm_clip_ops->owner) __MOD_DEC_USE_COUNT(atm_clip_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMARPD_CTRL: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } #if defined(CONFIG_ATM_CLIP_MODULE) @@ -732,51 +674,50 @@ __MOD_DEC_USE_COUNT(atm_clip_ops->owner); if (!error) sock->state = SS_CONNECTED; - ret_val = error; } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMARP_MKIP: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_clip_ops()) { - ret_val = atm_clip_ops->clip_mkip(vcc, arg); + error = atm_clip_ops->clip_mkip(vcc, arg); if (atm_clip_ops->owner) __MOD_DEC_USE_COUNT(atm_clip_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMARP_SETENTRY: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_clip_ops()) { - ret_val = atm_clip_ops->clip_setentry(vcc, arg); + error = atm_clip_ops->clip_setentry(vcc, arg); if (atm_clip_ops->owner) __MOD_DEC_USE_COUNT(atm_clip_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMARP_ENCAP: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_clip_ops()) { - ret_val = atm_clip_ops->clip_encap(vcc, arg); + error = atm_clip_ops->clip_encap(vcc, arg); if (atm_clip_ops->owner) __MOD_DEC_USE_COUNT(atm_clip_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; #endif #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) case ATMLEC_CTRL: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } #if defined(CONFIG_ATM_LANE_MODULE) @@ -789,39 +730,38 @@ __MOD_DEC_USE_COUNT(atm_lane_ops->owner); if (error >= 0) sock->state = SS_CONNECTED; - ret_val = error; } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMLEC_MCAST: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_lane_ops()) { - ret_val = atm_lane_ops->mcast_attach(vcc, (int) arg); + error = atm_lane_ops->mcast_attach(vcc, (int) arg); if (atm_lane_ops->owner) __MOD_DEC_USE_COUNT(atm_lane_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMLEC_DATA: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_lane_ops()) { - ret_val = atm_lane_ops->vcc_attach(vcc, (void *) arg); + error = atm_lane_ops->vcc_attach(vcc, (void *) arg); if (atm_lane_ops->owner) __MOD_DEC_USE_COUNT(atm_lane_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; #endif #if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE) case ATMMPC_CTRL: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } #if defined(CONFIG_ATM_MPOA_MODULE) @@ -834,248 +774,90 @@ __MOD_DEC_USE_COUNT(atm_mpoa_ops->owner); if (error >= 0) sock->state = SS_CONNECTED; - ret_val = error; } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; case ATMMPC_DATA: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (try_atm_mpoa_ops()) { - ret_val = atm_mpoa_ops->vcc_attach(vcc, arg); + error = atm_mpoa_ops->vcc_attach(vcc, arg); if (atm_mpoa_ops->owner) __MOD_DEC_USE_COUNT(atm_mpoa_ops->owner); } else - ret_val = -ENOSYS; + error = -ENOSYS; goto done; #endif #if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE) case SIOCSIFATMTCP: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (!atm_tcp_ops.attach) { - ret_val = -ENOPKG; + error = -ENOPKG; goto done; } - fops_get (&atm_tcp_ops); - error = atm_tcp_ops.attach(vcc,(int) arg); - if (error >= 0) sock->state = SS_CONNECTED; - else fops_put (&atm_tcp_ops); - ret_val = error; + fops_get(&atm_tcp_ops); + error = atm_tcp_ops.attach(vcc, (int) arg); + if (error >= 0) + sock->state = SS_CONNECTED; + else + fops_put(&atm_tcp_ops); goto done; case ATMTCP_CREATE: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (!atm_tcp_ops.create_persistent) { - ret_val = -ENOPKG; + error = -ENOPKG; goto done; } error = atm_tcp_ops.create_persistent((int) arg); - if (error < 0) fops_put (&atm_tcp_ops); - ret_val = error; + if (error < 0) + fops_put(&atm_tcp_ops); goto done; case ATMTCP_REMOVE: if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; + error = -EPERM; goto done; } if (!atm_tcp_ops.remove_persistent) { - ret_val = -ENOPKG; + error = -ENOPKG; goto done; } error = atm_tcp_ops.remove_persistent((int) arg); - fops_put (&atm_tcp_ops); - ret_val = error; + fops_put(&atm_tcp_ops); goto done; #endif default: break; } + error = -ENOIOCTLCMD; #if defined(CONFIG_PPPOATM) || defined(CONFIG_PPPOATM_MODULE) - if (pppoatm_ioctl_hook) { - ret_val = pppoatm_ioctl_hook(vcc, cmd, arg); - if (ret_val != -ENOIOCTLCMD) - goto done; - } + down(&pppoatm_ioctl_mutex); + if (pppoatm_ioctl_hook) + error = pppoatm_ioctl_hook(vcc, cmd, arg); + up(&pppoatm_ioctl_mutex); + if (error != -ENOIOCTLCMD) + goto done; #endif #if defined(CONFIG_ATM_BR2684) || defined(CONFIG_ATM_BR2684_MODULE) - if (br2684_ioctl_hook) { - ret_val = br2684_ioctl_hook(vcc, cmd, arg); - if (ret_val != -ENOIOCTLCMD) - goto done; - } -#endif - - if (get_user(buf,&((struct atmif_sioc *) arg)->arg)) { - ret_val = -EFAULT; - goto done; - } - if (get_user(len,&((struct atmif_sioc *) arg)->length)) { - ret_val = -EFAULT; - goto done; - } - if (get_user(number,&((struct atmif_sioc *) arg)->number)) { - ret_val = -EFAULT; - goto done; - } - if (!(dev = atm_dev_lookup(number))) { - ret_val = -ENODEV; + down(&br2684_ioctl_mutex); + if (br2684_ioctl_hook) + error = br2684_ioctl_hook(vcc, cmd, arg); + up(&br2684_ioctl_mutex); + if (error != -ENOIOCTLCMD) goto done; - } - - size = 0; - switch (cmd) { - case ATM_GETTYPE: - size = strlen(dev->type)+1; - if (copy_to_user(buf,dev->type,size)) { - ret_val = -EFAULT; - goto done_release; - } - break; - case ATM_GETESI: - size = ESI_LEN; - if (copy_to_user(buf,dev->esi,size)) { - ret_val = -EFAULT; - goto done_release; - } - break; - case ATM_SETESI: - { - int i; - - for (i = 0; i < ESI_LEN; i++) - if (dev->esi[i]) { - ret_val = -EEXIST; - goto done_release; - } - } - /* fall through */ - case ATM_SETESIF: - { - unsigned char esi[ESI_LEN]; - - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - goto done_release; - } - if (copy_from_user(esi,buf,ESI_LEN)) { - ret_val = -EFAULT; - goto done_release; - } - memcpy(dev->esi,esi,ESI_LEN); - ret_val = ESI_LEN; - goto done_release; - } - case ATM_GETSTATZ: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - goto done_release; - } - /* fall through */ - case ATM_GETSTAT: - size = sizeof(struct atm_dev_stats); - error = fetch_stats(dev,buf,cmd == ATM_GETSTATZ); - if (error) { - ret_val = error; - goto done_release; - } - break; - case ATM_GETCIRANGE: - size = sizeof(struct atm_cirange); - if (copy_to_user(buf,&dev->ci_range,size)) { - ret_val = -EFAULT; - goto done_release; - } - break; - case ATM_GETLINKRATE: - size = sizeof(int); - if (copy_to_user(buf,&dev->link_rate,size)) { - ret_val = -EFAULT; - goto done_release; - } - break; - case ATM_RSTADDR: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - goto done_release; - } - atm_reset_addr(dev); - break; - case ATM_ADDADDR: - case ATM_DELADDR: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - goto done_release; - } - { - struct sockaddr_atmsvc addr; +#endif - if (copy_from_user(&addr,buf,sizeof(addr))) { - ret_val = -EFAULT; - goto done_release; - } - if (cmd == ATM_ADDADDR) - ret_val = atm_add_addr(dev,&addr); - else - ret_val = atm_del_addr(dev,&addr); - goto done_release; - } - case ATM_GETADDR: - size = atm_get_addr(dev,buf,len); - if (size < 0) - ret_val = size; - else - /* may return 0, but later on size == 0 means "don't - write the length" */ - ret_val = put_user(size, - &((struct atmif_sioc *) arg)->length) ? -EFAULT : 0; - goto done_release; - case ATM_SETLOOP: - if (__ATM_LM_XTRMT((int) (long) buf) && - __ATM_LM_XTLOC((int) (long) buf) > - __ATM_LM_XTRMT((int) (long) buf)) { - ret_val = -EINVAL; - goto done_release; - } - /* fall through */ - case ATM_SETCIRANGE: - case SONET_GETSTATZ: - case SONET_SETDIAG: - case SONET_CLRDIAG: - case SONET_SETFRAMING: - if (!capable(CAP_NET_ADMIN)) { - ret_val = -EPERM; - goto done_release; - } - /* fall through */ - default: - if (!dev->ops->ioctl) { - ret_val = -EINVAL; - goto done_release; - } - size = dev->ops->ioctl(dev,cmd,buf); - if (size < 0) { - ret_val = (size == -ENOIOCTLCMD ? -EINVAL : size); - goto done_release; - } - } - - if (size) - ret_val = put_user(size,&((struct atmif_sioc *) arg)->length) ? - -EFAULT : 0; - else - ret_val = 0; -done_release: - atm_dev_release(dev); + error = atm_dev_ioctl(cmd, arg); done: - return ret_val; + return error; } diff -urN linux-2.4.22-bk23/net/atm/common.h linux-2.4.22-bk24/net/atm/common.h --- linux-2.4.22-bk23/net/atm/common.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/common.h 2003-09-23 03:07:18.000000000 -0700 @@ -18,7 +18,7 @@ int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len, struct scm_cookie *scm); unsigned int atm_poll(struct file *file,struct socket *sock,poll_table *wait); -int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg); +int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int atm_setsockopt(struct socket *sock,int level,int optname,char *optval, int optlen); int atm_getsockopt(struct socket *sock,int level,int optname,char *optval, @@ -28,6 +28,9 @@ void atm_release_vcc_sk(struct sock *sk,int free_sk); void atm_shutdown_dev(struct atm_dev *dev); +void pppoatm_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long)); +void br2684_ioctl_set(int (*hook)(struct atm_vcc *, unsigned int, unsigned long)); + int atmpvc_init(void); void atmpvc_exit(void); int atmsvc_init(void); diff -urN linux-2.4.22-bk23/net/atm/pppoatm.c linux-2.4.22-bk24/net/atm/pppoatm.c --- linux-2.4.22-bk23/net/atm/pppoatm.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/pppoatm.c 2003-09-23 03:07:18.000000000 -0700 @@ -44,6 +44,8 @@ #include #include +#include "common.h" + #if 0 #define DPRINTK(format, args...) \ printk(KERN_DEBUG "pppoatm: " format, ##args) @@ -344,17 +346,15 @@ /* the following avoids some spurious warnings from the compiler */ #define UNUSED __attribute__((unused)) -extern int (*pppoatm_ioctl_hook)(struct atm_vcc *, unsigned int, unsigned long); - static int __init UNUSED pppoatm_init(void) { - pppoatm_ioctl_hook = pppoatm_ioctl; + pppoatm_ioctl_set(pppoatm_ioctl); return 0; } static void __exit UNUSED pppoatm_exit(void) { - pppoatm_ioctl_hook = NULL; + pppoatm_ioctl_set(NULL); } module_init(pppoatm_init); diff -urN linux-2.4.22-bk23/net/atm/pvc.c linux-2.4.22-bk24/net/atm/pvc.c --- linux-2.4.22-bk23/net/atm/pvc.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/pvc.c 2003-09-23 03:07:18.000000000 -0700 @@ -74,24 +74,24 @@ static struct proto_ops SOCKOPS_WRAPPED(pvc_proto_ops) = { - family: PF_ATMPVC, + .family = PF_ATMPVC, - release: atm_release, - bind: pvc_bind, - connect: pvc_connect, - socketpair: sock_no_socketpair, - accept: sock_no_accept, - getname: pvc_getname, - poll: atm_poll, - ioctl: atm_ioctl, - listen: sock_no_listen, - shutdown: pvc_shutdown, - setsockopt: atm_setsockopt, - getsockopt: atm_getsockopt, - sendmsg: atm_sendmsg, - recvmsg: atm_recvmsg, - mmap: sock_no_mmap, - sendpage: sock_no_sendpage, + .release = atm_release, + .bind = pvc_bind, + .connect = pvc_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = pvc_getname, + .poll = atm_poll, + .ioctl = vcc_ioctl, + .listen = sock_no_listen, + .shutdown = pvc_shutdown, + .setsockopt = atm_setsockopt, + .getsockopt = atm_getsockopt, + .sendmsg = atm_sendmsg, + .recvmsg = atm_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, }; diff -urN linux-2.4.22-bk23/net/atm/resources.c linux-2.4.22-bk24/net/atm/resources.c --- linux-2.4.22-bk23/net/atm/resources.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/resources.c 2003-09-23 03:07:18.000000000 -0700 @@ -7,6 +7,7 @@ #include #include #include +#include #include /* for barrier */ #include #include @@ -15,6 +16,7 @@ #include "common.h" #include "resources.h" +#include "addr.h" #ifndef NULL @@ -172,6 +174,238 @@ } +static void copy_aal_stats(struct k_atm_aal_stats *from, + struct atm_aal_stats *to) +{ +#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) + __AAL_STAT_ITEMS +#undef __HANDLE_ITEM +} + + +static void subtract_aal_stats(struct k_atm_aal_stats *from, + struct atm_aal_stats *to) +{ +#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i) + __AAL_STAT_ITEMS +#undef __HANDLE_ITEM +} + + +static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats *arg, int zero) +{ + struct atm_dev_stats tmp; + int error = 0; + + copy_aal_stats(&dev->stats.aal0, &tmp.aal0); + copy_aal_stats(&dev->stats.aal34, &tmp.aal34); + copy_aal_stats(&dev->stats.aal5, &tmp.aal5); + if (arg) + error = copy_to_user(arg, &tmp, sizeof(tmp)); + if (zero && !error) { + subtract_aal_stats(&dev->stats.aal0, &tmp.aal0); + subtract_aal_stats(&dev->stats.aal34, &tmp.aal34); + subtract_aal_stats(&dev->stats.aal5, &tmp.aal5); + } + return error ? -EFAULT : 0; +} + + +int atm_dev_ioctl(unsigned int cmd, unsigned long arg) +{ + void *buf; + int error = 0, len, number, size = 0; + struct atm_dev *dev; + + if (cmd == ATM_GETNAMES) { + int *tmp_buf, *tmp_bufp; + struct list_head *p; + /* + * ATM_GETNAMES is a special case: it doesn't require a + * device number argument + */ + if (get_user(buf, &((struct atm_iobuf *) arg)->buffer)) + return -EFAULT; + if (get_user(len, &((struct atm_iobuf *) arg)->length)) + return -EFAULT; + spin_lock(&atm_dev_lock); + list_for_each(p, &atm_devs) + size += sizeof(int); + if (size > len) { + spin_unlock(&atm_dev_lock); + return -E2BIG; + } + tmp_buf = tmp_bufp = kmalloc(size, GFP_ATOMIC); + if (!tmp_buf) { + spin_unlock(&atm_dev_lock); + return -ENOMEM; + } + list_for_each(p, &atm_devs) { + dev = list_entry(p, struct atm_dev, dev_list); + *tmp_bufp++ = dev->number; + } + spin_unlock(&atm_dev_lock); + error = (copy_to_user(buf, tmp_buf, size) || + put_user(size, &((struct atm_iobuf *) arg)->length)) + ? -EFAULT : 0; + kfree(tmp_buf); + return error; + } + + if (get_user(buf, &((struct atmif_sioc *) arg)->arg)) + return -EFAULT; + if (get_user(len, &((struct atmif_sioc *) arg)->length)) + return -EFAULT; + if (get_user(number, &((struct atmif_sioc *) arg)->number)) + return -EFAULT; + + if (!(dev = atm_dev_lookup(number))) + return -ENODEV; + + switch (cmd) { + case ATM_GETTYPE: + size = strlen(dev->type) + 1; + if (copy_to_user(buf, dev->type, size)) { + error = -EFAULT; + goto done; + } + break; + case ATM_GETESI: + size = ESI_LEN; + if (copy_to_user(buf, dev->esi, size)) { + error = -EFAULT; + goto done; + } + break; + case ATM_SETESI: + { + int i; + + for (i = 0; i < ESI_LEN; i++) + if (dev->esi[i]) { + error = -EEXIST; + goto done; + } + } + /* fall through */ + case ATM_SETESIF: + { + unsigned char esi[ESI_LEN]; + + if (!capable(CAP_NET_ADMIN)) { + error = -EPERM; + goto done; + } + if (copy_from_user(esi, buf, ESI_LEN)) { + error = -EFAULT; + goto done; + } + memcpy(dev->esi, esi, ESI_LEN); + error = ESI_LEN; + goto done; + } + case ATM_GETSTATZ: + if (!capable(CAP_NET_ADMIN)) { + error = -EPERM; + goto done; + } + /* fall through */ + case ATM_GETSTAT: + size = sizeof(struct atm_dev_stats); + error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ); + if (error) + goto done; + break; + case ATM_GETCIRANGE: + size = sizeof(struct atm_cirange); + if (copy_to_user(buf, &dev->ci_range, size)) { + error = -EFAULT; + goto done; + } + break; + case ATM_GETLINKRATE: + size = sizeof(int); + if (copy_to_user(buf, &dev->link_rate, size)) { + error = -EFAULT; + goto done; + } + break; + case ATM_RSTADDR: + if (!capable(CAP_NET_ADMIN)) { + error = -EPERM; + goto done; + } + atm_reset_addr(dev); + break; + case ATM_ADDADDR: + case ATM_DELADDR: + if (!capable(CAP_NET_ADMIN)) { + error = -EPERM; + goto done; + } + { + struct sockaddr_atmsvc addr; + + if (copy_from_user(&addr, buf, sizeof(addr))) { + error = -EFAULT; + goto done; + } + if (cmd == ATM_ADDADDR) + error = atm_add_addr(dev, &addr); + else + error = atm_del_addr(dev, &addr); + goto done; + } + case ATM_GETADDR: + error = atm_get_addr(dev, buf, len); + if (error < 0) + goto done; + size = error; + /* write back size even if it's zero */ + goto write_size; + case ATM_SETLOOP: + if (__ATM_LM_XTRMT((int) (long) buf) && + __ATM_LM_XTLOC((int) (long) buf) > + __ATM_LM_XTRMT((int) (long) buf)) { + error = -EINVAL; + goto done; + } + /* fall through */ + case ATM_SETCIRANGE: + case SONET_GETSTATZ: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + if (!capable(CAP_NET_ADMIN)) { + error = -EPERM; + goto done; + } + /* fall through */ + default: + if (!dev->ops->ioctl) { + error = -EINVAL; + goto done; + } + size = dev->ops->ioctl(dev, cmd, buf); + if (size < 0) { + error = (size == -ENOIOCTLCMD ? -EINVAL : size); + goto done; + } + } + + if (size) { +write_size: + error = put_user(size, + &((struct atmif_sioc *) arg)->length) + ? -EFAULT : 0; + } +done: + atm_dev_release(dev); + return error; +} + + + /* Handler for sk->destruct, invoked by sk_free() */ static void atm_free_sock(struct sock *sk) { diff -urN linux-2.4.22-bk23/net/atm/resources.h linux-2.4.22-bk24/net/atm/resources.h --- linux-2.4.22-bk23/net/atm/resources.h 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/resources.h 2003-09-23 03:07:18.000000000 -0700 @@ -16,6 +16,7 @@ struct sock *alloc_atm_vcc_sk(int family); void free_atm_vcc_sk(struct sock *sk); +int atm_dev_ioctl(unsigned int cmd, unsigned long arg); #ifdef CONFIG_PROC_FS diff -urN linux-2.4.22-bk23/net/atm/svc.c linux-2.4.22-bk24/net/atm/svc.c --- linux-2.4.22-bk23/net/atm/svc.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/atm/svc.c 2003-09-23 03:07:18.000000000 -0700 @@ -392,24 +392,24 @@ static struct proto_ops SOCKOPS_WRAPPED(svc_proto_ops) = { - family: PF_ATMSVC, + .family = PF_ATMSVC, - release: svc_release, - bind: svc_bind, - connect: svc_connect, - socketpair: sock_no_socketpair, - accept: svc_accept, - getname: svc_getname, - poll: atm_poll, - ioctl: atm_ioctl, - listen: svc_listen, - shutdown: svc_shutdown, - setsockopt: svc_setsockopt, - getsockopt: svc_getsockopt, - sendmsg: atm_sendmsg, - recvmsg: atm_recvmsg, - mmap: sock_no_mmap, - sendpage: sock_no_sendpage, + .release = svc_release, + .bind = svc_bind, + .connect = svc_connect, + .socketpair = sock_no_socketpair, + .accept = svc_accept, + .getname = svc_getname, + .poll = atm_poll, + .ioctl = vcc_ioctl, + .listen = svc_listen, + .shutdown = svc_shutdown, + .setsockopt = svc_setsockopt, + .getsockopt = svc_getsockopt, + .sendmsg = atm_sendmsg, + .recvmsg = atm_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, }; diff -urN linux-2.4.22-bk23/net/ipv4/af_inet.c linux-2.4.22-bk24/net/ipv4/af_inet.c --- linux-2.4.22-bk23/net/ipv4/af_inet.c 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv4/af_inet.c 2003-09-23 03:07:18.000000000 -0700 @@ -470,7 +470,7 @@ /* It is off by default, see below. */ int sysctl_ip_nonlocal_bind; -static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in *addr=(struct sockaddr_in *)uaddr; struct sock *sk=sock->sk; @@ -703,7 +703,7 @@ * This does both peername and sockname. */ -static int inet_getname(struct socket *sock, struct sockaddr *uaddr, +int inet_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sock *sk = sock->sk; @@ -820,7 +820,7 @@ * There's a good 20K of config code hanging around the kernel. */ -static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; int err; diff -urN linux-2.4.22-bk23/net/ipv4/igmp.c linux-2.4.22-bk24/net/ipv4/igmp.c --- linux-2.4.22-bk23/net/ipv4/igmp.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv4/igmp.c 2003-09-23 03:07:18.000000000 -0700 @@ -285,8 +285,10 @@ return 0; } skb = alloc_skb(size + dev->hard_header_len + 15, GFP_ATOMIC); - if (skb == NULL) + if (skb == NULL) { + ip_rt_put(rt); return 0; + } skb->dst = &rt->u.dst; skb->dev = dev; diff -urN linux-2.4.22-bk23/net/ipv4/ip_output.c linux-2.4.22-bk24/net/ipv4/ip_output.c --- linux-2.4.22-bk23/net/ipv4/ip_output.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv4/ip_output.c 2003-09-23 03:07:18.000000000 -0700 @@ -339,7 +339,7 @@ return ip_fragment(skb, skb->dst->output); } -int ip_queue_xmit(struct sk_buff *skb) +int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct ip_options *opt = sk->protinfo.af_inet.opt; @@ -384,7 +384,7 @@ iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0)); *((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff)); iph->tot_len = htons(skb->len); - if (ip_dont_fragment(sk, &rt->u.dst)) + if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok) iph->frag_off = htons(IP_DF); else iph->frag_off = 0; diff -urN linux-2.4.22-bk23/net/ipv4/netfilter/ip_nat_snmp_basic.c linux-2.4.22-bk24/net/ipv4/netfilter/ip_nat_snmp_basic.c --- linux-2.4.22-bk23/net/ipv4/netfilter/ip_nat_snmp_basic.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv4/netfilter/ip_nat_snmp_basic.c 2003-09-23 03:07:18.000000000 -0700 @@ -53,9 +53,9 @@ #include #include #include +#include #include #include -#include diff -urN linux-2.4.22-bk23/net/ipv4/netfilter/ipt_REJECT.c linux-2.4.22-bk24/net/ipv4/netfilter/ipt_REJECT.c --- linux-2.4.22-bk23/net/ipv4/netfilter/ipt_REJECT.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv4/netfilter/ipt_REJECT.c 2003-09-23 03:07:18.000000000 -0700 @@ -186,8 +186,8 @@ nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, nskb->nh.iph->ihl); - /* "Never happens" */ - if (nskb->len > nskb->dst->pmtu) + /* dst->pmtu can be zero because it is not set for local dst's */ + if (nskb->dst->pmtu && nskb->len > nskb->dst->pmtu) goto free_nskb; connection_attach(nskb, oldskb->nfct); diff -urN linux-2.4.22-bk23/net/ipv4/tcp_output.c linux-2.4.22-bk24/net/ipv4/tcp_output.c --- linux-2.4.22-bk23/net/ipv4/tcp_output.c 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv4/tcp_output.c 2003-09-23 03:07:18.000000000 -0700 @@ -275,7 +275,7 @@ TCP_INC_STATS(TcpOutSegs); - err = tp->af_specific->queue_xmit(skb); + err = tp->af_specific->queue_xmit(skb, 0); if (err <= 0) return err; diff -urN linux-2.4.22-bk23/net/ipv6/Makefile linux-2.4.22-bk24/net/ipv6/Makefile --- linux-2.4.22-bk23/net/ipv6/Makefile 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv6/Makefile 2003-09-23 03:07:18.000000000 -0700 @@ -13,7 +13,9 @@ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ - ip6_flowlabel.o + ip6_flowlabel.o ipv6_syms.o + +export-objs := ipv6_syms.o obj-m := $(O_TARGET) diff -urN linux-2.4.22-bk23/net/ipv6/addrconf.c linux-2.4.22-bk24/net/ipv6/addrconf.c --- linux-2.4.22-bk23/net/ipv6/addrconf.c 2003-09-23 03:06:58.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv6/addrconf.c 2003-09-23 03:07:18.000000000 -0700 @@ -1981,11 +1981,112 @@ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC); } +static void inline ipv6_store_devconf(struct ipv6_devconf *cnf, + __s32 *array, int bytes) +{ + memset(array, 0, bytes); + array[DEVCONF_FORWARDING] = cnf->forwarding; + array[DEVCONF_HOPLIMIT] = cnf->hop_limit; + array[DEVCONF_MTU6] = cnf->mtu6; + array[DEVCONF_ACCEPT_RA] = cnf->accept_ra; + array[DEVCONF_ACCEPT_REDIRECTS] = cnf->accept_redirects; + array[DEVCONF_AUTOCONF] = cnf->autoconf; + array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits; + array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; + array[DEVCONF_RTR_SOLICIT_INTERVAL] = cnf->rtr_solicit_interval; + array[DEVCONF_RTR_SOLICIT_DELAY] = cnf->rtr_solicit_delay; +#ifdef CONFIG_IPV6_PRIVACY + array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; + array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; + array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft; + array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry; + array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; +#endif +} + +static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, + struct inet6_dev *idev, + int type, u32 pid, u32 seq) +{ + __s32 *array = NULL; + struct ifinfomsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + struct rtattr *subattr; + + nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r)); + if (pid) nlh->nlmsg_flags |= NLM_F_MULTI; + r = NLMSG_DATA(nlh); + r->ifi_family = AF_INET6; + r->ifi_type = dev->type; + r->ifi_index = dev->ifindex; + r->ifi_flags = dev->flags; + r->ifi_change = 0; + if (!netif_running(dev) || !netif_carrier_ok(dev)) + r->ifi_flags &= ~IFF_RUNNING; + else + r->ifi_flags |= IFF_RUNNING; + + RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); + + subattr = (struct rtattr*)skb->tail; + + RTA_PUT(skb, IFLA_PROTINFO, 0, NULL); + + /* return the device flags */ + RTA_PUT(skb, IFLA_INET6_FLAGS, sizeof(__u32), &idev->if_flags); + + /* return the device sysctl params */ + if ((array = kmalloc(DEVCONF_MAX * sizeof(*array), GFP_ATOMIC)) == NULL) + goto rtattr_failure; + ipv6_store_devconf(&idev->cnf, array, DEVCONF_MAX * sizeof(*array)); + RTA_PUT(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(*array), array); + + /* XXX - Statistics/MC not implemented */ + subattr->rta_len = skb->tail - (u8*)subattr; + + nlh->nlmsg_len = skb->tail - b; + kfree(array); + return skb->len; + +nlmsg_failure: +rtattr_failure: + if (array) + kfree(array); + skb_trim(skb, b - skb->data); + return -1; +} + +static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx, err; + int s_idx = cb->args[0]; + struct net_device *dev; + struct inet6_dev *idev; + + read_lock(&dev_base_lock); + for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { + if (idx < s_idx) + continue; + if ((idev = in6_dev_get(dev)) == NULL) + continue; + err = inet6_fill_ifinfo(skb, dev, idev, RTM_NEWLINK, + NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq); + in6_dev_put(idev); + if (err <= 0) + break; + } + read_unlock(&dev_base_lock); + cb->args[0] = idx; + + return skb->len; +} + static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] = { { NULL, NULL, }, { NULL, NULL, }, - { NULL, NULL, }, + { NULL, inet6_dump_ifinfo, }, { NULL, NULL, }, { inet6_rtm_newaddr, NULL, }, diff -urN linux-2.4.22-bk23/net/ipv6/af_inet6.c linux-2.4.22-bk24/net/ipv6/af_inet6.c --- linux-2.4.22-bk23/net/ipv6/af_inet6.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv6/af_inet6.c 2003-09-23 03:07:18.000000000 -0700 @@ -233,7 +233,7 @@ /* bind for INET6 API */ -static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr; struct sock *sk = sock->sk; @@ -329,7 +329,7 @@ return 0; } -static int inet6_release(struct socket *sock) +int inet6_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -376,7 +376,7 @@ * This does both peername and sockname. */ -static int inet6_getname(struct socket *sock, struct sockaddr *uaddr, +int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr; @@ -413,7 +413,7 @@ return(0); } -static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; int err = -EINVAL; diff -urN linux-2.4.22-bk23/net/ipv6/ipv6_syms.c linux-2.4.22-bk24/net/ipv6/ipv6_syms.c --- linux-2.4.22-bk23/net/ipv6/ipv6_syms.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/ipv6/ipv6_syms.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,35 @@ + +#include +#include +#include +#include +#include +#include +#include + +EXPORT_SYMBOL(ipv6_addr_type); +EXPORT_SYMBOL(icmpv6_send); +EXPORT_SYMBOL(icmpv6_statistics); +EXPORT_SYMBOL(icmpv6_err_convert); +EXPORT_SYMBOL(ndisc_mc_map); +EXPORT_SYMBOL(register_inet6addr_notifier); +EXPORT_SYMBOL(unregister_inet6addr_notifier); +EXPORT_SYMBOL(ip6_route_output); +#ifdef CONFIG_NETFILTER +EXPORT_SYMBOL(ip6_route_me_harder); +#endif +EXPORT_SYMBOL(addrconf_lock); +EXPORT_SYMBOL(ipv6_setsockopt); +EXPORT_SYMBOL(ipv6_getsockopt); +EXPORT_SYMBOL(inet6_register_protosw); +EXPORT_SYMBOL(inet6_unregister_protosw); +EXPORT_SYMBOL(inet6_add_protocol); +EXPORT_SYMBOL(inet6_del_protocol); +EXPORT_SYMBOL(ip6_xmit); +EXPORT_SYMBOL(inet6_release); +EXPORT_SYMBOL(inet6_bind); +EXPORT_SYMBOL(inet6_getname); +EXPORT_SYMBOL(inet6_ioctl); +EXPORT_SYMBOL(ipv6_get_saddr); +EXPORT_SYMBOL(ipv6_chk_addr); +EXPORT_SYMBOL(in6_dev_finish_destroy); diff -urN linux-2.4.22-bk23/net/ipv6/ndisc.c linux-2.4.22-bk24/net/ipv6/ndisc.c --- linux-2.4.22-bk23/net/ipv6/ndisc.c 2003-09-23 03:06:58.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv6/ndisc.c 2003-09-23 03:07:18.000000000 -0700 @@ -944,6 +944,17 @@ in6_dev->if_flags |= IF_RA_RCVD; } + /* + * Remember the managed/otherconf flags from most recently + * received RA message (RFC 2462) -- yoshfuji + */ + in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED | + IF_RA_OTHERCONF)) | + (ra_msg->icmph.icmp6_addrconf_managed ? + IF_RA_MANAGED : 0) | + (ra_msg->icmph.icmp6_addrconf_other ? + IF_RA_OTHERCONF : 0); + lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); diff -urN linux-2.4.22-bk23/net/ipv6/tcp_ipv6.c linux-2.4.22-bk24/net/ipv6/tcp_ipv6.c --- linux-2.4.22-bk23/net/ipv6/tcp_ipv6.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/ipv6/tcp_ipv6.c 2003-09-23 03:07:18.000000000 -0700 @@ -59,7 +59,7 @@ struct sk_buff *skb); static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); -static int tcp_v6_xmit(struct sk_buff *skb); +static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok); static struct tcp_func ipv6_mapped; static struct tcp_func ipv6_specific; @@ -1715,7 +1715,7 @@ return 0; } -static int tcp_v6_xmit(struct sk_buff *skb) +static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6; diff -urN linux-2.4.22-bk23/net/netsyms.c linux-2.4.22-bk24/net/netsyms.c --- linux-2.4.22-bk23/net/netsyms.c 2003-09-23 03:06:58.000000000 -0700 +++ linux-2.4.22-bk24/net/netsyms.c 2003-09-23 03:07:18.000000000 -0700 @@ -60,7 +60,7 @@ extern struct net_proto_family inet_family_ops; -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_IP_SCTP_MODULE) #include #include #include @@ -164,6 +164,7 @@ EXPORT_SYMBOL(put_cmsg); EXPORT_SYMBOL(sock_kmalloc); EXPORT_SYMBOL(sock_kfree_s); +EXPORT_SYMBOL(sock_map_fd); EXPORT_SYMBOL(sockfd_lookup); #ifdef CONFIG_FILTER @@ -256,6 +257,8 @@ EXPORT_SYMBOL(ip_route_output_key); EXPORT_SYMBOL(ip_route_input); EXPORT_SYMBOL(icmp_send); +EXPORT_SYMBOL(icmp_statistics); +EXPORT_SYMBOL(icmp_err_convert); EXPORT_SYMBOL(ip_options_compile); EXPORT_SYMBOL(ip_options_undo); EXPORT_SYMBOL(arp_send); @@ -294,19 +297,7 @@ #endif -#ifdef CONFIG_IPV6 -EXPORT_SYMBOL(ipv6_addr_type); -EXPORT_SYMBOL(icmpv6_send); -EXPORT_SYMBOL(ndisc_mc_map); -EXPORT_SYMBOL(register_inet6addr_notifier); -EXPORT_SYMBOL(unregister_inet6addr_notifier); -#include -EXPORT_SYMBOL(ip6_route_output); -#ifdef CONFIG_NETFILTER -EXPORT_SYMBOL(ip6_route_me_harder); -#endif -#endif -#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) +#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_IP_SCTP_MODULE) /* inet functions common to v4 and v6 */ EXPORT_SYMBOL(inet_release); EXPORT_SYMBOL(inet_stream_connect); @@ -422,6 +413,14 @@ EXPORT_SYMBOL(tcp_read_sock); +#ifdef CONFIG_IP_SCTP_MODULE +EXPORT_SYMBOL(ip_setsockopt); +EXPORT_SYMBOL(ip_getsockopt); +EXPORT_SYMBOL(inet_ioctl); +EXPORT_SYMBOL(inet_bind); +EXPORT_SYMBOL(inet_getname); +#endif /* CONFIG_IP_SCTP_MODULE */ + EXPORT_SYMBOL(netlink_set_err); EXPORT_SYMBOL(netlink_broadcast); EXPORT_SYMBOL(netlink_unicast); diff -urN linux-2.4.22-bk23/net/sched/sch_generic.c linux-2.4.22-bk24/net/sched/sch_generic.c --- linux-2.4.22-bk23/net/sched/sch_generic.c 2002-11-28 15:53:16.000000000 -0800 +++ linux-2.4.22-bk24/net/sched/sch_generic.c 2003-09-23 03:07:18.000000000 -0700 @@ -7,7 +7,7 @@ * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, - * Jamal Hadi Salim, 990601 + * Jamal Hadi Salim, 990601 * - Ingress support */ @@ -283,6 +283,8 @@ if (list->qlen <= qdisc->dev->tx_queue_len) { __skb_queue_tail(list, skb); qdisc->q.qlen++; + qdisc->stats.bytes += skb->len; + qdisc->stats.packets++; return 0; } qdisc->stats.drops++; @@ -331,6 +333,21 @@ qdisc->q.qlen = 0; } +static int pfifo_fast_dump(struct Qdisc *qdisc, struct sk_buff *skb) +{ + unsigned char *b = skb->tail; + struct tc_prio_qopt opt; + + opt.bands = 3; + memcpy(&opt.priomap, prio2band, TC_PRIO_MAX+1); + RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); + return skb->len; + +rtattr_failure: + skb_trim(skb, b - skb->data); + return -1; +} + static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt) { int i; @@ -358,6 +375,10 @@ pfifo_fast_init, pfifo_fast_reset, + NULL, + NULL, + pfifo_fast_dump, + }; struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops) @@ -445,6 +466,12 @@ printk(KERN_INFO "%s: activation failed\n", dev->name); return; } + + write_lock(&qdisc_tree_lock); + qdisc->next = dev->qdisc_list; + dev->qdisc_list = qdisc; + write_unlock(&qdisc_tree_lock); + } else { qdisc = &noqueue_qdisc; } diff -urN linux-2.4.22-bk23/net/sctp/Config.in linux-2.4.22-bk24/net/sctp/Config.in --- linux-2.4.22-bk23/net/sctp/Config.in 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/Config.in 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,20 @@ +# +# SCTP configuration +# +mainmenu_option next_comment +comment ' SCTP Configuration (EXPERIMENTAL)' + +if [ "$CONFIG_IPV6" != "n" ]; then + define_bool CONFIG_IPV6_SCTP__ $CONFIG_IPV6 +else + define_bool CONFIG_IPV6_SCTP__ y +fi + +dep_tristate ' The SCTP Protocol (EXPERIMENTAL)' CONFIG_IP_SCTP $CONFIG_IPV6_SCTP__ +if [ "$CONFIG_IP_SCTP" != "n" ]; then + bool ' SCTP: Use old checksum (Adler-32)' CONFIG_SCTP_ADLER32 + bool ' SCTP: Debug messages' CONFIG_SCTP_DBG_MSG + bool ' SCTP: Debug object counts' CONFIG_SCTP_DBG_OBJCNT +fi + +endmenu diff -urN linux-2.4.22-bk23/net/sctp/Makefile linux-2.4.22-bk24/net/sctp/Makefile --- linux-2.4.22-bk23/net/sctp/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/Makefile 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,27 @@ +# +# Makefile for SCTP support code. +# + +O_TARGET := sctp.o + +obj-$(CONFIG_IP_SCTP) += sctp.o + +obj-y := endpointola.o output.o sm_make_chunk.o associola.o hashdriver.o \ + outqueue.o sm_sideeffect.o transport.o bind_addr.o input.o primitive.o \ + sm_statefuns.o tsnmap.o command.o inqueue.o proc.o sm_statetable.o \ + ulpevent.o protocol.o socket.o ulpqueue.o debug.o sla1.o ssnmap.o + +ifeq ($(CONFIG_SCTP_ADLER32), y) +obj-y += adler32.o +else +obj-y += crc32c.o +endif + +obj-$(CONFIG_SCTP_DBG_OBJCNT) += objcnt.o +obj-$(CONFIG_SYSCTL) += sysctl.o + +obj-$(subst m,y,$(CONFIG_IPV6)) += ipv6.o + +sctp-objs := $(obj-y) + +include $(TOPDIR)/Rules.make diff -urN linux-2.4.22-bk23/net/sctp/adler32.c linux-2.4.22-bk24/net/sctp/adler32.c --- linux-2.4.22-bk23/net/sctp/adler32.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/adler32.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,171 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2003 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * This file has direct heritage from the SCTP user-level reference + * implementation by R. Stewart, et al. These functions implement the + * Adler-32 algorithm as specified by RFC 2960. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Randall Stewart + * Ken Morneau + * Qiaobing Xie + * Sridhar Samudrala + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +/* This is an entry point for external calls + * Define this function in the header file. This is + * direct from rfc1950, ... + * + * The following C code computes the Adler-32 checksum of a data buffer. + * It is written for clarity, not for speed. The sample code is in the + * ANSI C programming language. Non C users may find it easier to read + * with these hints: + * + * & Bitwise AND operator. + * >> Bitwise right shift operator. When applied to an + * unsigned quantity, as here, right shift inserts zero bit(s) + * at the left. + * << Bitwise left shift operator. Left shift inserts zero + * bit(s) at the right. + * ++ "n++" increments the variable n. + * % modulo operator: a % b is the remainder of a divided by b. + * + * Well, the above is a bit of a lie, I have optimized this a small + * tad, but I have commented the original lines below + */ + +#include +#include + +#define BASE 65521 /* largest prime smaller than 65536 */ + + +/* Performance work as shown this pig to be the + * worst CPU wise guy. I have done what I could think + * of on my flight to Australia but I am sure some + * clever assembly could speed this up, but of + * course this would require the dreaded #ifdef's for + * architecture. If you can speed this up more, pass + * it back and we will incorporate it :-) + */ + +unsigned long update_adler32(unsigned long adler, + unsigned char *buf, int len) +{ + __u32 s1 = adler & 0xffff; + __u32 s2 = (adler >> 16) & 0xffff; + int n; + + for (n = 0; n < len; n++,buf++) { + /* s1 = (s1 + buf[n]) % BASE */ + /* first we add */ + s1 = (s1 + *buf); + + /* Now if we need to, we do a mod by + * subtracting. It seems a bit faster + * since I really will only ever do + * one subtract at the MOST, since buf[n] + * is a max of 255. + */ + if (s1 >= BASE) + s1 -= BASE; + + /* s2 = (s2 + s1) % BASE */ + /* first we add */ + s2 = (s2 + s1); + + /* again, it is more efficient (it seems) to + * subtract since the most s2 will ever be + * is (BASE-1 + BASE-1) in the worse case. + * This would then be (2 * BASE) - 2, which + * will still only do one subtract. On Intel + * this is much better to do this way and + * avoid the divide. Have not -pg'd on + * sparc. + */ + if (s2 >= BASE) { + /* s2 %= BASE;*/ + s2 -= BASE; + } + } + + /* Return the adler32 of the bytes buf[0..len-1] */ + return (s2 << 16) + s1; +} + +__u32 sctp_start_cksum(__u8 *ptr, __u16 count) +{ + /* + * Update a running Adler-32 checksum with the bytes + * buf[0..len-1] and return the updated checksum. The Adler-32 + * checksum should be initialized to 1. + */ + __u32 adler = 1L; + __u32 zero = 0L; + + /* Calculate the CRC up to the checksum field. */ + adler = update_adler32(adler, ptr, + sizeof(struct sctphdr) - sizeof(__u32)); + /* Skip over the checksum field. */ + adler = update_adler32(adler, (unsigned char *) &zero, + sizeof(__u32)); + ptr += sizeof(struct sctphdr); + count -= sizeof(struct sctphdr); + + /* Calculate the rest of the Adler-32. */ + adler = update_adler32(adler, ptr, count); + + return adler; +} + +__u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 adler) +{ + adler = update_adler32(adler, ptr, count); + + return adler; +} + +__u32 sctp_update_copy_cksum(__u8 *to, __u8 *from, __u16 count, __u32 adler) +{ + /* Its not worth it to try harder. Adler32 is obsolescent. */ + adler = update_adler32(adler, from, count); + memcpy(to, from, count); + + return adler; +} + +__u32 sctp_end_cksum(__u32 adler) +{ + return adler; +} diff -urN linux-2.4.22-bk23/net/sctp/associola.c linux-2.4.22-bk24/net/sctp/associola.c --- linux-2.4.22-bk23/net/sctp/associola.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/associola.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,1106 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * This module provides the abstraction for an SCTP association. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Xingang Guo + * Hui Huang + * Sridhar Samudrala + * Daisy Chang + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Forward declarations for internal functions. */ +static void sctp_assoc_bh_rcv(struct sctp_association *asoc); + + +/* 1st Level Abstractions. */ + +/* Allocate and initialize a new association */ +struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep, + const struct sock *sk, + sctp_scope_t scope, int gfp) +{ + struct sctp_association *asoc; + + asoc = t_new(struct sctp_association, gfp); + if (!asoc) + goto fail; + + if (!sctp_association_init(asoc, ep, sk, scope, gfp)) + goto fail_init; + + asoc->base.malloced = 1; + SCTP_DBG_OBJCNT_INC(assoc); + + return asoc; + +fail_init: + kfree(asoc); +fail: + return NULL; +} + +/* Initialize a new association from provided memory. */ +struct sctp_association *sctp_association_init(struct sctp_association *asoc, + const struct sctp_endpoint *ep, + const struct sock *sk, + sctp_scope_t scope, + int gfp) +{ + struct sctp_opt *sp; + struct sctp_protocol *proto = sctp_get_protocol(); + int i; + + /* Retrieve the SCTP per socket area. */ + sp = sctp_sk((struct sock *)sk); + + /* Init all variables to a known value. */ + memset(asoc, 0, sizeof(struct sctp_association)); + + /* Discarding const is appropriate here. */ + asoc->ep = (struct sctp_endpoint *)ep; + sctp_endpoint_hold(asoc->ep); + + /* Hold the sock. */ + asoc->base.sk = (struct sock *)sk; + sock_hold(asoc->base.sk); + + /* Initialize the common base substructure. */ + asoc->base.type = SCTP_EP_TYPE_ASSOCIATION; + + /* Initialize the object handling fields. */ + atomic_set(&asoc->base.refcnt, 1); + asoc->base.dead = 0; + asoc->base.malloced = 0; + + /* Initialize the bind addr area. */ + sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port); + asoc->base.addr_lock = RW_LOCK_UNLOCKED; + + asoc->state = SCTP_STATE_CLOSED; + asoc->state_timestamp = jiffies; + + /* Set things that have constant value. */ + asoc->cookie_life.tv_sec = sctp_proto.valid_cookie_life / HZ; + asoc->cookie_life.tv_usec = (sctp_proto.valid_cookie_life % HZ) * + 1000000L / HZ; + + asoc->pmtu = 0; + asoc->frag_point = 0; + + /* Initialize the default association max_retrans and RTO values. */ + asoc->max_retrans = proto->max_retrans_association; + asoc->rto_initial = proto->rto_initial; + asoc->rto_max = proto->rto_max; + asoc->rto_min = proto->rto_min; + + asoc->overall_error_threshold = 0; + asoc->overall_error_count = 0; + + /* Initialize the maximum mumber of new data packets that can be sent + * in a burst. + */ + asoc->max_burst = proto->max_burst; + + /* Copy things from the endpoint. */ + for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) { + asoc->timeouts[i] = ep->timeouts[i]; + init_timer(&asoc->timers[i]); + asoc->timers[i].function = sctp_timer_events[i]; + asoc->timers[i].data = (unsigned long) asoc; + } + + /* Pull default initialization values from the sock options. + * Note: This assumes that the values have already been + * validated in the sock. + */ + asoc->c.sinit_max_instreams = sp->initmsg.sinit_max_instreams; + asoc->c.sinit_num_ostreams = sp->initmsg.sinit_num_ostreams; + asoc->max_init_attempts = sp->initmsg.sinit_max_attempts; + asoc->max_init_timeo = sp->initmsg.sinit_max_init_timeo * HZ; + + /* Allocate storage for the ssnmap after the inbound and outbound + * streams have been negotiated during Init. + */ + asoc->ssnmap = NULL; + + /* Set the local window size for receive. + * This is also the rcvbuf space per association. + * RFC 6 - A SCTP receiver MUST be able to receive a minimum of + * 1500 bytes in one SCTP packet. + */ + if (sk->rcvbuf < SCTP_DEFAULT_MINWINDOW) + asoc->rwnd = SCTP_DEFAULT_MINWINDOW; + else + asoc->rwnd = sk->rcvbuf; + + asoc->a_rwnd = asoc->rwnd; + + asoc->rwnd_over = 0; + + /* Use my own max window until I learn something better. */ + asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW; + + /* Set the sndbuf size for transmit. */ + asoc->sndbuf_used = 0; + + init_waitqueue_head(&asoc->wait); + + asoc->c.my_vtag = sctp_generate_tag(ep); + asoc->peer.i.init_tag = 0; /* INIT needs a vtag of 0. */ + asoc->c.peer_vtag = 0; + asoc->c.my_ttag = 0; + asoc->c.peer_ttag = 0; + + asoc->c.initial_tsn = sctp_generate_tsn(ep); + + asoc->next_tsn = asoc->c.initial_tsn; + + asoc->ctsn_ack_point = asoc->next_tsn - 1; + asoc->highest_sacked = asoc->ctsn_ack_point; + asoc->last_cwr_tsn = asoc->ctsn_ack_point; + asoc->unack_data = 0; + + SCTP_DEBUG_PRINTK("myctsnap for %s INIT as 0x%x.\n", + asoc->ep->debug_name, + asoc->ctsn_ack_point); + + /* ADDIP Section 4.1 Asconf Chunk Procedures + * + * When an endpoint has an ASCONF signaled change to be sent to the + * remote endpoint it should do the following: + * ... + * A2) a serial number should be assigned to the chunk. The serial + * number should be a monotonically increasing number. All serial + * numbers are defined to be initialized at the start of the + * association to the same value as the initial TSN. + */ + asoc->addip_serial = asoc->c.initial_tsn; + + /* Make an empty list of remote transport addresses. */ + INIT_LIST_HEAD(&asoc->peer.transport_addr_list); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * After the reception of the first data chunk in an + * association the endpoint must immediately respond with a + * sack to acknowledge the data chunk. Subsequent + * acknowledgements should be done as described in Section + * 6.2. + * + * [We implement this by telling a new association that it + * already received one packet.] + */ + asoc->peer.sack_needed = 1; + + /* Create an input queue. */ + sctp_inq_init(&asoc->base.inqueue); + sctp_inq_set_th_handler(&asoc->base.inqueue, + (void (*)(void *))sctp_assoc_bh_rcv, + asoc); + + /* Create an output queue. */ + sctp_outq_init(asoc, &asoc->outqueue); + sctp_outq_set_output_handlers(&asoc->outqueue, + sctp_packet_init, + sctp_packet_config, + sctp_packet_append_chunk, + sctp_packet_transmit_chunk, + sctp_packet_transmit); + + if (NULL == sctp_ulpq_init(&asoc->ulpq, asoc)) + goto fail_init; + + /* Set up the tsn tracking. */ + sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, 0); + + skb_queue_head_init(&asoc->addip_chunks); + + asoc->need_ecne = 0; + + asoc->debug_name = "unnamedasoc"; + asoc->eyecatcher = SCTP_ASSOC_EYECATCHER; + + /* Assume that peer would support both address types unless we are + * told otherwise. + */ + asoc->peer.ipv4_address = 1; + asoc->peer.ipv6_address = 1; + INIT_LIST_HEAD(&asoc->asocs); + + asoc->autoclose = sp->autoclose; + + return asoc; + +fail_init: + sctp_endpoint_put(asoc->ep); + sock_put(asoc->base.sk); + return NULL; +} + +/* Free this association if possible. There may still be users, so + * the actual deallocation may be delayed. + */ +void sctp_association_free(struct sctp_association *asoc) +{ + struct sock *sk = asoc->base.sk; + struct sctp_transport *transport; + struct list_head *pos, *temp; + int i; + + list_del(&asoc->asocs); + + /* Decrement the backlog value for a TCP-style listening socket. */ + if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) && + (SCTP_SS_LISTENING == sk->state)) + sk->ack_backlog--; + + /* Mark as dead, so other users can know this structure is + * going away. + */ + asoc->base.dead = 1; + + /* Dispose of any data lying around in the outqueue. */ + sctp_outq_free(&asoc->outqueue); + + /* Dispose of any pending messages for the upper layer. */ + sctp_ulpq_free(&asoc->ulpq); + + /* Dispose of any pending chunks on the inqueue. */ + sctp_inq_free(&asoc->base.inqueue); + + /* Free ssnmap storage. */ + sctp_ssnmap_free(asoc->ssnmap); + + /* Clean up the bound address list. */ + sctp_bind_addr_free(&asoc->base.bind_addr); + + /* Do we need to go through all of our timers and + * delete them? To be safe we will try to delete all, but we + * should be able to go through and make a guess based + * on our state. + */ + for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) { + if (timer_pending(&asoc->timers[i]) && + del_timer(&asoc->timers[i])) + sctp_association_put(asoc); + } + + /* Free peer's cached cookie. */ + if (asoc->peer.cookie) { + kfree(asoc->peer.cookie); + } + + /* Release the transport structures. */ + list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + list_del(pos); + sctp_transport_free(transport); + } + + asoc->eyecatcher = 0; + + sctp_association_put(asoc); +} + +/* Cleanup and free up an association. */ +static void sctp_association_destroy(struct sctp_association *asoc) +{ + SCTP_ASSERT(asoc->base.dead, "Assoc is not dead", return); + + sctp_endpoint_put(asoc->ep); + sock_put(asoc->base.sk); + + if (asoc->base.malloced) { + kfree(asoc); + SCTP_DBG_OBJCNT_DEC(assoc); + } +} + +/* Change the primary destination address for the peer. */ +void sctp_assoc_set_primary(struct sctp_association *asoc, + struct sctp_transport *transport) +{ + asoc->peer.primary_path = transport; + + /* Set a default msg_name for events. */ + memcpy(&asoc->peer.primary_addr, &transport->ipaddr, + sizeof(union sctp_addr)); + + /* If the primary path is changing, assume that the + * user wants to use this new path. + */ + if (transport->active) + asoc->peer.active_path = transport; +} + +/* Add a transport address to an association. */ +struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, + const union sctp_addr *addr, + int gfp) +{ + struct sctp_transport *peer; + struct sctp_opt *sp; + unsigned short port; + + /* AF_INET and AF_INET6 share common port field. */ + port = addr->v4.sin_port; + + /* Set the port if it has not been set yet. */ + if (0 == asoc->peer.port) { + asoc->peer.port = port; + } + + /* Check to see if this is a duplicate. */ + peer = sctp_assoc_lookup_paddr(asoc, addr); + if (peer) + return peer; + + peer = sctp_transport_new(addr, gfp); + if (!peer) + return NULL; + + sctp_transport_set_owner(peer, asoc); + + /* Initialize the pmtu of the transport. */ + sctp_transport_pmtu(peer); + + /* If this is the first transport addr on this association, + * initialize the association PMTU to the peer's PMTU. + * If not and the current association PMTU is higher than the new + * peer's PMTU, reset the association PMTU to the new peer's PMTU. + */ + if (asoc->pmtu) + asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu); + else + asoc->pmtu = peer->pmtu; + + SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " + "%d\n", asoc, asoc->pmtu); + + asoc->frag_point = sctp_frag_point(asoc->pmtu); + + /* The asoc->peer.port might not be meaningful yet, but + * initialize the packet structure anyway. + */ + (asoc->outqueue.init_output)(&peer->packet, + peer, + asoc->base.bind_addr.port, + asoc->peer.port); + + /* 7.2.1 Slow-Start + * + * o The initial cwnd before data transmission or after a + * sufficiently long idle period MUST be <= 2*MTU. + * + * o The initial value of ssthresh MAY be arbitrarily high + * (for example, implementations MAY use the size of the + * receiver advertised window). + */ + peer->cwnd = asoc->pmtu * 2; + + /* At this point, we may not have the receiver's advertised window, + * so initialize ssthresh to the default value and it will be set + * later when we process the INIT. + */ + peer->ssthresh = SCTP_DEFAULT_MAXWINDOW; + + peer->partial_bytes_acked = 0; + peer->flight_size = 0; + + peer->error_threshold = peer->max_retrans; + + /* Update the overall error threshold value of the association + * taking the new peer's error threshold into account. + */ + asoc->overall_error_threshold = + min(asoc->overall_error_threshold + peer->error_threshold, + asoc->max_retrans); + + /* By default, enable heartbeat for peer address. */ + peer->hb_allowed = 1; + + /* Initialize the peer's heartbeat interval based on the + * sock configured value. + */ + sp = sctp_sk(asoc->base.sk); + peer->hb_interval = sp->paddrparam.spp_hbinterval * HZ; + + /* Attach the remote transport to our asoc. */ + list_add_tail(&peer->transports, &asoc->peer.transport_addr_list); + + /* If we do not yet have a primary path, set one. */ + if (NULL == asoc->peer.primary_path) { + sctp_assoc_set_primary(asoc, peer); + asoc->peer.retran_path = peer; + } + + if (asoc->peer.active_path == asoc->peer.retran_path) + asoc->peer.retran_path = peer; + + return peer; +} + +/* Lookup a transport by address. */ +struct sctp_transport *sctp_assoc_lookup_paddr( + const struct sctp_association *asoc, + const union sctp_addr *address) +{ + struct sctp_transport *t; + struct list_head *pos; + + /* Cycle through all transports searching for a peer address. */ + + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + if (sctp_cmp_addr_exact(address, &t->ipaddr)) + return t; + } + + return NULL; +} + +/* Engage in transport control operations. + * Mark the transport up or down and send a notification to the user. + * Select and update the new active and retran paths. + */ +void sctp_assoc_control_transport(struct sctp_association *asoc, + struct sctp_transport *transport, + sctp_transport_cmd_t command, + sctp_sn_error_t error) +{ + struct sctp_transport *t = NULL; + struct sctp_transport *first; + struct sctp_transport *second; + struct sctp_ulpevent *event; + struct list_head *pos; + int spc_state = 0; + + /* Record the transition on the transport. */ + switch (command) { + case SCTP_TRANSPORT_UP: + transport->active = 1; + spc_state = ADDRESS_AVAILABLE; + break; + + case SCTP_TRANSPORT_DOWN: + transport->active = 0; + spc_state = ADDRESS_UNREACHABLE; + break; + + default: + return; + }; + + /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the + * user. + */ + event = sctp_ulpevent_make_peer_addr_change(asoc, + (struct sockaddr_storage *) &transport->ipaddr, + 0, spc_state, error, GFP_ATOMIC); + if (event) + sctp_ulpq_tail_event(&asoc->ulpq, event); + + /* Select new active and retran paths. */ + + /* Look for the two most recently used active transports. + * + * This code produces the wrong ordering whenever jiffies + * rolls over, but we still get usable transports, so we don't + * worry about it. + */ + first = NULL; second = NULL; + + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + + if (!t->active) + continue; + if (!first || t->last_time_heard > first->last_time_heard) { + second = first; + first = t; + } + if (!second || t->last_time_heard > second->last_time_heard) + second = t; + } + + /* RFC 2960 6.4 Multi-Homed SCTP Endpoints + * + * By default, an endpoint should always transmit to the + * primary path, unless the SCTP user explicitly specifies the + * destination transport address (and possibly source + * transport address) to use. + * + * [If the primary is active but not most recent, bump the most + * recently used transport.] + */ + if (asoc->peer.primary_path->active && + first != asoc->peer.primary_path) { + second = first; + first = asoc->peer.primary_path; + } + + /* If we failed to find a usable transport, just camp on the + * primary, even if it is inactive. + */ + if (NULL == first) { + first = asoc->peer.primary_path; + second = asoc->peer.primary_path; + } + + /* Set the active and retran transports. */ + asoc->peer.active_path = first; + asoc->peer.retran_path = second; +} + +/* Hold a reference to an association. */ +void sctp_association_hold(struct sctp_association *asoc) +{ + atomic_inc(&asoc->base.refcnt); +} + +/* Release a reference to an association and cleanup + * if there are no more references. + */ +void sctp_association_put(struct sctp_association *asoc) +{ + if (atomic_dec_and_test(&asoc->base.refcnt)) + sctp_association_destroy(asoc); +} + +/* Allocate the next TSN, Transmission Sequence Number, for the given + * association. + */ +__u32 sctp_association_get_next_tsn(struct sctp_association *asoc) +{ + /* From Section 1.6 Serial Number Arithmetic: + * Transmission Sequence Numbers wrap around when they reach + * 2**32 - 1. That is, the next TSN a DATA chunk MUST use + * after transmitting TSN = 2*32 - 1 is TSN = 0. + */ + __u32 retval = asoc->next_tsn; + asoc->next_tsn++; + asoc->unack_data++; + + return retval; +} + +/* Allocate 'num' TSNs by incrementing the association's TSN by num. */ +__u32 sctp_association_get_tsn_block(struct sctp_association *asoc, int num) +{ + __u32 retval = asoc->next_tsn; + + asoc->next_tsn += num; + asoc->unack_data += num; + + return retval; +} + + +/* Compare two addresses to see if they match. Wildcard addresses + * only match themselves. + */ +int sctp_cmp_addr_exact(const union sctp_addr *ss1, + const union sctp_addr *ss2) +{ + struct sctp_af *af; + + af = sctp_get_af_specific(ss1->sa.sa_family); + if (unlikely(!af)) + return 0; + + return af->cmp_addr(ss1, ss2); +} + +/* Return an ecne chunk to get prepended to a packet. + * Note: We are sly and return a shared, prealloced chunk. FIXME: + * No we don't, but we could/should. + */ +sctp_chunk_t *sctp_get_ecne_prepend(struct sctp_association *asoc) +{ + struct sctp_chunk *chunk; + + /* Send ECNE if needed. + * Not being able to allocate a chunk here is not deadly. + */ + if (asoc->need_ecne) + chunk = sctp_make_ecne(asoc, asoc->last_ecne_tsn); + else + chunk = NULL; + + return chunk; +} + +/* Use this function for the packet prepend callback when no ECNE + * packet is desired (e.g. some packets don't like to be bundled). + */ +sctp_chunk_t *sctp_get_no_prepend(struct sctp_association *asoc) +{ + return NULL; +} + +/* + * Find which transport this TSN was sent on. + */ +struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *asoc, __u32 tsn) +{ + struct sctp_transport *active; + struct sctp_transport *match; + struct list_head *entry, *pos; + struct sctp_transport *transport; + sctp_chunk_t *chunk; + __u32 key = htonl(tsn); + + match = NULL; + + /* + * FIXME: In general, find a more efficient data structure for + * searching. + */ + + /* + * The general strategy is to search each transport's transmitted + * list. Return which transport this TSN lives on. + * + * Let's be hopeful and check the active_path first. + * Another optimization would be to know if there is only one + * outbound path and not have to look for the TSN at all. + * + */ + + active = asoc->peer.active_path; + + list_for_each(entry, &active->transmitted) { + chunk = list_entry(entry, sctp_chunk_t, transmitted_list); + + if (key == chunk->subh.data_hdr->tsn) { + match = active; + goto out; + } + } + + /* If not found, go search all the other transports. */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + + if (transport == active) + break; + list_for_each(entry, &transport->transmitted) { + chunk = list_entry(entry, sctp_chunk_t, + transmitted_list); + if (key == chunk->subh.data_hdr->tsn) { + match = transport; + goto out; + } + } + } +out: + return match; +} + +/* Is this the association we are looking for? */ +struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc, + const union sctp_addr *laddr, + const union sctp_addr *paddr) +{ + struct sctp_transport *transport; + + sctp_read_lock(&asoc->base.addr_lock); + + if ((asoc->base.bind_addr.port == laddr->v4.sin_port) && + (asoc->peer.port == paddr->v4.sin_port)) { + transport = sctp_assoc_lookup_paddr(asoc, paddr); + if (!transport) + goto out; + + if (sctp_bind_addr_match(&asoc->base.bind_addr, laddr, + sctp_sk(asoc->base.sk))) + goto out; + } + transport = NULL; + +out: + sctp_read_unlock(&asoc->base.addr_lock); + return transport; +} + +/* Do delayed input processing. This is scheduled by sctp_rcv(). */ +static void sctp_assoc_bh_rcv(struct sctp_association *asoc) +{ + struct sctp_endpoint *ep; + sctp_chunk_t *chunk; + struct sock *sk; + struct sctp_inq *inqueue; + int state, subtype; + sctp_assoc_t associd = sctp_assoc2id(asoc); + int error = 0; + + /* The association should be held so we should be safe. */ + ep = asoc->ep; + sk = asoc->base.sk; + + inqueue = &asoc->base.inqueue; + while (NULL != (chunk = sctp_inq_pop(inqueue))) { + state = asoc->state; + subtype = chunk->chunk_hdr->type; + + /* Remember where the last DATA chunk came from so we + * know where to send the SACK. + */ + if (sctp_chunk_is_data(chunk)) + asoc->peer.last_data_from = chunk->transport; + else + SCTP_INC_STATS(SctpInCtrlChunks); + + if (chunk->transport) + chunk->transport->last_time_heard = jiffies; + + /* Run through the state machine. */ + error = sctp_do_sm(SCTP_EVENT_T_CHUNK, SCTP_ST_CHUNK(subtype), + state, ep, asoc, chunk, GFP_ATOMIC); + + /* Check to see if the association is freed in response to + * the incoming chunk. If so, get out of the while loop. + */ + if (!sctp_id2assoc(sk, associd)) + break; + + /* If there is an error on chunk, discard this packet. */ + if (error && chunk) + chunk->pdiscard = 1; + } + +} + +/* This routine moves an association from its old sk to a new sk. */ +void sctp_assoc_migrate(struct sctp_association *assoc, struct sock *newsk) +{ + struct sctp_opt *newsp = sctp_sk(newsk); + struct sock *oldsk = assoc->base.sk; + + /* Delete the association from the old endpoint's list of + * associations. + */ + list_del(&assoc->asocs); + + /* Decrement the backlog value for a TCP-style socket. */ + if (SCTP_SOCKET_TCP == sctp_sk(oldsk)->type) + oldsk->ack_backlog--; + + /* Release references to the old endpoint and the sock. */ + sctp_endpoint_put(assoc->ep); + sock_put(assoc->base.sk); + + /* Get a reference to the new endpoint. */ + assoc->ep = newsp->ep; + sctp_endpoint_hold(assoc->ep); + + /* Get a reference to the new sock. */ + assoc->base.sk = newsk; + sock_hold(assoc->base.sk); + + /* Add the association to the new endpoint's list of associations. */ + sctp_endpoint_add_asoc(newsp->ep, assoc); +} + +/* Update an association (possibly from unexpected COOKIE-ECHO processing). */ +void sctp_assoc_update(struct sctp_association *asoc, struct sctp_association *new) +{ + /* Copy in new parameters of peer. */ + asoc->c = new->c; + asoc->peer.rwnd = new->peer.rwnd; + asoc->peer.sack_needed = new->peer.sack_needed; + asoc->peer.i = new->peer.i; + sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, + asoc->peer.i.initial_tsn); + + /* FIXME: + * Do we need to copy primary_path etc? + * + * More explicitly, addresses may have been removed and + * this needs accounting for. + */ + + /* If the case is A (association restart), use + * initial_tsn as next_tsn. If the case is B, use + * current next_tsn in case data sent to peer + * has been discarded and needs retransmission. + */ + if (SCTP_STATE_ESTABLISHED == asoc->state) { + + asoc->next_tsn = new->next_tsn; + asoc->ctsn_ack_point = new->ctsn_ack_point; + + /* Reinitialize SSN for both local streams + * and peer's streams. + */ + sctp_ssnmap_clear(asoc->ssnmap); + + } else { + asoc->ctsn_ack_point = asoc->next_tsn - 1; + if (!asoc->ssnmap) { + /* Move the ssnmap. */ + asoc->ssnmap = new->ssnmap; + new->ssnmap = NULL; + } + } + +} + +/* Update the retran path for sending a retransmitted packet. + * Round-robin through the active transports, else round-robin + * through the inactive transports as this is the next best thing + * we can try. + */ +void sctp_assoc_update_retran_path(struct sctp_association *asoc) +{ + struct sctp_transport *t, *next; + struct list_head *head = &asoc->peer.transport_addr_list; + struct list_head *pos; + + /* Find the next transport in a round-robin fashion. */ + t = asoc->peer.retran_path; + pos = &t->transports; + next = NULL; + + while (1) { + /* Skip the head. */ + if (pos->next == head) + pos = head->next; + else + pos = pos->next; + + t = list_entry(pos, struct sctp_transport, transports); + + /* Try to find an active transport. */ + + if (t->active) { + break; + } else { + /* Keep track of the next transport in case + * we don't find any active transport. + */ + if (!next) + next = t; + } + + /* We have exhausted the list, but didn't find any + * other active transports. If so, use the next + * transport. + */ + if (t == asoc->peer.retran_path) { + t = next; + break; + } + } + + asoc->peer.retran_path = t; +} + +/* Choose the transport for sending a SHUTDOWN packet. */ +struct sctp_transport *sctp_assoc_choose_shutdown_transport(struct sctp_association *asoc) +{ + /* If this is the first time SHUTDOWN is sent, use the active path, + * else use the retran path. If the last SHUTDOWN was sent over the + * retran path, update the retran path and use it. + */ + if (!asoc->shutdown_last_sent_to) + return asoc->peer.active_path; + else { + if (asoc->shutdown_last_sent_to == asoc->peer.retran_path) + sctp_assoc_update_retran_path(asoc); + return asoc->peer.retran_path; + } + +} + +/* Update the association's pmtu and frag_point by going through all the + * transports. This routine is called when a transport's PMTU has changed. + */ +void sctp_assoc_sync_pmtu(struct sctp_association *asoc) +{ + struct sctp_transport *t; + struct list_head *pos; + __u32 pmtu = 0; + + if (!asoc) + return; + + /* Get the lowest pmtu of all the transports. */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + if (!pmtu || (t->pmtu < pmtu)) + pmtu = t->pmtu; + } + + if (pmtu) { + asoc->pmtu = pmtu; + asoc->frag_point = sctp_frag_point(pmtu); + } + + SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n", + __FUNCTION__, asoc, asoc->pmtu, asoc->frag_point); +} + +/* Should we send a SACK to update our peer? */ +static inline int sctp_peer_needs_update(struct sctp_association *asoc) +{ + switch (asoc->state) { + case SCTP_STATE_ESTABLISHED: + case SCTP_STATE_SHUTDOWN_PENDING: + case SCTP_STATE_SHUTDOWN_RECEIVED: + if ((asoc->rwnd > asoc->a_rwnd) && + ((asoc->rwnd - asoc->a_rwnd) >= + min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) + return 1; + break; + default: + break; + } + return 0; +} + +/* Increase asoc's rwnd by len and send any window update SACK if needed. */ +void sctp_assoc_rwnd_increase(struct sctp_association *asoc, int len) +{ + sctp_chunk_t *sack; + struct timer_list *timer; + + if (asoc->rwnd_over) { + if (asoc->rwnd_over >= len) { + asoc->rwnd_over -= len; + } else { + asoc->rwnd += (len - asoc->rwnd_over); + asoc->rwnd_over = 0; + } + } else { + asoc->rwnd += len; + } + + SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) " + "- %u\n", __FUNCTION__, asoc, len, asoc->rwnd, + asoc->rwnd_over, asoc->a_rwnd); + + /* Send a window update SACK if the rwnd has increased by at least the + * minimum of the association's PMTU and half of the receive buffer. + * The algorithm used is similar to the one described in + * Section 4.2.3.3 of RFC 1122. + */ + if (sctp_peer_needs_update(asoc)) { + asoc->a_rwnd = asoc->rwnd; + SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p " + "rwnd: %u a_rwnd: %u\n", __FUNCTION__, + asoc, asoc->rwnd, asoc->a_rwnd); + sack = sctp_make_sack(asoc); + if (!sack) + return; + + asoc->peer.sack_needed = 0; + + sctp_outq_tail(&asoc->outqueue, sack); + + /* Stop the SACK timer. */ + timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; + if (timer_pending(timer) && del_timer(timer)) + sctp_association_put(asoc); + } +} + +/* Decrease asoc's rwnd by len. */ +void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, int len) +{ + SCTP_ASSERT(asoc->rwnd, "rwnd zero", return); + SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return); + if (asoc->rwnd >= len) { + asoc->rwnd -= len; + } else { + asoc->rwnd_over = len - asoc->rwnd; + asoc->rwnd = 0; + } + SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", + __FUNCTION__, asoc, len, asoc->rwnd, + asoc->rwnd_over); +} + +/* Build the bind address list for the association based on info from the + * local endpoint and the remote peer. + */ +int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc, int gfp) +{ + sctp_scope_t scope; + int flags; + + /* Use scoping rules to determine the subset of addresses from + * the endpoint. + */ + scope = sctp_scope(&asoc->peer.active_path->ipaddr); + flags = (PF_INET6 == asoc->base.sk->family) ? SCTP_ADDR6_ALLOWED : 0; + if (asoc->peer.ipv4_address) + flags |= SCTP_ADDR4_PEERSUPP; + if (asoc->peer.ipv6_address) + flags |= SCTP_ADDR6_PEERSUPP; + + return sctp_bind_addr_copy(&asoc->base.bind_addr, + &asoc->ep->base.bind_addr, + scope, gfp, flags); +} + +/* Build the association's bind address list from the cookie. */ +int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc, + sctp_cookie_t *cookie, int gfp) +{ + int var_size2 = ntohs(cookie->peer_init->chunk_hdr.length); + int var_size3 = cookie->raw_addr_list_len; + __u8 *raw = (__u8 *)cookie + sizeof(sctp_cookie_t) + var_size2; + + return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw, var_size3, + asoc->ep->base.bind_addr.port, gfp); +} diff -urN linux-2.4.22-bk23/net/sctp/bind_addr.c linux-2.4.22-bk24/net/sctp/bind_addr.c --- linux-2.4.22-bk23/net/sctp/bind_addr.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/bind_addr.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,396 @@ +/* SCTP kernel reference Implementation + * Copyright (c) Cisco 1999,2000 + * Copyright (c) Motorola 1999,2000,2001 + * Copyright (c) International Business Machines Corp., 2001,2002 + * Copyright (c) La Monte H.P. Yarroll 2001 + * + * This file is part of the SCTP kernel reference implementation. + * + * A collection class to handle the storage of transport addresses. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Daisy Chang + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Forward declarations for internal helpers. */ +static int sctp_copy_one_addr(sctp_bind_addr_t *, union sctp_addr *, + sctp_scope_t scope, int gfp, int flags); +static void sctp_bind_addr_clean(sctp_bind_addr_t *); + +/* First Level Abstractions. */ + +/* Copy 'src' to 'dest' taking 'scope' into account. Omit addresses + * in 'src' which have a broader scope than 'scope'. + */ +int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src, + sctp_scope_t scope, int gfp, int flags) +{ + struct sockaddr_storage_list *addr; + struct list_head *pos; + int error = 0; + + /* All addresses share the same port. */ + dest->port = src->port; + + /* Extract the addresses which are relevant for this scope. */ + list_for_each(pos, &src->address_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + error = sctp_copy_one_addr(dest, &addr->a, scope, + gfp, flags); + if (error < 0) + goto out; + } + + /* If there are no addresses matching the scope and + * this is global scope, try to get a link scope address, with + * the assumption that we must be sitting behind a NAT. + */ + if (list_empty(&dest->address_list) && (SCTP_SCOPE_GLOBAL == scope)) { + list_for_each(pos, &src->address_list) { + addr = list_entry(pos, struct sockaddr_storage_list, + list); + error = sctp_copy_one_addr(dest, &addr->a, + SCTP_SCOPE_LINK, gfp, + flags); + if (error < 0) + goto out; + } + } + +out: + if (error) + sctp_bind_addr_clean(dest); + + return error; +} + +/* Create a new SCTP_bind_addr from nothing. */ +sctp_bind_addr_t *sctp_bind_addr_new(int gfp) +{ + sctp_bind_addr_t *retval; + + retval = t_new(sctp_bind_addr_t, gfp); + if (!retval) + goto nomem; + + sctp_bind_addr_init(retval, 0); + retval->malloced = 1; + SCTP_DBG_OBJCNT_INC(bind_addr); + +nomem: + return retval; +} + +/* Initialize the SCTP_bind_addr structure for either an endpoint or + * an association. + */ +void sctp_bind_addr_init(sctp_bind_addr_t *bp, __u16 port) +{ + bp->malloced = 0; + + INIT_LIST_HEAD(&bp->address_list); + bp->port = port; +} + +/* Dispose of the address list. */ +static void sctp_bind_addr_clean(sctp_bind_addr_t *bp) +{ + struct sockaddr_storage_list *addr; + struct list_head *pos, *temp; + + /* Empty the bind address list. */ + list_for_each_safe(pos, temp, &bp->address_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + list_del(pos); + kfree(addr); + SCTP_DBG_OBJCNT_DEC(addr); + } +} + +/* Dispose of an SCTP_bind_addr structure */ +void sctp_bind_addr_free(sctp_bind_addr_t *bp) +{ + /* Empty the bind address list. */ + sctp_bind_addr_clean(bp); + + if (bp->malloced) { + kfree(bp); + SCTP_DBG_OBJCNT_DEC(bind_addr); + } +} + +/* Add an address to the bind address list in the SCTP_bind_addr structure. */ +int sctp_add_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *new, + int gfp) +{ + struct sockaddr_storage_list *addr; + + /* Add the address to the bind address list. */ + addr = t_new(struct sockaddr_storage_list, gfp); + if (!addr) + return -ENOMEM; + + addr->a = *new; + + /* Fix up the port if it has not yet been set. + * Both v4 and v6 have the port at the same offset. + */ + if (!addr->a.v4.sin_port) + addr->a.v4.sin_port = bp->port; + + INIT_LIST_HEAD(&addr->list); + list_add_tail(&addr->list, &bp->address_list); + SCTP_DBG_OBJCNT_INC(addr); + + return 0; +} + +/* Delete an address from the bind address list in the SCTP_bind_addr + * structure. + */ +int sctp_del_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *del_addr) +{ + struct list_head *pos, *temp; + struct sockaddr_storage_list *addr; + + list_for_each_safe(pos, temp, &bp->address_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + if (sctp_cmp_addr_exact(&addr->a, del_addr)) { + /* Found the exact match. */ + list_del(pos); + kfree(addr); + SCTP_DBG_OBJCNT_DEC(addr); + + return 0; + } + } + + return -EINVAL; +} + +/* Create a network byte-order representation of all the addresses + * formated as SCTP parameters. + * + * The second argument is the return value for the length. + */ +union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, + int *addrs_len, int gfp) +{ + union sctp_params addrparms; + union sctp_params retval; + int addrparms_len; + sctp_addr_param_t rawaddr; + int len; + struct sockaddr_storage_list *addr; + struct list_head *pos; + addrparms_len = 0; + len = 0; + + /* Allocate enough memory at once. */ + list_for_each(pos, &bp->address_list) { + len += sizeof(sctp_addr_param_t); + } + + /* Don't even bother embedding an address if there + * is only one. + */ + if (len == sizeof(sctp_addr_param_t)) { + retval.v = NULL; + goto end_raw; + } + + retval.v = kmalloc(len, gfp); + if (!retval.v) + goto end_raw; + + addrparms = retval; + + list_for_each(pos, &bp->address_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + len = sockaddr2sctp_addr(&addr->a, &rawaddr); + memcpy(addrparms.v, &rawaddr, len); + addrparms.v += len; + addrparms_len += len; + } + +end_raw: + *addrs_len = addrparms_len; + return retval; +} + +/* + * Create an address list out of the raw address list format (IPv4 and IPv6 + * address parameters). + */ +int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list, + int addrs_len, __u16 port, int gfp) +{ + sctp_addr_param_t *rawaddr; + sctp_paramhdr_t *param; + union sctp_addr addr; + int retval = 0; + int len; + + /* Convert the raw address to standard address format */ + while (addrs_len) { + param = (sctp_paramhdr_t *)raw_addr_list; + rawaddr = (sctp_addr_param_t *)raw_addr_list; + + switch (param->type) { + case SCTP_PARAM_IPV4_ADDRESS: + case SCTP_PARAM_IPV6_ADDRESS: + sctp_param2sockaddr(&addr, rawaddr, port, 0); + retval = sctp_add_bind_addr(bp, &addr, gfp); + if (retval) { + /* Can't finish building the list, clean up. */ + sctp_bind_addr_clean(bp); + break;; + } + len = ntohs(param->length); + addrs_len -= len; + raw_addr_list += len; + break; + default: + /* Corrupted raw addr list! */ + retval = -EINVAL; + sctp_bind_addr_clean(bp); + break; + } + if (retval) + break; + } + + return retval; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* Does this contain a specified address? Allow wildcarding. */ +int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr, + struct sctp_opt *opt) +{ + struct sockaddr_storage_list *laddr; + struct list_head *pos; + + list_for_each(pos, &bp->address_list) { + laddr = list_entry(pos, struct sockaddr_storage_list, list); + if (opt->pf->cmp_addr(&laddr->a, addr, opt)) + return 1; + } + + return 0; +} + +/* Copy out addresses from the global local address list. */ +static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr, + sctp_scope_t scope, int gfp, int flags) +{ + struct sctp_protocol *proto = sctp_get_protocol(); + int error = 0; + + if (sctp_is_any(addr)) { + error = sctp_copy_local_addr_list(proto, dest, scope, + gfp, flags); + } else if (sctp_in_scope(addr, scope)) { + /* Now that the address is in scope, check to see if + * the address type is supported by local sock as + * well as the remote peer. + */ + if ((((AF_INET == addr->sa.sa_family) && + (flags & SCTP_ADDR4_PEERSUPP))) || + (((AF_INET6 == addr->sa.sa_family) && + (flags & SCTP_ADDR6_ALLOWED) && + (flags & SCTP_ADDR6_PEERSUPP)))) + error = sctp_add_bind_addr(dest, addr, gfp); + } + + return error; +} + +/* Is this a wildcard address? */ +int sctp_is_any(const union sctp_addr *addr) +{ + struct sctp_af *af = sctp_get_af_specific(addr->sa.sa_family); + if (!af) + return 0; + return af->is_any(addr); +} + +/* Is 'addr' valid for 'scope'? */ +int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope) +{ + sctp_scope_t addr_scope = sctp_scope(addr); + + /* The unusable SCTP addresses will not be considered with + * any defined scopes. + */ + if (SCTP_SCOPE_UNUSABLE == addr_scope) + return 0; + /* + * For INIT and INIT-ACK address list, let L be the level of + * of requested destination address, sender and receiver + * SHOULD include all of its addresses with level greater + * than or equal to L. + */ + if (addr_scope <= scope) + return 1; + + return 0; +} + +/******************************************************************** + * 3rd Level Abstractions + ********************************************************************/ + +/* What is the scope of 'addr'? */ +sctp_scope_t sctp_scope(const union sctp_addr *addr) +{ + struct sctp_af *af; + + af = sctp_get_af_specific(addr->sa.sa_family); + if (!af) + return SCTP_SCOPE_UNUSABLE; + + return af->scope((union sctp_addr *)addr); +} diff -urN linux-2.4.22-bk23/net/sctp/command.c linux-2.4.22-bk24/net/sctp/command.c --- linux-2.4.22-bk23/net/sctp/command.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/command.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,104 @@ +/* SCTP kernel reference Implementation Copyright (C) 1999-2001 + * Cisco, Motorola, and IBM + * Copyright 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions manipulate sctp command sequences. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include + +/* Create a new sctp_command_sequence. */ +sctp_cmd_seq_t *sctp_new_cmd_seq(int gfp) +{ + sctp_cmd_seq_t *retval = t_new(sctp_cmd_seq_t, gfp); + + if (retval) + sctp_init_cmd_seq(retval); + + return retval; +} + +/* Initialize a block of memory as a command sequence. */ +int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) +{ + memset(seq, 0, sizeof(sctp_cmd_seq_t)); + return 1; /* We always succeed. */ +} + +/* Add a command to a sctp_cmd_seq_t. + * Return 0 if the command sequence is full. + */ +int sctp_add_cmd(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) +{ + if (seq->next_free_slot >= SCTP_MAX_NUM_COMMANDS) + goto fail; + + seq->cmds[seq->next_free_slot].verb = verb; + seq->cmds[seq->next_free_slot++].obj = obj; + + return 1; + +fail: + return 0; +} + +/* Rewind an sctp_cmd_seq_t to iterate from the start. */ +int sctp_rewind_sequence(sctp_cmd_seq_t *seq) +{ + seq->next_cmd = 0; + return 1; /* We always succeed. */ +} + +/* Return the next command structure in a sctp_cmd_seq. + * Returns NULL at the end of the sequence. + */ +sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq) +{ + sctp_cmd_t *retval = NULL; + + if (seq->next_cmd < seq->next_free_slot) + retval = &seq->cmds[seq->next_cmd++]; + + return retval; +} + +/* Dispose of a command sequence. */ +void sctp_free_cmd_seq(sctp_cmd_seq_t *seq) +{ + kfree(seq); +} diff -urN linux-2.4.22-bk23/net/sctp/crc32c.c linux-2.4.22-bk24/net/sctp/crc32c.c --- linux-2.4.22-bk23/net/sctp/crc32c.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/crc32c.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,220 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * SCTP Checksum functions + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Dinakaran Joseph + * Jon Grimm + * Sridhar Samudrala + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +/* The following code has been taken directly from + * draft-ietf-tsvwg-sctpcsum-03.txt + * + * The code has now been modified specifically for SCTP knowledge. + */ + +#include +#include + +#define CRC32C_POLY 0x1EDC6F41 +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Copyright 2001, D. Otis. Use this program, code or tables */ +/* extracted from it, as desired without restriction. */ +/* */ +/* 32 Bit Reflected CRC table generation for SCTP. */ +/* To accommodate serial byte data being shifted out least */ +/* significant bit first, the table's 32 bit words are reflected */ +/* which flips both byte and bit MS and LS positions. The CRC */ +/* is calculated MS bits first from the perspective of the serial*/ +/* stream. The x^32 term is implied and the x^0 term may also */ +/* be shown as +1. The polynomial code used is 0x1EDC6F41. */ +/* Castagnoli93 */ +/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+ */ +/* x^11+x^10+x^9+x^8+x^6+x^0 */ +/* Guy Castagnoli Stefan Braeuer and Martin Herrman */ +/* "Optimization of Cyclic Redundancy-Check Codes */ +/* with 24 and 32 Parity Bits", */ +/* IEEE Transactions on Communications, Vol.41, No.6, June 1993 */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +__u32 crc_c[256] = { + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, + 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, + 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, + 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, + 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, + 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, + 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, + 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, + 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, + 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, + 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, + 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, + 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, + 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, + 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, + 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, + 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, + 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, + 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, + 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, + 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, + 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, + 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, + 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, + 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, + 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, + 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, + 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, + 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, + 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, + 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, + 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, + 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, +}; + +__u32 sctp_start_cksum(__u8 *buffer, __u16 length) +{ + __u32 crc32 = ~(__u32) 0; + __u32 i; + + /* Optimize this routine to be SCTP specific, knowing how + * to skip the checksum field of the SCTP header. + */ + + /* Calculate CRC up to the checksum. */ + for (i = 0; i < (sizeof(struct sctphdr) - sizeof(__u32)); i++) + CRC32C(crc32, buffer[i]); + + /* Skip checksum field of the header. */ + for (i = 0; i < sizeof(__u32); i++) + CRC32C(crc32, 0); + + /* Calculate the rest of the CRC. */ + for (i = sizeof(struct sctphdr); i < length ; i++) + CRC32C(crc32, buffer[i]); + + return crc32; +} + +__u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32) +{ + __u32 i; + + for (i = 0; i < length ; i++) + CRC32C(crc32, buffer[i]); + + return crc32; +} + +__u32 sctp_update_copy_cksum(__u8 *to, __u8 *from, __u16 length, __u32 crc32) +{ + __u32 i; + __u32 *_to = (__u32 *)to; + __u32 *_from = (__u32 *)from; + + for (i = 0; i < (length/4); i++) { + _to[i] = _from[i]; + CRC32C(crc32, from[i*4]); + CRC32C(crc32, from[i*4+1]); + CRC32C(crc32, from[i*4+2]); + CRC32C(crc32, from[i*4+3]); + } + + return crc32; +} + +__u32 sctp_end_cksum(__u32 crc32) +{ + __u32 result; + __u8 byte0, byte1, byte2, byte3; + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polyomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + return crc32; +} diff -urN linux-2.4.22-bk23/net/sctp/debug.c linux-2.4.22-bk24/net/sctp/debug.c --- linux-2.4.22-bk23/net/sctp/debug.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/debug.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,202 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * This file is part of the implementation of the add-IP extension, + * based on June 29, 2001, + * for the SCTP kernel reference Implementation. + * + * This file converts numerical ID value to alphabetical names for SCTP + * terms such as chunk type, parameter time, event type, etc. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Xingang Guo + * Jon Grimm + * Daisy Chang + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include + +#if SCTP_DEBUG +int sctp_debug_flag = 1; /* Initially enable DEBUG */ +#endif /* SCTP_DEBUG */ + +/* These are printable forms of Chunk ID's from section 3.1. */ +static const char *sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = { + "DATA", + "INIT", + "INIT_ACK", + "SACK", + "HEARTBEAT", + "HEARTBEAT_ACK", + "ABORT", + "SHUTDOWN", + "SHUTDOWN_ACK", + "ERROR", + "COOKIE_ECHO", + "COOKIE_ACK", + "ECN_ECNE", + "ECN_CWR", + "SHUTDOWN_COMPLETE", +}; + +/* Lookup "chunk type" debug name. */ +const char *sctp_cname(const sctp_subtype_t cid) +{ + if (cid.chunk < 0) + return "illegal chunk id"; + if (cid.chunk <= SCTP_CID_BASE_MAX) + return sctp_cid_tbl[cid.chunk]; + + switch (cid.chunk) { + case SCTP_CID_ASCONF: + return "ASCONF"; + + case SCTP_CID_ASCONF_ACK: + return "ASCONF_ACK"; + + default: + return "unknown chunk"; + }; + return "unknown chunk"; +} + +/* These are printable form of variable-length parameters. */ +const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = { + "", + "PARAM_HEARTBEAT_INFO", + "", + "", + "", + "PARAM_IPV4_ADDRESS", + "PARAM_IPV6_ADDRESS", + "PARAM_STATE_COOKIE", + "PARAM_UNRECOGNIZED_PARAMETERS", + "PARAM_COOKIE_PRESERVATIVE", + "", + "PARAM_HOST_NAME_ADDRESS", + "PARAM_SUPPORTED_ADDRESS_TYPES", +}; + +/* These are printable forms of the states. */ +const char *sctp_state_tbl[SCTP_STATE_NUM_STATES] = { + "STATE_EMPTY", + "STATE_CLOSED", + "STATE_COOKIE_WAIT", + "STATE_COOKIE_ECHOED", + "STATE_ESTABLISHED", + "STATE_SHUTDOWN_PENDING", + "STATE_SHUTDOWN_SENT", + "STATE_SHUTDOWN_RECEIVED", + "STATE_SHUTDOWN_ACK_SENT", +}; + +/* Events that could change the state of an association. */ +const char *sctp_evttype_tbl[] = { + "EVENT_T_unknown", + "EVENT_T_CHUNK", + "EVENT_T_TIMEOUT", + "EVENT_T_OTHER", + "EVENT_T_PRIMITIVE" +}; + +/* Return value of a state function */ +const char *sctp_status_tbl[] = { + "DISPOSITION_DISCARD", + "DISPOSITION_CONSUME", + "DISPOSITION_NOMEM", + "DISPOSITION_DELETE_TCB", + "DISPOSITION_ABORT", + "DISPOSITION_VIOLATION", + "DISPOSITION_NOT_IMPL", + "DISPOSITION_ERROR", + "DISPOSITION_BUG" +}; + +/* Printable forms of primitives */ +static const char *sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = { + "PRIMITIVE_ASSOCIATE", + "PRIMITIVE_SHUTDOWN", + "PRIMITIVE_ABORT", + "PRIMITIVE_SEND", + "PRIMITIVE_REQUESTHEARTBEAT", +}; + +/* Lookup primitive debug name. */ +const char *sctp_pname(const sctp_subtype_t id) +{ + if (id.primitive < 0) + return "illegal primitive"; + if (id.primitive <= SCTP_EVENT_PRIMITIVE_MAX) + return sctp_primitive_tbl[id.primitive]; + return "unknown_primitive"; +} + +static const char *sctp_other_tbl[] = { + "NO_PENDING_TSN", +}; + +/* Lookup "other" debug name. */ +const char *sctp_oname(const sctp_subtype_t id) +{ + if (id.other < 0) + return "illegal 'other' event"; + if (id.other < SCTP_EVENT_OTHER_MAX) + return sctp_other_tbl[id.other]; + return "unknown 'other' event"; +} + +static const char *sctp_timer_tbl[] = { + "TIMEOUT_NONE", + "TIMEOUT_T1_COOKIE", + "TIMEOUT_T1_INIT", + "TIMEOUT_T2_SHUTDOWN", + "TIMEOUT_T3_RTX", + "TIMEOUT_T5_SHUTDOWN_GUARD", + "TIMEOUT_HEARTBEAT", + "TIMEOUT_SACK", + "TIMEOUT_AUTOCLOSE", +}; + +/* Lookup timer debug name. */ +const char *sctp_tname(const sctp_subtype_t id) +{ + if (id.timeout < 0) + return "illegal 'timer' event"; + if (id.timeout <= SCTP_EVENT_TIMEOUT_MAX) + return sctp_timer_tbl[id.timeout]; + return "unknown_timer"; +} diff -urN linux-2.4.22-bk23/net/sctp/endpointola.c linux-2.4.22-bk24/net/sctp/endpointola.c --- linux-2.4.22-bk23/net/sctp/endpointola.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/endpointola.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,396 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2002 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * This abstraction represents an SCTP endpoint. + * + * This file is part of the implementation of the add-IP extension, + * based on June 29, 2001, + * for the SCTP kernel reference Implementation. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Daisy Chang + * Dajiang Zhang + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include /* get_random_bytes() */ +#include +#include +#include +#include + +/* Forward declarations for internal helpers. */ +static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep); + +/* Create a sctp_endpoint with all that boring stuff initialized. + * Returns NULL if there isn't enough memory. + */ +struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, int gfp) +{ + struct sctp_endpoint *ep; + + /* Build a local endpoint. */ + ep = t_new(struct sctp_endpoint, gfp); + if (!ep) + goto fail; + if (!sctp_endpoint_init(ep, sk, gfp)) + goto fail_init; + ep->base.malloced = 1; + SCTP_DBG_OBJCNT_INC(ep); + return ep; + +fail_init: + kfree(ep); +fail: + return NULL; +} + +/* + * Initialize the base fields of the endpoint structure. + */ +struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, + struct sock *sk, int gfp) +{ + struct sctp_opt *sp = sctp_sk(sk); + memset(ep, 0, sizeof(struct sctp_endpoint)); + + /* Initialize the base structure. */ + /* What type of endpoint are we? */ + ep->base.type = SCTP_EP_TYPE_SOCKET; + + /* Initialize the basic object fields. */ + atomic_set(&ep->base.refcnt, 1); + ep->base.dead = 0; + ep->base.malloced = 1; + + /* Create an input queue. */ + sctp_inq_init(&ep->base.inqueue); + + /* Set its top-half handler */ + sctp_inq_set_th_handler(&ep->base.inqueue, + (void (*)(void *))sctp_endpoint_bh_rcv, ep); + + /* Initialize the bind addr area */ + sctp_bind_addr_init(&ep->base.bind_addr, 0); + ep->base.addr_lock = RW_LOCK_UNLOCKED; + + /* Remember who we are attached to. */ + ep->base.sk = sk; + sock_hold(ep->base.sk); + + /* Create the lists of associations. */ + INIT_LIST_HEAD(&ep->asocs); + + /* Set up the base timeout information. */ + ep->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0; + ep->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] = + SCTP_DEFAULT_TIMEOUT_T1_COOKIE; + ep->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] = + SCTP_DEFAULT_TIMEOUT_T1_INIT; + ep->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = + sp->rtoinfo.srto_initial; + ep->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0; + + /* sctpimpguide-05 Section 2.12.2 + * If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the + * recommended value of 5 times 'RTO.Max'. + */ + ep->timeouts[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD] + = 5 * sp->rtoinfo.srto_max; + + ep->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] = + SCTP_DEFAULT_TIMEOUT_HEARTBEAT; + ep->timeouts[SCTP_EVENT_TIMEOUT_SACK] = + SCTP_DEFAULT_TIMEOUT_SACK; + ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = + sp->autoclose * HZ; + + /* Set up the default send/receive buffer space. */ + + /* FIXME - Should the min and max window size be configurable + * sysctl parameters as opposed to be constants? + */ + sk->rcvbuf = SCTP_DEFAULT_MAXWINDOW; + sk->sndbuf = SCTP_DEFAULT_MAXWINDOW * 2; + + /* Use SCTP specific send buffer space queues. */ + sk->write_space = sctp_write_space; + sk->use_write_queue = 1; + + /* Initialize the secret key used with cookie. */ + get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE); + ep->last_key = ep->current_key = 0; + ep->key_changed_at = jiffies; + + ep->debug_name = "unnamedEndpoint"; + return ep; +} + +/* Add an association to an endpoint. */ +void sctp_endpoint_add_asoc(struct sctp_endpoint *ep, + struct sctp_association *asoc) +{ + struct sock *sk = ep->base.sk; + + /* Now just add it to our list of asocs */ + list_add_tail(&asoc->asocs, &ep->asocs); + + /* Increment the backlog value for a TCP-style listening socket. */ + if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) && + (SCTP_SS_LISTENING == sk->state)) + sk->ack_backlog++; +} + +/* Free the endpoint structure. Delay cleanup until + * all users have released their reference count on this structure. + */ +void sctp_endpoint_free(struct sctp_endpoint *ep) +{ + ep->base.dead = 1; + sctp_endpoint_put(ep); +} + +/* Final destructor for endpoint. */ +void sctp_endpoint_destroy(struct sctp_endpoint *ep) +{ + SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); + + ep->base.sk->state = SCTP_SS_CLOSED; + + /* Unlink this endpoint, so we can't find it again! */ + sctp_unhash_endpoint(ep); + + /* Free up the HMAC transform. */ + if (sctp_sk(ep->base.sk)->hmac) + sctp_crypto_free_tfm(sctp_sk(ep->base.sk)->hmac); + + /* Cleanup. */ + sctp_inq_free(&ep->base.inqueue); + sctp_bind_addr_free(&ep->base.bind_addr); + + /* Remove and free the port */ + if (ep->base.sk->prev != NULL) + sctp_put_port(ep->base.sk); + + /* Give up our hold on the sock. */ + if (ep->base.sk) + sock_put(ep->base.sk); + + /* Finally, free up our memory. */ + if (ep->base.malloced) { + kfree(ep); + SCTP_DBG_OBJCNT_DEC(ep); + } +} + +/* Hold a reference to an endpoint. */ +void sctp_endpoint_hold(struct sctp_endpoint *ep) +{ + atomic_inc(&ep->base.refcnt); +} + +/* Release a reference to an endpoint and clean up if there are + * no more references. + */ +void sctp_endpoint_put(struct sctp_endpoint *ep) +{ + if (atomic_dec_and_test(&ep->base.refcnt)) + sctp_endpoint_destroy(ep); +} + +/* Is this the endpoint we are looking for? */ +struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, + const union sctp_addr *laddr) +{ + struct sctp_endpoint *retval; + + sctp_read_lock(&ep->base.addr_lock); + if (ep->base.bind_addr.port == laddr->v4.sin_port) { + if (sctp_bind_addr_match(&ep->base.bind_addr, laddr, + sctp_sk(ep->base.sk))) { + retval = ep; + goto out; + } + } + + retval = NULL; + +out: + sctp_read_unlock(&ep->base.addr_lock); + return retval; +} + +/* Find the association that goes with this chunk. + * We do a linear search of the associations for this endpoint. + * We return the matching transport address too. + */ +struct sctp_association *__sctp_endpoint_lookup_assoc( + const struct sctp_endpoint *ep, + const union sctp_addr *paddr, + struct sctp_transport **transport) +{ + int rport; + struct sctp_association *asoc; + struct list_head *pos; + + rport = paddr->v4.sin_port; + + list_for_each(pos, &ep->asocs) { + asoc = list_entry(pos, struct sctp_association, asocs); + if (rport == asoc->peer.port) { + sctp_read_lock(&asoc->base.addr_lock); + *transport = sctp_assoc_lookup_paddr(asoc, paddr); + sctp_read_unlock(&asoc->base.addr_lock); + + if (*transport) + return asoc; + } + } + + *transport = NULL; + return NULL; +} + +/* Lookup association on an endpoint based on a peer address. BH-safe. */ +struct sctp_association *sctp_endpoint_lookup_assoc( + const struct sctp_endpoint *ep, + const union sctp_addr *paddr, + struct sctp_transport **transport) +{ + struct sctp_association *asoc; + + sctp_local_bh_disable(); + asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport); + sctp_local_bh_enable(); + + return asoc; +} + +/* Look for any peeled off association from the endpoint that matches the + * given peer address. + */ +int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, + const union sctp_addr *paddr) +{ + struct list_head *pos; + struct sockaddr_storage_list *addr; + sctp_bind_addr_t *bp; + + sctp_read_lock(&ep->base.addr_lock); + bp = &ep->base.bind_addr; + list_for_each(pos, &bp->address_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + if (sctp_has_association(&addr->a, paddr)) { + sctp_read_unlock(&ep->base.addr_lock); + return 1; + } + } + sctp_read_unlock(&ep->base.addr_lock); + + return 0; +} + +/* Do delayed input processing. This is scheduled by sctp_rcv(). + * This may be called on BH or task time. + */ +static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep) +{ + struct sctp_association *asoc; + struct sock *sk; + struct sctp_transport *transport; + sctp_chunk_t *chunk; + struct sctp_inq *inqueue; + sctp_subtype_t subtype; + sctp_state_t state; + int error = 0; + + if (ep->base.dead) + return; + + asoc = NULL; + inqueue = &ep->base.inqueue; + sk = ep->base.sk; + + while (NULL != (chunk = sctp_inq_pop(inqueue))) { + subtype.chunk = chunk->chunk_hdr->type; + + /* We might have grown an association since last we + * looked, so try again. + * + * This happens when we've just processed our + * COOKIE-ECHO chunk. + */ + if (NULL == chunk->asoc) { + asoc = sctp_endpoint_lookup_assoc(ep, + sctp_source(chunk), + &transport); + chunk->asoc = asoc; + chunk->transport = transport; + } + + state = asoc ? asoc->state : SCTP_STATE_CLOSED; + + /* Remember where the last DATA chunk came from so we + * know where to send the SACK. + */ + if (asoc && sctp_chunk_is_data(chunk)) + asoc->peer.last_data_from = chunk->transport; + else + SCTP_INC_STATS(SctpInCtrlChunks); + + if (chunk->transport) + chunk->transport->last_time_heard = jiffies; + + error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state, + ep, asoc, chunk, GFP_ATOMIC); + + if (error && chunk) + chunk->pdiscard = 1; + + /* Check to see if the endpoint is freed in response to + * the incoming chunk. If so, get out of the while loop. + */ + if (!sctp_sk(sk)->ep) + break; + } +} diff -urN linux-2.4.22-bk23/net/sctp/hashdriver.c linux-2.4.22-bk24/net/sctp/hashdriver.c --- linux-2.4.22-bk23/net/sctp/hashdriver.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/hashdriver.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,128 @@ +/* SCTP reference Implementation Copyright (C) 1999 Cisco And Motorola + * + * This file origiantes from Randy Stewart's SCTP reference Implementation. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Randy Stewart rstewar1@email.mot.com + * Ken Morneau kmorneau@cisco.com + * Qiaobing Xie qxie1@email.mot.com + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorperated into the next SCTP release. + * + * There are still LOTS of bugs in this code... I always run on the motto + * "it is a wonder any code ever works :)" + */ + +#include +#include +#include +#include + +/* SCTP Main driver. + * passing a two pointers and two lengths, + * returning a digest pointer filled. The md5 code + * was taken directly from the RFC (2104) so to understand it + * you may want to go look at the RFC referenced in the + * SCTP spec. We did modify this code to either user OUR + * implementation of SLA1 or the MD5 that comes from its + * RFC. SLA1 may have IPR issues so you need to check in + * to this if you wish to use it... Or at least that is + * what the FIP-180.1 web page says. + */ + +void sctp_hash_digest(const char *key, const int in_key_len, + const char *text, const int text_len, + __u8 *digest) +{ + int key_len = in_key_len; + struct SLA_1_Context context; + + __u8 k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + __u8 k_opad[65]; /* outer padding - + * key XORd with opad + */ + __u8 tk[20]; + int i; + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + struct SLA_1_Context tctx; + + SLA1_Init(&tctx); + SLA1_Process(&tctx, key, key_len); + SLA1_Final(&tctx,tk); + key = tk; + key_len = 20; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner hash */ + SLA1_Init(&context); /* init context for 1st + * pass + */ + SLA1_Process(&context, k_ipad, 64); /* start with inner pad */ + SLA1_Process(&context, text, text_len); /* then text of datagram */ + SLA1_Final(&context,digest); /* finish up 1st pass */ + + /* + * perform outer hash + */ + SLA1_Init(&context); /* init context for 2nd + * pass + */ + SLA1_Process(&context, k_opad, 64); /* start with outer pad */ + SLA1_Process(&context, digest, 20); /* then results of 1st + * hash + */ + SLA1_Final(&context, digest); /* finish up 2nd pass */ +} + diff -urN linux-2.4.22-bk23/net/sctp/input.c linux-2.4.22-bk24/net/sctp/input.c --- linux-2.4.22-bk23/net/sctp/input.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/input.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,834 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions handle all input from the IP layer into SCTP. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Xingang Guo + * Jon Grimm + * Hui Huang + * Daisy Chang + * Sridhar Samudrala + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include /* For struct list_head */ +#include +#include +#include /* For struct timeval */ +#include +#include +#include +#include +#include +#include +#include + +/* Forward declarations for internal helpers. */ +static int sctp_rcv_ootb(struct sk_buff *); +struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, + const union sctp_addr *laddr, + const union sctp_addr *paddr, + struct sctp_transport **transportp); +struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr); + + +/* Calculate the SCTP checksum of an SCTP packet. */ +static inline int sctp_rcv_checksum(struct sk_buff *skb) +{ + struct sctphdr *sh; + __u32 cmp, val; + struct sk_buff *list = skb_shinfo(skb)->frag_list; + + sh = (struct sctphdr *) skb->h.raw; + cmp = ntohl(sh->checksum); + + val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); + + for (; list; list = list->next) + val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), + val); + + val = sctp_end_cksum(val); + + if (val != cmp) { + /* CRC failure, dump it. */ + SCTP_INC_STATS_BH(SctpChecksumErrors); + return -1; + } + return 0; +} + +/* + * This is the routine which IP calls when receiving an SCTP packet. + */ +int sctp_rcv(struct sk_buff *skb) +{ + struct sock *sk; + struct sctp_association *asoc; + struct sctp_endpoint *ep = NULL; + struct sctp_ep_common *rcvr; + struct sctp_transport *transport = NULL; + sctp_chunk_t *chunk; + struct sctphdr *sh; + union sctp_addr src; + union sctp_addr dest; + int family; + struct sctp_af *af; + int ret = 0; + + if (skb->pkt_type!=PACKET_HOST) + goto discard_it; + + SCTP_INC_STATS_BH(SctpInSCTPPacks); + + sh = (struct sctphdr *) skb->h.raw; + + /* Pull up the IP and SCTP headers. */ + __skb_pull(skb, skb->h.raw - skb->data); + if (skb->len < sizeof(struct sctphdr)) + goto bad_packet; + if (sctp_rcv_checksum(skb) < 0) + goto bad_packet; + + skb_pull(skb, sizeof(struct sctphdr)); + + family = ipver2af(skb->nh.iph->version); + af = sctp_get_af_specific(family); + if (unlikely(!af)) + goto bad_packet; + + /* Initialize local addresses for lookups. */ + af->from_skb(&src, skb, 1); + af->from_skb(&dest, skb, 0); + + /* If the packet is to or from a non-unicast address, + * silently discard the packet. + * + * This is not clearly defined in the RFC except in section + * 8.4 - OOTB handling. However, based on the book "Stream Control + * Transmission Protocol" 2.1, "It is important to note that the + * IP address of an SCTP transport address must be a routable + * unicast address. In other words, IP multicast addresses and + * IP broadcast addresses cannot be used in an SCTP transport + * address." + */ + if (!af->addr_valid(&src) || !af->addr_valid(&dest)) + goto discard_it; + + asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport); + + /* + * RFC 2960, 8.4 - Handle "Out of the blue" Packets. + * An SCTP packet is called an "out of the blue" (OOTB) + * packet if it is correctly formed, i.e., passed the + * receiver's checksum check, but the receiver is not + * able to identify the association to which this + * packet belongs. + */ + if (!asoc) { + ep = __sctp_rcv_lookup_endpoint(&dest); + if (sctp_rcv_ootb(skb)) { + SCTP_INC_STATS_BH(SctpOutOfBlues); + goto discard_release; + } + } + + /* Retrieve the common input handling substructure. */ + rcvr = asoc ? &asoc->base : &ep->base; + sk = rcvr->sk; + + if (!ipsec_sk_policy(sk, skb)) + goto discard_release; + + ret = sk_filter(sk, skb, 1); + if (ret) + goto discard_release; + + /* Create an SCTP packet structure. */ + chunk = sctp_chunkify(skb, asoc, sk); + if (!chunk) { + ret = -ENOMEM; + goto discard_release; + } + + /* Remember what endpoint is to handle this packet. */ + chunk->rcvr = rcvr; + + /* Remember the SCTP header. */ + chunk->sctp_hdr = sh; + + /* Set the source and destination addresses of the incoming chunk. */ + sctp_init_addrs(chunk, &src, &dest); + + /* Remember where we came from. */ + chunk->transport = transport; + + /* Acquire access to the sock lock. Note: We are safe from other + * bottom halves on this lock, but a user may be in the lock too, + * so check if it is busy. + */ + sctp_bh_lock_sock(sk); + + if (sock_owned_by_user(sk)) + sk_add_backlog(sk, (struct sk_buff *) chunk); + else + sctp_backlog_rcv(sk, (struct sk_buff *) chunk); + + /* Release the sock and any reference counts we took in the + * lookup calls. + */ + sctp_bh_unlock_sock(sk); + if (asoc) + sctp_association_put(asoc); + else + sctp_endpoint_put(ep); + sock_put(sk); + return ret; + +bad_packet: + SCTP_INC_STATS(SctpChecksumErrors); + +discard_it: + kfree_skb(skb); + return ret; + +discard_release: + /* Release any structures we may be holding. */ + if (asoc) { + sock_put(asoc->base.sk); + sctp_association_put(asoc); + } else { + sock_put(ep->base.sk); + sctp_endpoint_put(ep); + } + + goto discard_it; +} + +/* Handle second half of inbound skb processing. If the sock was busy, + * we may have need to delay processing until later when the sock is + * released (on the backlog). If not busy, we call this routine + * directly from the bottom half. + */ +int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) +{ + sctp_chunk_t *chunk; + struct sctp_inq *inqueue; + + /* One day chunk will live inside the skb, but for + * now this works. + */ + chunk = (sctp_chunk_t *) skb; + inqueue = &chunk->rcvr->inqueue; + + sctp_inq_push(inqueue, chunk); + return 0; +} + +/* Handle icmp frag needed error. */ +void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, + struct sctp_transport *t, __u32 pmtu) +{ + if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) { + printk(KERN_WARNING "%s: Reported pmtu %d too low, " + "using default minimum of %d\n", __FUNCTION__, pmtu, + SCTP_DEFAULT_MINSEGMENT); + pmtu = SCTP_DEFAULT_MINSEGMENT; + } + + if (!sock_owned_by_user(sk) && t && (t->pmtu != pmtu)) { + t->pmtu = pmtu; + sctp_assoc_sync_pmtu(asoc); + sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD); + } +} + +/* Common lookup code for icmp/icmpv6 error handler. */ +struct sock *sctp_err_lookup(int family, struct sk_buff *skb, + struct sctphdr *sctphdr, + struct sctp_endpoint **epp, + struct sctp_association **app, + struct sctp_transport **tpp) +{ + union sctp_addr saddr; + union sctp_addr daddr; + struct sctp_af *af; + struct sock *sk = NULL; + struct sctp_endpoint *ep = NULL; + struct sctp_association *asoc = NULL; + struct sctp_transport *transport = NULL; + + *app = NULL; *epp = NULL; *tpp = NULL; + + af = sctp_get_af_specific(family); + if (unlikely(!af)) { + return NULL; + } + + /* Initialize local addresses for lookups. */ + af->from_skb(&saddr, skb, 1); + af->from_skb(&daddr, skb, 0); + + /* Look for an association that matches the incoming ICMP error + * packet. + */ + asoc = __sctp_lookup_association(&saddr, &daddr, &transport); + if (!asoc) { + /* If there is no matching association, see if it matches any + * endpoint. This may happen for an ICMP error generated in + * response to an INIT_ACK. + */ + ep = __sctp_rcv_lookup_endpoint(&daddr); + if (!ep) { + return NULL; + } + } + + if (asoc) { + if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) { + ICMP_INC_STATS_BH(IcmpInErrors); + goto out; + } + sk = asoc->base.sk; + } else + sk = ep->base.sk; + + sctp_bh_lock_sock(sk); + + /* If too many ICMPs get dropped on busy + * servers this needs to be solved differently. + */ + if (sock_owned_by_user(sk)) + NET_INC_STATS_BH(LockDroppedIcmps); + + *epp = ep; + *app = asoc; + *tpp = transport; + return sk; + +out: + sock_put(sk); + if (asoc) + sctp_association_put(asoc); + if (ep) + sctp_endpoint_put(ep); + return NULL; +} + +/* Common cleanup code for icmp/icmpv6 error handler. */ +void sctp_err_finish(struct sock *sk, struct sctp_endpoint *ep, + struct sctp_association *asoc) +{ + sctp_bh_unlock_sock(sk); + sock_put(sk); + if (asoc) + sctp_association_put(asoc); + if (ep) + sctp_endpoint_put(ep); +} + +/* + * 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 + * it's just the icmp type << 8 | icmp code. After adjustment + * header points to the first 8 bytes of the sctp header. We need + * to find the appropriate port. + * + * The locking strategy used here is very "optimistic". When + * someone else accesses the socket the ICMP is just dropped + * and for some paths there is no check at all. + * A more general error queue to queue errors for later handling + * is probably better. + * + */ +void sctp_v4_err(struct sk_buff *skb, __u32 info) +{ + struct iphdr *iph = (struct iphdr *)skb->data; + struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2)); + int type = skb->h.icmph->type; + int code = skb->h.icmph->code; + struct sock *sk; + struct sctp_endpoint *ep; + struct sctp_association *asoc; + struct sctp_transport *transport; + struct inet_opt *inet; + char *saveip, *savesctp; + int err; + + if (skb->len < ((iph->ihl << 2) + 8)) { + ICMP_INC_STATS_BH(IcmpInErrors); + return; + } + + /* Fix up skb to look at the embedded net header. */ + saveip = skb->nh.raw; + savesctp = skb->h.raw; + skb->nh.iph = iph; + skb->h.raw = (char *)sh; + sk = sctp_err_lookup(AF_INET, skb, sh, &ep, &asoc, &transport); + /* Put back, the original pointers. */ + skb->nh.raw = saveip; + skb->h.raw = savesctp; + if (!sk) { + ICMP_INC_STATS_BH(IcmpInErrors); + return; + } + /* Warning: The sock lock is held. Remember to call + * sctp_err_finish! + */ + + switch (type) { + case ICMP_PARAMETERPROB: + err = EPROTO; + break; + case ICMP_DEST_UNREACH: + if (code > NR_ICMP_UNREACH) + goto out_unlock; + + /* PMTU discovery (RFC1191) */ + if (ICMP_FRAG_NEEDED == code) { + sctp_icmp_frag_needed(sk, asoc, transport, info); + goto out_unlock; + } + + err = icmp_err_convert[code].errno; + break; + case ICMP_TIME_EXCEEDED: + /* Ignore any time exceeded errors due to fragment reassembly + * timeouts. + */ + if (ICMP_EXC_FRAGTIME == code) + goto out_unlock; + + err = EHOSTUNREACH; + break; + default: + goto out_unlock; + } + + inet = inet_sk(sk); + if (!sock_owned_by_user(sk) && inet->recverr) { + sk->err = err; + sk->error_report(sk); + } else { /* Only an error on timeout */ + sk->err_soft = err; + } + +out_unlock: + sctp_err_finish(sk, ep, asoc); +} + +/* + * RFC 2960, 8.4 - Handle "Out of the blue" Packets. + * + * This function scans all the chunks in the OOTB packet to determine if + * the packet should be discarded right away. If a response might be needed + * for this packet, or, if further processing is possible, the packet will + * be queued to a proper inqueue for the next phase of handling. + * + * Output: + * Return 0 - If further processing is needed. + * Return 1 - If the packet can be discarded right away. + */ +int sctp_rcv_ootb(struct sk_buff *skb) +{ + sctp_chunkhdr_t *ch; + __u8 *ch_end; + sctp_errhdr_t *err; + + ch = (sctp_chunkhdr_t *) skb->data; + + /* Scan through all the chunks in the packet. */ + do { + ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); + + /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the + * receiver MUST silently discard the OOTB packet and take no + * further action. + */ + if (SCTP_CID_ABORT == ch->type) + goto discard; + + /* RFC 8.4, 6) If the packet contains a SHUTDOWN COMPLETE + * chunk, the receiver should silently discard the packet + * and take no further action. + */ + if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type) + goto discard; + + /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR + * or a COOKIE ACK the SCTP Packet should be silently + * discarded. + */ + if (SCTP_CID_COOKIE_ACK == ch->type) + goto discard; + + if (SCTP_CID_ERROR == ch->type) { + err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t)); + if (SCTP_ERROR_STALE_COOKIE == err->cause) + goto discard; + } + + ch = (sctp_chunkhdr_t *) ch_end; + } while (ch_end < skb->tail); + + return 0; + +discard: + return 1; +} + +/* Insert endpoint into the hash table. */ +void __sctp_hash_endpoint(struct sctp_endpoint *ep) +{ + struct sctp_ep_common **epp; + struct sctp_ep_common *epb; + sctp_hashbucket_t *head; + + epb = &ep->base; + + epb->hashent = sctp_ep_hashfn(epb->bind_addr.port); + head = &sctp_proto.ep_hashbucket[epb->hashent]; + + sctp_write_lock(&head->lock); + epp = &head->chain; + epb->next = *epp; + if (epb->next) + (*epp)->pprev = &epb->next; + *epp = epb; + epb->pprev = epp; + sctp_write_unlock(&head->lock); +} + +/* Add an endpoint to the hash. Local BH-safe. */ +void sctp_hash_endpoint(struct sctp_endpoint *ep) +{ + sctp_local_bh_disable(); + __sctp_hash_endpoint(ep); + sctp_local_bh_enable(); +} + +/* Remove endpoint from the hash table. */ +void __sctp_unhash_endpoint(struct sctp_endpoint *ep) +{ + sctp_hashbucket_t *head; + struct sctp_ep_common *epb; + + epb = &ep->base; + + epb->hashent = sctp_ep_hashfn(epb->bind_addr.port); + + head = &sctp_proto.ep_hashbucket[epb->hashent]; + + sctp_write_lock(&head->lock); + + if (epb->pprev) { + if (epb->next) + epb->next->pprev = epb->pprev; + *epb->pprev = epb->next; + epb->pprev = NULL; + } + + sctp_write_unlock(&head->lock); +} + +/* Remove endpoint from the hash. Local BH-safe. */ +void sctp_unhash_endpoint(struct sctp_endpoint *ep) +{ + sctp_local_bh_disable(); + __sctp_unhash_endpoint(ep); + sctp_local_bh_enable(); +} + +/* Look up an endpoint. */ +struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr) +{ + sctp_hashbucket_t *head; + struct sctp_ep_common *epb; + struct sctp_endpoint *ep; + int hash; + + hash = sctp_ep_hashfn(laddr->v4.sin_port); + head = &sctp_proto.ep_hashbucket[hash]; + read_lock(&head->lock); + for (epb = head->chain; epb; epb = epb->next) { + ep = sctp_ep(epb); + if (sctp_endpoint_is_match(ep, laddr)) + goto hit; + } + + ep = sctp_sk((sctp_get_ctl_sock()))->ep; + epb = &ep->base; + +hit: + sctp_endpoint_hold(ep); + sock_hold(epb->sk); + read_unlock(&head->lock); + return ep; +} + +/* Add an association to the hash. Local BH-safe. */ +void sctp_hash_established(struct sctp_association *asoc) +{ + sctp_local_bh_disable(); + __sctp_hash_established(asoc); + sctp_local_bh_enable(); +} + +/* Insert association into the hash table. */ +void __sctp_hash_established(struct sctp_association *asoc) +{ + struct sctp_ep_common **epp; + struct sctp_ep_common *epb; + sctp_hashbucket_t *head; + + epb = &asoc->base; + + /* Calculate which chain this entry will belong to. */ + epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, asoc->peer.port); + + head = &sctp_proto.assoc_hashbucket[epb->hashent]; + + sctp_write_lock(&head->lock); + epp = &head->chain; + epb->next = *epp; + if (epb->next) + (*epp)->pprev = &epb->next; + *epp = epb; + epb->pprev = epp; + sctp_write_unlock(&head->lock); +} + +/* Remove association from the hash table. Local BH-safe. */ +void sctp_unhash_established(struct sctp_association *asoc) +{ + sctp_local_bh_disable(); + __sctp_unhash_established(asoc); + sctp_local_bh_enable(); +} + +/* Remove association from the hash table. */ +void __sctp_unhash_established(struct sctp_association *asoc) +{ + sctp_hashbucket_t *head; + struct sctp_ep_common *epb; + + epb = &asoc->base; + + epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, + asoc->peer.port); + + head = &sctp_proto.assoc_hashbucket[epb->hashent]; + + sctp_write_lock(&head->lock); + + if (epb->pprev) { + if (epb->next) + epb->next->pprev = epb->pprev; + *epb->pprev = epb->next; + epb->pprev = NULL; + } + + sctp_write_unlock(&head->lock); +} + +/* Look up an association. */ +struct sctp_association *__sctp_lookup_association( + const union sctp_addr *local, + const union sctp_addr *peer, + struct sctp_transport **pt) +{ + sctp_hashbucket_t *head; + struct sctp_ep_common *epb; + struct sctp_association *asoc; + struct sctp_transport *transport; + int hash; + + /* Optimize here for direct hit, only listening connections can + * have wildcards anyways. + */ + hash = sctp_assoc_hashfn(local->v4.sin_port, peer->v4.sin_port); + head = &sctp_proto.assoc_hashbucket[hash]; + read_lock(&head->lock); + for (epb = head->chain; epb; epb = epb->next) { + asoc = sctp_assoc(epb); + transport = sctp_assoc_is_match(asoc, local, peer); + if (transport) + goto hit; + } + + read_unlock(&head->lock); + + return NULL; + +hit: + *pt = transport; + sctp_association_hold(asoc); + sock_hold(epb->sk); + read_unlock(&head->lock); + return asoc; +} + +/* Look up an association. BH-safe. */ +struct sctp_association *sctp_lookup_association(const union sctp_addr *laddr, + const union sctp_addr *paddr, + struct sctp_transport **transportp) +{ + struct sctp_association *asoc; + + sctp_local_bh_disable(); + asoc = __sctp_lookup_association(laddr, paddr, transportp); + sctp_local_bh_enable(); + + return asoc; +} + +/* Is there an association matching the given local and peer addresses? */ +int sctp_has_association(const union sctp_addr *laddr, + const union sctp_addr *paddr) +{ + struct sctp_association *asoc; + struct sctp_transport *transport; + + if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) { + sock_put(asoc->base.sk); + sctp_association_put(asoc); + return 1; + } + + return 0; +} + +/* + * SCTP Implementors Guide, 2.18 Handling of address + * parameters within the INIT or INIT-ACK. + * + * D) When searching for a matching TCB upon reception of an INIT + * or INIT-ACK chunk the receiver SHOULD use not only the + * source address of the packet (containing the INIT or + * INIT-ACK) but the receiver SHOULD also use all valid + * address parameters contained within the chunk. + * + * 2.18.3 Solution description + * + * This new text clearly specifies to an implementor the need + * to look within the INIT or INIT-ACK. Any implementation that + * does not do this, may not be able to establish associations + * in certain circumstances. + * + */ +static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, + const union sctp_addr *laddr, struct sctp_transport **transportp) +{ + struct sctp_association *asoc; + union sctp_addr addr; + union sctp_addr *paddr = &addr; + struct sctphdr *sh = (struct sctphdr *) skb->h.raw; + sctp_chunkhdr_t *ch; + union sctp_params params; + sctp_init_chunk_t *init; + + ch = (sctp_chunkhdr_t *) skb->data; + + /* If this is INIT/INIT-ACK look inside the chunk too. */ + switch (ch->type) { + case SCTP_CID_INIT: + case SCTP_CID_INIT_ACK: + break; + default: + return NULL; + } + + /* + * This code will NOT touch anything inside the chunk--it is + * strictly READ-ONLY. + * + * RFC 2960 3 SCTP packet Format + * + * Multiple chunks can be bundled into one SCTP packet up to + * the MTU size, except for the INIT, INIT ACK, and SHUTDOWN + * COMPLETE chunks. These chunks MUST NOT be bundled with any + * other chunk in a packet. See Section 6.10 for more details + * on chunk bundling. + */ + + /* Find the start of the TLVs and the end of the chunk. This is + * the region we search for address parameters. + */ + init = (sctp_init_chunk_t *)skb->data; + + /* Walk the parameters looking for embedded addresses. */ + sctp_walk_params(params, init, init_hdr.params) { + + /* Note: Ignoring hostname addresses. */ + if ((SCTP_PARAM_IPV4_ADDRESS != params.p->type) && + (SCTP_PARAM_IPV6_ADDRESS != params.p->type)) + continue; + + sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source), 0); + asoc = __sctp_lookup_association(laddr, paddr, transportp); + if (asoc) + return asoc; + } + + return NULL; +} + +/* Lookup an association for an inbound skb. */ +struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, + const union sctp_addr *paddr, + const union sctp_addr *laddr, + struct sctp_transport **transportp) +{ + struct sctp_association *asoc; + + asoc = __sctp_lookup_association(laddr, paddr, transportp); + + /* Further lookup for INIT/INIT-ACK packets. + * SCTP Implementors Guide, 2.18 Handling of address + * parameters within the INIT or INIT-ACK. + */ + if (!asoc) + asoc = __sctp_rcv_init_lookup(skb, laddr, transportp); + + return asoc; +} diff -urN linux-2.4.22-bk23/net/sctp/inqueue.c linux-2.4.22-bk24/net/sctp/inqueue.c --- linux-2.4.22-bk23/net/sctp/inqueue.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/inqueue.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,199 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2002 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions are the methods for accessing the SCTP inqueue. + * + * An SCTP inqueue is a queue into which you push SCTP packets + * (which might be bundles or fragments of chunks) and out of which you + * pop SCTP whole chunks. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include + +/* Initialize an SCTP inqueue. */ +void sctp_inq_init(struct sctp_inq *queue) +{ + skb_queue_head_init(&queue->in); + queue->in_progress = NULL; + + /* Create a task for delivering data. */ + INIT_LIST_HEAD(&queue->immediate.list); + queue->immediate.sync = 0; + queue->immediate.routine = NULL; + queue->immediate.data = NULL; + + queue->malloced = 0; +} + +/* Create an initialized sctp_inq. */ +struct sctp_inq *sctp_inq_new(void) +{ + struct sctp_inq *retval; + + retval = t_new(struct sctp_inq, GFP_ATOMIC); + if (retval) { + sctp_inq_init(retval); + retval->malloced = 1; + } + return retval; +} + +/* Release the memory associated with an SCTP inqueue. */ +void sctp_inq_free(struct sctp_inq *queue) +{ + sctp_chunk_t *chunk; + + /* Empty the queue. */ + while ((chunk = (sctp_chunk_t *) skb_dequeue(&queue->in))) + sctp_free_chunk(chunk); + + /* If there is a packet which is currently being worked on, + * free it as well. + */ + if (queue->in_progress) + sctp_free_chunk(queue->in_progress); + + if (queue->malloced) { + /* Dump the master memory segment. */ + kfree(queue); + } +} + +/* Put a new packet in an SCTP inqueue. + * We assume that packet->sctp_hdr is set and in host byte order. + */ +void sctp_inq_push(struct sctp_inq *q, sctp_chunk_t *packet) +{ + /* Directly call the packet handling routine. */ + + /* We are now calling this either from the soft interrupt + * or from the backlog processing. + * Eventually, we should clean up inqueue to not rely + * on the BH related data structures. + */ + skb_queue_tail(&(q->in), (struct sk_buff *) packet); + q->immediate.routine(q->immediate.data); +} + +/* Extract a chunk from an SCTP inqueue. + * + * WARNING: If you need to put the chunk on another queue, you need to + * make a shallow copy (clone) of it. + */ +sctp_chunk_t *sctp_inq_pop(struct sctp_inq *queue) +{ + sctp_chunk_t *chunk; + sctp_chunkhdr_t *ch = NULL; + + /* The assumption is that we are safe to process the chunks + * at this time. + */ + + if ((chunk = queue->in_progress)) { + /* There is a packet that we have been working on. + * Any post processing work to do before we move on? + */ + if (chunk->singleton || + chunk->end_of_packet || + chunk->pdiscard) { + sctp_free_chunk(chunk); + chunk = queue->in_progress = NULL; + } else { + /* Nothing to do. Next chunk in the packet, please. */ + ch = (sctp_chunkhdr_t *) chunk->chunk_end; + + /* Force chunk->skb->data to chunk->chunk_end. */ + skb_pull(chunk->skb, + chunk->chunk_end - chunk->skb->data); + } + } + + /* Do we need to take the next packet out of the queue to process? */ + if (!chunk) { + /* Is the queue empty? */ + if (skb_queue_empty(&queue->in)) + return NULL; + + chunk = queue->in_progress = + (sctp_chunk_t *) skb_dequeue(&queue->in); + + /* This is the first chunk in the packet. */ + chunk->singleton = 1; + ch = (sctp_chunkhdr_t *) chunk->skb->data; + } + + chunk->chunk_hdr = ch; + chunk->chunk_end = ((__u8 *) ch) + + WORD_ROUND(ntohs(ch->length)); + skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); + chunk->subh.v = NULL; /* Subheader is no longer valid. */ + + if (chunk->chunk_end < chunk->skb->tail) { + /* This is not a singleton */ + chunk->singleton = 0; + } else { + /* We are at the end of the packet, so mark the chunk + * in case we need to send a SACK. + */ + chunk->end_of_packet = 1; + } + + SCTP_DEBUG_PRINTK("+++sctp_inq_pop+++ chunk %p[%s]," + " length %d, skb->len %d\n",chunk, + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)), + ntohs(chunk->chunk_hdr->length), chunk->skb->len); + return chunk; +} + +/* Set a top-half handler. + * + * Originally, we the top-half handler was scheduled as a BH. We now + * call the handler directly in sctp_inq_push() at a time that + * we know we are lock safe. + * The intent is that this routine will pull stuff out of the + * inqueue and process it. + */ +void sctp_inq_set_th_handler(struct sctp_inq *q, + void (*callback)(void *), void *arg) +{ + q->immediate.routine = callback; + q->immediate.data = arg; +} + diff -urN linux-2.4.22-bk23/net/sctp/ipv6.c linux-2.4.22-bk24/net/sctp/ipv6.c --- linux-2.4.22-bk23/net/sctp/ipv6.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/ipv6.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,868 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * Copyright (c) 2002-2003 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * SCTP over IPv6. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Le Yanqun + * Hui Huang + * La Monte H.P. Yarroll + * Sridhar Samudrala + * Jon Grimm + * + * Based on: + * linux/net/ipv6/tcp_ipv6.c + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern struct notifier_block sctp_inetaddr_notifier; + +/* FIXME: This macro needs to be moved to a common header file. */ +#define NIP6(addr) \ + ntohs((addr)->s6_addr16[0]), \ + ntohs((addr)->s6_addr16[1]), \ + ntohs((addr)->s6_addr16[2]), \ + ntohs((addr)->s6_addr16[3]), \ + ntohs((addr)->s6_addr16[4]), \ + ntohs((addr)->s6_addr16[5]), \ + ntohs((addr)->s6_addr16[6]), \ + ntohs((addr)->s6_addr16[7]) + +/* ICMP error handler. */ +void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + struct inet6_dev *idev; + struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; + struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); + struct sock *sk; + struct sctp_endpoint *ep; + struct sctp_association *asoc; + struct sctp_transport *transport; + struct ipv6_pinfo *np; + char *saveip, *savesctp; + int err; + + idev = in6_dev_get(skb->dev); + + /* Fix up skb to look at the embedded net header. */ + saveip = skb->nh.raw; + savesctp = skb->h.raw; + skb->nh.ipv6h = iph; + skb->h.raw = (char *)sh; + sk = sctp_err_lookup(AF_INET6, skb, sh, &ep, &asoc, &transport); + /* Put back, the original pointers. */ + skb->nh.raw = saveip; + skb->h.raw = savesctp; + if (!sk) { + ICMP6_INC_STATS_BH(Icmp6InErrors); + goto out; + } + + /* Warning: The sock lock is held. Remember to call + * sctp_err_finish! + */ + + switch (type) { + case ICMPV6_PKT_TOOBIG: + sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info)); + goto out_unlock; + default: + break; + } + + np = inet6_sk(sk); + icmpv6_err_convert(type, code, &err); + if (!sock_owned_by_user(sk) && np->recverr) { + sk->err = err; + sk->error_report(sk); + } else { /* Only an error on timeout */ + sk->err_soft = err; + } + +out_unlock: + sctp_err_finish(sk, ep, asoc); +out: + if (likely(idev != NULL)) + in6_dev_put(idev); +} + +/* Based on tcp_v6_xmit() in tcp_ipv6.c. */ +static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, + int ipfragok) +{ + struct sock *sk = skb->sk; + struct ipv6_pinfo *np = inet6_sk(sk); + struct flowi fl; + + fl.proto = sk->protocol; + + /* Fill in the dest address from the route entry passed with the skb + * and the source address from the transport. + */ + fl.fl6_dst = &transport->ipaddr.v6.sin6_addr; + fl.fl6_src = &transport->saddr.v6.sin6_addr; + + fl.fl6_flowlabel = np->flow_label; + IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); + if (ipv6_addr_type(fl.fl6_src) & IPV6_ADDR_LINKLOCAL) + fl.oif = transport->saddr.v6.sin6_scope_id; + else + fl.oif = sk->bound_dev_if; + fl.uli_u.ports.sport = (sk)->sport; + fl.uli_u.ports.dport = transport->ipaddr.v6.sin6_port; + + if (np->opt && np->opt->srcrt) { + struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; + fl.nl_u.ip6_u.daddr = rt0->addr; + } + + SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, " + "src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " + "dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + __FUNCTION__, skb, skb->len, NIP6(fl.fl6_src), + NIP6(fl.fl6_dst)); + + SCTP_INC_STATS(SctpOutSCTPPacks); + + return ip6_xmit(sk, skb, &fl, np->opt); +} + +/* Returns the dst cache entry for the given source and destination ip + * addresses. + */ +struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, + union sctp_addr *daddr, + union sctp_addr *saddr) +{ + struct dst_entry *dst; + struct flowi fl = { + .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } }; + + SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", + __FUNCTION__, NIP6(fl.fl6_dst)); + + if (saddr) { + fl.fl6_src = &saddr->v6.sin6_addr; + SCTP_DEBUG_PRINTK( + "SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ", + NIP6(fl.fl6_src)); + } + + dst = ip6_route_output(NULL, &fl); + if (dst) { + struct rt6_info *rt; + rt = (struct rt6_info *)dst; + SCTP_DEBUG_PRINTK( + "rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " + "rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr)); + } else { + SCTP_DEBUG_PRINTK("NO ROUTE\n"); + } + + return dst; +} + +/* Returns the number of consecutive initial bits that match in the 2 ipv6 + * addresses. + */ +static inline int sctp_v6_addr_match_len(union sctp_addr *s1, + union sctp_addr *s2) +{ + struct in6_addr *a1 = &s1->v6.sin6_addr; + struct in6_addr *a2 = &s2->v6.sin6_addr; + int i, j; + + for (i = 0; i < 4 ; i++) { + __u32 a1xora2; + + a1xora2 = a1->s6_addr32[i] ^ a2->s6_addr32[i]; + + if ((j = fls(ntohl(a1xora2)))) + return (i * 32 + 32 - j); + } + + return (i*32); +} + +/* Fills in the source address(saddr) based on the destination address(daddr) + * and asoc's bind address list. + */ +void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst, + union sctp_addr *daddr, union sctp_addr *saddr) +{ + sctp_bind_addr_t *bp; + rwlock_t *addr_lock; + struct sockaddr_storage_list *laddr; + struct list_head *pos; + sctp_scope_t scope; + union sctp_addr *baddr = NULL; + __u8 matchlen = 0; + __u8 bmatchlen; + + SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p " + "daddr:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", + __FUNCTION__, asoc, dst, NIP6(&daddr->v6.sin6_addr)); + + if (!asoc) { + ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr); + SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + NIP6(&saddr->v6.sin6_addr)); + return; + } + + scope = sctp_scope(daddr); + + bp = &asoc->base.bind_addr; + addr_lock = &asoc->base.addr_lock; + + /* Go through the bind address list and find the best source address + * that matches the scope of the destination address. + */ + sctp_read_lock(addr_lock); + list_for_each(pos, &bp->address_list) { + laddr = list_entry(pos, struct sockaddr_storage_list, list); + if ((laddr->a.sa.sa_family == AF_INET6) && + (scope <= sctp_scope(&laddr->a))) { + bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); + if (!baddr || (matchlen < bmatchlen)) { + baddr = &laddr->a; + matchlen = bmatchlen; + } + } + } + + if (baddr) { + memcpy(saddr, baddr, sizeof(union sctp_addr)); + SCTP_DEBUG_PRINTK("saddr: " + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + NIP6(&saddr->v6.sin6_addr)); + } else { + printk(KERN_ERR "%s: asoc:%p Could not find a valid source " + "address for the " + "dest:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + __FUNCTION__, asoc, NIP6(&daddr->v6.sin6_addr)); + } + + sctp_read_unlock(addr_lock); +} + +/* Make a copy of all potential local addresses. */ +static void sctp_v6_copy_addrlist(struct list_head *addrlist, + struct net_device *dev) +{ + struct inet6_dev *in6_dev; + struct inet6_ifaddr *ifp; + struct sockaddr_storage_list *addr; + + read_lock(&addrconf_lock); + if ((in6_dev = __in6_dev_get(dev)) == NULL) { + read_unlock(&addrconf_lock); + return; + } + + read_lock(&in6_dev->lock); + for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) { + /* Add the address to the local list. */ + addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC); + if (addr) { + addr->a.v6.sin6_family = AF_INET6; + addr->a.v6.sin6_port = 0; + addr->a.v6.sin6_addr = ifp->addr; + addr->a.v6.sin6_scope_id = dev->ifindex; + INIT_LIST_HEAD(&addr->list); + list_add_tail(&addr->list, addrlist); + } + } + + read_unlock(&in6_dev->lock); + read_unlock(&addrconf_lock); +} + +/* Initialize a sockaddr_storage from in incoming skb. */ +static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb, + int is_saddr) +{ + void *from; + __u16 *port; + struct sctphdr *sh; + + port = &addr->v6.sin6_port; + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_flowinfo = 0; /* FIXME */ + addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif; + + sh = (struct sctphdr *) skb->h.raw; + if (is_saddr) { + *port = ntohs(sh->source); + from = &skb->nh.ipv6h->saddr; + } else { + *port = ntohs(sh->dest); + from = &skb->nh.ipv6h->daddr; + } + ipv6_addr_copy(&addr->v6.sin6_addr, from); +} + +/* Initialize an sctp_addr from a socket. */ +static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk) +{ + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_port = (sk)->num; + addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr; +} + +/* Initialize sk->rcv_saddr from sctp_addr. */ +static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk) +{ + inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr; +} + +/* Initialize sk->daddr from sctp_addr. */ +static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk) +{ + inet6_sk(sk)->daddr = addr->v6.sin6_addr; +} + +/* Initialize a sctp_addr from a dst_entry. */ +static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, + unsigned short port) +{ + struct rt6_info *rt = (struct rt6_info *)dst; + addr->sa.sa_family = AF_INET6; + addr->v6.sin6_port = port; + ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); +} + +/* Compare addresses exactly. + * FIXME: v4-mapped-v6. + */ +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2) +{ + if (addr1->sa.sa_family != addr2->sa.sa_family) + return 0; + if (ipv6_addr_cmp(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr)) + return 0; + /* If this is a linklocal address, compare the scope_id. */ + if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { + if (addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id && + (addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)) { + return 0; + } + } + + return 1; +} + +/* Initialize addr struct to INADDR_ANY. */ +static void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port) +{ + memset(addr, 0x00, sizeof(union sctp_addr)); + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_port = port; +} + +/* Is this a wildcard address? */ +static int sctp_v6_is_any(const union sctp_addr *addr) +{ + int type; + type = ipv6_addr_type((struct in6_addr *)&addr->v6.sin6_addr); + return IPV6_ADDR_ANY == type; +} + +/* Should this be available for binding? */ +static int sctp_v6_available(const union sctp_addr *addr) +{ + int type; + struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; + + type = ipv6_addr_type(in6); + if (IPV6_ADDR_ANY == type) + return 1; + if (!(type & IPV6_ADDR_UNICAST)) + return 0; + + return ipv6_chk_addr(in6, NULL); +} + +/* This function checks if the address is a valid address to be used for + * SCTP. + * + * Output: + * Return 0 - If the address is a non-unicast or an illegal address. + * Return 1 - If the address is a unicast. + */ +static int sctp_v6_addr_valid(union sctp_addr *addr) +{ + int ret = ipv6_addr_type(&addr->v6.sin6_addr); + + /* FIXME: v4-mapped-v6 address support. */ + + /* Is this a non-unicast address */ + if (!(ret & IPV6_ADDR_UNICAST)) + return 0; + + return 1; +} + +/* What is the scope of 'addr'? */ +static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) +{ + int v6scope; + sctp_scope_t retval; + + /* The IPv6 scope is really a set of bit fields. + * See IFA_* in . Map to a generic SCTP scope. + */ + + v6scope = ipv6_addr_scope(&addr->v6.sin6_addr); + switch (v6scope) { + case IFA_HOST: + retval = SCTP_SCOPE_LOOPBACK; + break; + case IFA_LINK: + retval = SCTP_SCOPE_LINK; + break; + case IFA_SITE: + retval = SCTP_SCOPE_PRIVATE; + break; + default: + retval = SCTP_SCOPE_GLOBAL; + break; + }; + + return retval; +} + +/* Create and initialize a new sk for the socket to be returned by accept(). */ +struct sock *sctp_v6_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) +{ + struct sock *newsk; + struct inet_opt *newinet; + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); + + newsk = sk_alloc(PF_INET6, GFP_KERNEL, sizeof(struct sock)); + if (!newsk) + goto out; + + sock_init_data(NULL, newsk); + sk_set_owner(newsk, THIS_MODULE); + + newsk->type = SOCK_STREAM; + + newsk->prot = sk->prot; + newsk->no_check = sk->no_check; + newsk->reuse = sk->reuse; + + newsk->destruct = inet_sock_destruct; + newsk->zapped = 0; + newsk->family = PF_INET6; + newsk->protocol = IPPROTO_SCTP; + newsk->backlog_rcv = sk->prot->backlog_rcv; + newsk->shutdown = sk->shutdown; + + newinet = inet_sk(newsk); + newnp = inet6_sk(newsk); + + memcpy(newnp, np, sizeof(struct ipv6_pinfo)); + + /* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname() + * and getpeername(). + */ + newsk->sport = sk->sport; + newnp->saddr = np->saddr; + newnp->rcv_saddr = np->rcv_saddr; + newsk->dport = htons(asoc->peer.port); + newnp->daddr = asoc->peer.primary_addr.v6.sin6_addr; + +#ifdef INET_REFCNT_DEBUG + atomic_inc(&inet6_sock_nr); + atomic_inc(&inet_sock_nr); +#endif + + if (0 != newsk->prot->init(newsk)) { + inet_sock_release(newsk); + newsk = NULL; + } + +out: + return newsk; +} + +/* Where did this skb come from? */ +static int sctp_v6_skb_iif(const struct sk_buff *skb) +{ + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + return opt->iif; +} + +/* Initialize a PF_INET6 socket msg_name. */ +static void sctp_inet6_msgname(char *msgname, int *addr_len) +{ + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)msgname; + sin6->sin6_family = AF_INET6; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; /*FIXME */ + *addr_len = sizeof(struct sockaddr_in6); +} + +/* Initialize a PF_INET msgname from a ulpevent. */ +static void sctp_inet6_event_msgname(struct sctp_ulpevent *event, + char *msgname, int *addrlen) +{ + struct sockaddr_in6 *sin6, *sin6from; + + if (msgname) { + union sctp_addr *addr; + + sctp_inet6_msgname(msgname, addrlen); + sin6 = (struct sockaddr_in6 *)msgname; + sin6->sin6_port = htons(event->event_asoc->peer.port); + addr = &event->event_asoc->peer.primary_addr; + + /* Note: If we go to a common v6 format, this code + * will change. + */ + + /* Map ipv4 address into v4-mapped-on-v6 address. */ + if (AF_INET == addr->sa.sa_family) { + /* FIXME: Easy, but there was no way to test this + * yet. + */ + return; + } + + sin6from = &event->event_asoc->peer.primary_addr.v6; + ipv6_addr_copy(&sin6->sin6_addr, &sin6from->sin6_addr); + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) + sin6->sin6_scope_id = sin6from->sin6_scope_id; + } +} + +/* Initialize a msg_name from an inbound skb. */ +static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, + int *addr_len) +{ + struct sctphdr *sh; + struct sockaddr_in6 *sin6; + + if (msgname) { + sctp_inet6_msgname(msgname, addr_len); + sin6 = (struct sockaddr_in6 *)msgname; + sh = (struct sctphdr *)skb->h.raw; + sin6->sin6_port = sh->source; + + /* FIXME: Map ipv4 address into v4-mapped-on-v6 address. */ + if (__constant_htons(ETH_P_IP) == skb->protocol) { + /* FIXME: The latest I-D added options for two + * behaviors. + */ + return; + } + + /* Otherwise, just copy the v6 address. */ + + ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr); + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + struct sctp_ulpevent *ev = sctp_skb2event(skb); + sin6->sin6_scope_id = ev->iif; + } + } +} + +/* Do we support this AF? */ +static int sctp_inet6_af_supported(sa_family_t family) +{ + /* FIXME: v4-mapped-v6 addresses. The I-D is still waffling + * on what to do with sockaddr formats for PF_INET6 sockets. + * For now assume we'll support both. + */ + switch (family) { + case AF_INET6: + case AF_INET: + return 1; + default: + return 0; + } +} + +/* Address matching with wildcards allowed. This extra level + * of indirection lets us choose whether a PF_INET6 should + * disallow any v4 addresses if we so choose. + */ +static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2, + struct sctp_opt *opt) +{ + struct sctp_af *af1, *af2; + + af1 = sctp_get_af_specific(addr1->sa.sa_family); + af2 = sctp_get_af_specific(addr2->sa.sa_family); + + if (!af1 || !af2) + return 0; + /* Today, wildcard AF_INET/AF_INET6. */ + if (sctp_is_any(addr1) || sctp_is_any(addr2)) + return 1; + + if (addr1->sa.sa_family != addr2->sa.sa_family) + return 0; + + return af1->cmp_addr(addr1, addr2); +} + +/* Verify that the provided sockaddr looks bindable. Common verification, + * has already been taken care of. + */ +static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + struct sctp_af *af; + + /* ASSERT: address family has already been verified. */ + if (addr->sa.sa_family != AF_INET6) + af = sctp_get_af_specific(addr->sa.sa_family); + else { + struct sock *sk; + int type = ipv6_addr_type(&addr->v6.sin6_addr); + sk = container_of(opt, struct sock, tp_pinfo.af_sctp); + if (type & IPV6_ADDR_LINKLOCAL) { + /* Note: Behavior similar to af_inet6.c: + * 1) Overrides previous bound_dev_if + * 2) Destructive even if bind isn't successful. + */ + + if (addr->v6.sin6_scope_id) + sk->bound_dev_if = addr->v6.sin6_scope_id; + if (!sk->bound_dev_if) + return 0; + } + af = opt->pf->af; + } + return af->available(addr); +} + +/* Verify that the provided sockaddr looks bindable. Common verification, + * has already been taken care of. + */ +static int sctp_inet6_send_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + struct sctp_af *af = NULL; + + /* ASSERT: address family has already been verified. */ + if (addr->sa.sa_family != AF_INET6) + af = sctp_get_af_specific(addr->sa.sa_family); + else { + struct sock *sk; + int type = ipv6_addr_type(&addr->v6.sin6_addr); + sk = container_of(opt, struct sock, tp_pinfo.af_sctp); + if (type & IPV6_ADDR_LINKLOCAL) { + /* Note: Behavior similar to af_inet6.c: + * 1) Overrides previous bound_dev_if + * 2) Destructive even if bind isn't successful. + */ + + if (addr->v6.sin6_scope_id) + sk->bound_dev_if = addr->v6.sin6_scope_id; + if (!sk->bound_dev_if) + return 0; + } + af = opt->pf->af; + } + + return af != NULL; +} + +/* Fill in Supported Address Type information for INIT and INIT-ACK + * chunks. Note: In the future, we may want to look at sock options + * to determine whether a PF_INET6 socket really wants to have IPV4 + * addresses. + * Returns number of addresses supported. + */ +static int sctp_inet6_supported_addrs(const struct sctp_opt *opt, + __u16 *types) +{ + types[0] = SCTP_PARAM_IPV4_ADDRESS; + types[1] = SCTP_PARAM_IPV6_ADDRESS; + return 2; +} + +static struct proto_ops inet6_seqpacket_ops = { + .family = PF_INET6, + .release = inet6_release, + .bind = inet6_bind, + .connect = inet_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = inet_accept, + .getname = inet6_getname, + .poll = sctp_poll, + .ioctl = inet6_ioctl, + .listen = sctp_inet_listen, + .shutdown = inet_shutdown, + .setsockopt = inet_setsockopt, + .getsockopt = inet_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = inet_recvmsg, + .mmap = sock_no_mmap, +}; + +static struct inet_protosw sctpv6_seqpacket_protosw = { + .type = SOCK_SEQPACKET, + .protocol = IPPROTO_SCTP, + .prot = &sctp_prot, + .ops = &inet6_seqpacket_ops, + .capability = -1, + .no_check = 0, + .flags = SCTP_PROTOSW_FLAG +}; +static struct inet_protosw sctpv6_stream_protosw = { + .type = SOCK_STREAM, + .protocol = IPPROTO_SCTP, + .prot = &sctp_prot, + .ops = &inet6_seqpacket_ops, + .capability = -1, + .no_check = 0, + .flags = SCTP_PROTOSW_FLAG, +}; + +static struct inet6_protocol sctpv6_protocol = { + .handler = sctp_rcv, + .err_handler = sctp_v6_err, + .next = NULL, + .protocol = IPPROTO_SCTP, + .copy = 0, + .data = NULL, + .name = "SCTPv6", +}; + +static struct sctp_af sctp_ipv6_specific = { + .sctp_xmit = sctp_v6_xmit, + .setsockopt = ipv6_setsockopt, + .getsockopt = ipv6_getsockopt, + .get_dst = sctp_v6_get_dst, + .get_saddr = sctp_v6_get_saddr, + .copy_addrlist = sctp_v6_copy_addrlist, + .from_skb = sctp_v6_from_skb, + .from_sk = sctp_v6_from_sk, + .to_sk_saddr = sctp_v6_to_sk_saddr, + .to_sk_daddr = sctp_v6_to_sk_daddr, + .dst_saddr = sctp_v6_dst_saddr, + .cmp_addr = sctp_v6_cmp_addr, + .scope = sctp_v6_scope, + .addr_valid = sctp_v6_addr_valid, + .inaddr_any = sctp_v6_inaddr_any, + .is_any = sctp_v6_is_any, + .available = sctp_v6_available, + .skb_iif = sctp_v6_skb_iif, + .net_header_len = sizeof(struct ipv6hdr), + .sockaddr_len = sizeof(struct sockaddr_in6), + .sa_family = AF_INET6, +}; + +static struct sctp_pf sctp_pf_inet6_specific = { + .event_msgname = sctp_inet6_event_msgname, + .skb_msgname = sctp_inet6_skb_msgname, + .af_supported = sctp_inet6_af_supported, + .cmp_addr = sctp_inet6_cmp_addr, + .bind_verify = sctp_inet6_bind_verify, + .send_verify = sctp_inet6_send_verify, + .supported_addrs = sctp_inet6_supported_addrs, + .create_accept_sk = sctp_v6_create_accept_sk, + .af = &sctp_ipv6_specific, +}; + +/* Initialize IPv6 support and register with inet6 stack. */ +int sctp_v6_init(void) +{ + /* Register inet6 protocol. */ + inet6_add_protocol(&sctpv6_protocol); + + /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */ + inet6_register_protosw(&sctpv6_seqpacket_protosw); + inet6_register_protosw(&sctpv6_stream_protosw); + + /* Register the SCTP specific PF_INET6 functions. */ + sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); + + /* Register the SCTP specific AF_INET6 functions. */ + sctp_register_af(&sctp_ipv6_specific); + + /* Register notifier for inet6 address additions/deletions. */ + register_inet6addr_notifier(&sctp_inetaddr_notifier); + + return 0; +} + +/* IPv6 specific exit support. */ +void sctp_v6_exit(void) +{ + list_del(&sctp_ipv6_specific.list); + inet6_del_protocol(&sctpv6_protocol); + inet6_unregister_protosw(&sctpv6_seqpacket_protosw); + inet6_unregister_protosw(&sctpv6_stream_protosw); + unregister_inet6addr_notifier(&sctp_inetaddr_notifier); +} diff -urN linux-2.4.22-bk23/net/sctp/objcnt.c linux-2.4.22-bk24/net/sctp/objcnt.c --- linux-2.4.22-bk23/net/sctp/objcnt.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/objcnt.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,135 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 2001 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * Support for memory object debugging. This allows one to monitor the + * object allocations/deallocations for types instrumented for this + * via the proc fs. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include + +/* + * Global counters to count raw object allocation counts. + * To add new counters, choose a unique suffix for the variable + * name as the helper macros key off this suffix to make + * life easier for the programmer. + */ + +SCTP_DBG_OBJCNT(sock); +SCTP_DBG_OBJCNT(ep); +SCTP_DBG_OBJCNT(transport); +SCTP_DBG_OBJCNT(assoc); +SCTP_DBG_OBJCNT(bind_addr); +SCTP_DBG_OBJCNT(chunk); +SCTP_DBG_OBJCNT(addr); +SCTP_DBG_OBJCNT(ssnmap); + +/* An array to make it easy to pretty print the debug information + * to the proc fs. + */ +sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = { + SCTP_DBG_OBJCNT_ENTRY(sock), + SCTP_DBG_OBJCNT_ENTRY(ep), + SCTP_DBG_OBJCNT_ENTRY(assoc), + SCTP_DBG_OBJCNT_ENTRY(transport), + SCTP_DBG_OBJCNT_ENTRY(chunk), + SCTP_DBG_OBJCNT_ENTRY(bind_addr), + SCTP_DBG_OBJCNT_ENTRY(addr), + SCTP_DBG_OBJCNT_ENTRY(ssnmap), +}; + +/* Callback from procfs to read out objcount information. + * Walk through the entries in the sctp_dbg_objcnt array, dumping + * the raw object counts for each monitored type. + * + * This code was modified from similar code in route.c + */ +static int sctp_dbg_objcnt_read(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + int len = 0; + off_t pos = 0; + int entries; + int i; + char temp[128]; + + /* How many entries? */ + entries = sizeof(sctp_dbg_objcnt)/sizeof(sctp_dbg_objcnt[0]); + + /* Walk the entries and print out the debug information + * for proc fs. + */ + for (i = 0; i < entries; i++) { + pos += 128; + + /* Skip ahead. */ + if (pos <= offset) { + len = 0; + continue; + } + /* Print out each entry. */ + sprintf(temp, "%s: %d", + sctp_dbg_objcnt[i].label, + atomic_read(sctp_dbg_objcnt[i].counter)); + + sprintf(buffer + len, "%-127s\n", temp); + len += 128; + if (pos >= offset+length) + goto done; + } + +done: + *start = buffer + len - (pos - offset); + len = pos - offset; + if (len > length) + len = length; + + return len; +} + +/* Initialize the objcount in the proc filesystem. */ +void sctp_dbg_objcnt_init(void) +{ + create_proc_read_entry("sctp_dbg_objcnt", 0, proc_net_sctp, + sctp_dbg_objcnt_read, NULL); +} + +/* Cleanup the objcount entry in the proc filesystem. */ +void sctp_dbg_objcnt_exit(void) +{ + remove_proc_entry("sctp_dbg_objcount", proc_net_sctp); +} + + diff -urN linux-2.4.22-bk23/net/sctp/output.c linux-2.4.22-bk24/net/sctp/output.c --- linux-2.4.22-bk23/net/sctp/output.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/output.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,636 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions handle output processing. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Sridhar Samudrala + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TEST_FRAME +#include +#endif /* TEST_FRAME (not defined) */ + +#include /* for sa_family_t */ +#include + +#include +#include + +/* Forward declarations for private helpers. */ +static void sctp_packet_reset(struct sctp_packet *packet); +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk); + +/* Config a packet. + * This appears to be a followup set of initializations.) + */ +struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, + __u32 vtag, int ecn_capable, + sctp_packet_phandler_t *prepend_handler) +{ + int packet_empty = (packet->size == SCTP_IP_OVERHEAD); + + packet->vtag = vtag; + packet->ecn_capable = ecn_capable; + packet->get_prepend_chunk = prepend_handler; + packet->has_cookie_echo = 0; + packet->has_sack = 0; + packet->ipfragok = 0; + + /* We might need to call the prepend_handler right away. */ + if (packet_empty) + sctp_packet_reset(packet); + return packet; +} + +/* Initialize the packet structure. */ +struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, + struct sctp_transport *transport, + __u16 sport, __u16 dport) +{ + packet->transport = transport; + packet->source_port = sport; + packet->destination_port = dport; + skb_queue_head_init(&packet->chunks); + packet->vtag = 0; + packet->ecn_capable = 0; + packet->get_prepend_chunk = NULL; + packet->has_cookie_echo = 0; + packet->has_sack = 0; + packet->ipfragok = 0; + packet->malloced = 0; + sctp_packet_reset(packet); + return packet; +} + +/* Free a packet. */ +void sctp_packet_free(struct sctp_packet *packet) +{ + struct sctp_chunk *chunk; + + while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) + sctp_free_chunk(chunk); + + if (packet->malloced) + kfree(packet); +} + +/* This routine tries to append the chunk to the offered packet. If adding + * the chunk causes the packet to exceed the path MTU and COOKIE_ECHO chunk + * is not present in the packet, it transmits the input packet. + * Data can be bundled with a packet containing a COOKIE_ECHO chunk as long + * as it can fit in the packet, but any more data that does not fit in this + * packet can be sent only after receiving the COOKIE_ACK. + */ +sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) +{ + sctp_xmit_t retval; + int error = 0; + + switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) { + case SCTP_XMIT_PMTU_FULL: + if (!packet->has_cookie_echo) { + error = sctp_packet_transmit(packet); + if (error < 0) + chunk->skb->sk->err = -error; + + /* If we have an empty packet, then we can NOT ever + * return PMTU_FULL. + */ + retval = sctp_packet_append_chunk(packet, chunk); + } + break; + + case SCTP_XMIT_MUST_FRAG: + case SCTP_XMIT_RWND_FULL: + case SCTP_XMIT_OK: + case SCTP_XMIT_NAGLE_DELAY: + break; + }; + + return retval; +} + +/* Try to bundle a SACK with the packet. */ +static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt, + struct sctp_chunk *chunk) +{ + sctp_xmit_t retval = SCTP_XMIT_OK; + + /* If sending DATA and haven't aleady bundled a SACK, try to + * bundle one in to the packet. + */ + if (sctp_chunk_is_data(chunk) && !pkt->has_sack && + !pkt->has_cookie_echo) { + struct sctp_association *asoc; + asoc = pkt->transport->asoc; + + if (asoc->a_rwnd > asoc->rwnd) { + struct sctp_chunk *sack; + asoc->a_rwnd = asoc->rwnd; + sack = sctp_make_sack(asoc); + if (sack) { + struct timer_list *timer; + retval = sctp_packet_append_chunk(pkt, sack); + asoc->peer.sack_needed = 0; + timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; + if (timer_pending(timer) && del_timer(timer)) + sctp_association_put(asoc); + } + } + } + return retval; +} + +/* Append a chunk to the offered packet reporting back any inability to do + * so. + */ +sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) +{ + sctp_xmit_t retval = SCTP_XMIT_OK; + __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length)); + size_t psize; + size_t pmtu; + int too_big; + + retval = sctp_packet_bundle_sack(packet, chunk); + psize = packet->size; + + if (retval != SCTP_XMIT_OK) + goto finish; + + pmtu = ((packet->transport->asoc) ? + (packet->transport->asoc->pmtu) : + (packet->transport->pmtu)); + + too_big = (psize + chunk_len > pmtu); + + /* Decide if we need to fragment or resubmit later. */ + if (too_big) { + int packet_empty = (packet->size == SCTP_IP_OVERHEAD); + + /* Both control chunks and data chunks with TSNs are + * non-fragmentable. + */ + int fragmentable = sctp_chunk_is_data(chunk) && + (!chunk->has_tsn); + if (packet_empty) { + if (fragmentable) { + retval = SCTP_XMIT_MUST_FRAG; + goto finish; + } else { + /* The packet is too big but we can + * not fragment it--we have to just + * transmit and rely on IP + * fragmentation. + */ + packet->ipfragok = 1; + goto append; + } + } else { /* !packet_empty */ + retval = SCTP_XMIT_PMTU_FULL; + goto finish; + } + } else { + /* The chunk fits in the packet. */ + goto append; + } + +append: + /* We believe that this chunk is OK to add to the packet (as + * long as we have the cwnd for it). + */ + + /* DATA is a special case since we must examine both rwnd and cwnd + * before we send DATA. + */ + if (sctp_chunk_is_data(chunk)) { + retval = sctp_packet_append_data(packet, chunk); + /* Disallow SACK bundling after DATA. */ + packet->has_sack = 1; + if (SCTP_XMIT_OK != retval) + goto finish; + } else if (SCTP_CID_COOKIE_ECHO == chunk->chunk_hdr->type) + packet->has_cookie_echo = 1; + else if (SCTP_CID_SACK == chunk->chunk_hdr->type) + packet->has_sack = 1; + + /* It is OK to send this chunk. */ + __skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk); + packet->size += chunk_len; +finish: + return retval; +} + +/* All packets are sent to the network through this function from + * sctp_outq_tail(). + * + * The return value is a normal kernel error return value. + */ +int sctp_packet_transmit(struct sctp_packet *packet) +{ + struct sctp_transport *tp = packet->transport; + struct sctp_association *asoc = tp->asoc; + struct sctphdr *sh; + __u32 crc32; + struct sk_buff *nskb; + struct sctp_chunk *chunk; + struct sock *sk; + int err = 0; + int padding; /* How much padding do we need? */ + __u8 has_data = 0; + struct dst_entry *dst; + + /* Do NOT generate a chunkless packet... */ + if (skb_queue_empty(&packet->chunks)) + return err; + + /* Set up convenience variables... */ + chunk = (struct sctp_chunk *) (packet->chunks.next); + sk = chunk->skb->sk; + + /* Allocate the new skb. */ + nskb = dev_alloc_skb(packet->size); + if (!nskb) { + err = -ENOMEM; + goto out; + } + + /* Make sure the outbound skb has enough header room reserved. */ + skb_reserve(nskb, SCTP_IP_OVERHEAD); + + /* Set the owning socket so that we know where to get the + * destination IP address. + */ + skb_set_owner_w(nskb, sk); + + /* Build the SCTP header. */ + sh = (struct sctphdr *)skb_push(nskb, sizeof(struct sctphdr)); + sh->source = htons(packet->source_port); + sh->dest = htons(packet->destination_port); + + /* From 6.8 Adler-32 Checksum Calculation: + * After the packet is constructed (containing the SCTP common + * header and one or more control or DATA chunks), the + * transmitter shall: + * + * 1) Fill in the proper Verification Tag in the SCTP common + * header and initialize the checksum field to 0's. + */ + sh->vtag = htonl(packet->vtag); + sh->checksum = 0; + + + /* 2) Calculate the Adler-32 checksum of the whole packet, + * including the SCTP common header and all the + * chunks. + * + * Note: Adler-32 is no longer applicable, as has been replaced + * by CRC32-C as described in . + */ + crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr)); + + /** + * 6.10 Bundling + * + * An endpoint bundles chunks by simply including multiple + * chunks in one outbound SCTP packet. ... + */ + + /** + * 3.2 Chunk Field Descriptions + * + * The total length of a chunk (including Type, Length and + * Value fields) MUST be a multiple of 4 bytes. If the length + * of the chunk is not a multiple of 4 bytes, the sender MUST + * pad the chunk with all zero bytes and this padding is not + * included in the chunk length field. The sender should + * never pad with more than 3 bytes. + * + * [This whole comment explains WORD_ROUND() below.] + */ + SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); + while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) { + chunk->num_times_sent++; + chunk->sent_at = jiffies; + if (sctp_chunk_is_data(chunk)) { + sctp_chunk_assign_tsn(chunk); + + /* 6.3.1 C4) When data is in flight and when allowed + * by rule C5, a new RTT measurement MUST be made each + * round trip. Furthermore, new RTT measurements + * SHOULD be made no more than once per round-trip + * for a given destination transport address. + */ + if ((1 == chunk->num_times_sent) && + (!tp->rto_pending)) { + chunk->rtt_in_progress = 1; + tp->rto_pending = 1; + } + has_data = 1; + } + padding = WORD_ROUND(chunk->skb->len) - chunk->skb->len; + if (padding) + memset(skb_put(chunk->skb, padding), 0, padding); + + crc32 = sctp_update_copy_cksum(skb_put(nskb, chunk->skb->len), + chunk->skb->data, + chunk->skb->len, crc32); + + SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d, " + "%s %d\n", + "*** Chunk", chunk, + sctp_cname(SCTP_ST_CHUNK( + chunk->chunk_hdr->type)), + chunk->has_tsn ? "TSN" : "No TSN", + chunk->has_tsn ? + ntohl(chunk->subh.data_hdr->tsn) : 0, + "length", ntohs(chunk->chunk_hdr->length), + "chunk->skb->len", chunk->skb->len, + "num_times_sent", chunk->num_times_sent, + "rtt_in_progress", chunk->rtt_in_progress); + + /* + * If this is a control chunk, this is our last + * reference. Free data chunks after they've been + * acknowledged or have failed. + */ + if (!sctp_chunk_is_data(chunk)) + sctp_free_chunk(chunk); + } + + /* Perform final transformation on checksum. */ + crc32 = sctp_end_cksum(crc32); + + /* 3) Put the resultant value into the checksum field in the + * common header, and leave the rest of the bits unchanged. + */ + sh->checksum = htonl(crc32); + + /* IP layer ECN support + * From RFC 2481 + * "The ECN-Capable Transport (ECT) bit would be set by the + * data sender to indicate that the end-points of the + * transport protocol are ECN-capable." + * + * If ECN capable && negotiated && it makes sense for + * this packet to support it (e.g. post ECN negotiation) + * then lets set the ECT bit + * + * FIXME: Need to do something else for IPv6 + */ + if (packet->ecn_capable) { + INET_ECN_xmit(nskb->sk); + } else { + INET_ECN_dontxmit(nskb->sk); + } + + /* Set up the IP options. */ + /* BUG: not implemented + * For v4 this all lives somewhere in sk->opt... + */ + + /* Dump that on IP! */ + if (asoc && asoc->peer.last_sent_to != tp) { + /* Considering the multiple CPU scenario, this is a + * "correcter" place for last_sent_to. --xguo + */ + asoc->peer.last_sent_to = tp; + } + + if (has_data) { + struct timer_list *timer; + unsigned long timeout; + + tp->last_time_used = jiffies; + + /* Restart the AUTOCLOSE timer when sending data. */ + if ((SCTP_STATE_ESTABLISHED == asoc->state) && + (asoc->autoclose)) { + timer = &asoc->timers[SCTP_EVENT_TIMEOUT_AUTOCLOSE]; + timeout = asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE]; + + if (!mod_timer(timer, jiffies + timeout)) + sctp_association_hold(asoc); + } + } + + dst = tp->dst; + /* The 'obsolete' field of dst is set to 2 when a dst is freed. */ + if (!dst || (dst->obsolete > 1)) { + dst_release(dst); + sctp_transport_route(tp, NULL, sctp_sk(sk)); + sctp_assoc_sync_pmtu(asoc); + } + + nskb->dst = dst_clone(tp->dst); + if (!nskb->dst) + goto no_route; + + SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n", + nskb->len); + (*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok); +out: + packet->size = SCTP_IP_OVERHEAD; + return err; +no_route: + kfree_skb(nskb); + IP_INC_STATS_BH(IpOutNoRoutes); + + /* FIXME: Returning the 'err' will effect all the associations + * associated with a socket, although only one of the paths of the + * association is unreachable. + * The real failure of a transport or association can be passed on + * to the user via notifications. So setting this error may not be + * required. + */ + /* err = -EHOSTUNREACH; */ + goto out; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* + * This private function resets the packet to a fresh state. + */ +static void sctp_packet_reset(struct sctp_packet *packet) +{ + struct sctp_chunk *chunk = NULL; + + packet->size = SCTP_IP_OVERHEAD; + + if (packet->get_prepend_chunk) + chunk = packet->get_prepend_chunk(packet->transport->asoc); + + /* If there a is a prepend chunk stick it on the list before + * any other chunks get appended. + */ + if (chunk) + sctp_packet_append_chunk(packet, chunk); +} + +/* This private function handles the specifics of appending DATA chunks. */ +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk) +{ + sctp_xmit_t retval = SCTP_XMIT_OK; + size_t datasize, rwnd, inflight; + struct sctp_transport *transport = packet->transport; + __u32 max_burst_bytes; + struct sctp_association *asoc = transport->asoc; + struct sctp_opt *sp = sctp_sk(asoc->base.sk); + struct sctp_outq *q = &asoc->outqueue; + + /* RFC 2960 6.1 Transmission of DATA Chunks + * + * A) At any given time, the data sender MUST NOT transmit new data to + * any destination transport address if its peer's rwnd indicates + * that the peer has no buffer space (i.e. rwnd is 0, see Section + * 6.2.1). However, regardless of the value of rwnd (including if it + * is 0), the data sender can always have one DATA chunk in flight to + * the receiver if allowed by cwnd (see rule B below). This rule + * allows the sender to probe for a change in rwnd that the sender + * missed due to the SACK having been lost in transit from the data + * receiver to the data sender. + */ + + rwnd = asoc->peer.rwnd; + inflight = asoc->outqueue.outstanding_bytes; + + datasize = sctp_data_size(chunk); + + if (datasize > rwnd) { + if (inflight > 0) { + /* We have (at least) one data chunk in flight, + * so we can't fall back to rule 6.1 B). + */ + retval = SCTP_XMIT_RWND_FULL; + goto finish; + } + } + + /* sctpimpguide-05 2.14.2 + * D) When the time comes for the sender to + * transmit new DATA chunks, the protocol parameter Max.Burst MUST + * first be applied to limit how many new DATA chunks may be sent. + * The limit is applied by adjusting cwnd as follows: + * if ((flightsize + Max.Burst * MTU) < cwnd) + * cwnd = flightsize + Max.Burst * MTU + */ + max_burst_bytes = asoc->max_burst * asoc->pmtu; + if ((transport->flight_size + max_burst_bytes) < transport->cwnd) { + transport->cwnd = transport->flight_size + max_burst_bytes; + SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: " + "transport: %p, cwnd: %d, " + "ssthresh: %d, flight_size: %d, " + "pba: %d\n", + __FUNCTION__, transport, + transport->cwnd, + transport->ssthresh, + transport->flight_size, + transport->partial_bytes_acked); + } + + /* RFC 2960 6.1 Transmission of DATA Chunks + * + * B) At any given time, the sender MUST NOT transmit new data + * to a given transport address if it has cwnd or more bytes + * of data outstanding to that transport address. + */ + /* RFC 7.2.4 & the Implementers Guide 2.8. + * + * 3) ... + * When a Fast Retransmit is being performed the sender SHOULD + * ignore the value of cwnd and SHOULD NOT delay retransmission. + */ + if (!chunk->fast_retransmit) + if (transport->flight_size >= transport->cwnd) { + retval = SCTP_XMIT_RWND_FULL; + goto finish; + } + + /* Nagle's algorithm to solve small-packet problem: + * Inhibit the sending of new chunks when new outgoing data arrives + * if any previously transmitted data on the connection remains + * unacknowledged. + */ + if (!sp->nodelay && SCTP_IP_OVERHEAD == packet->size && + q->outstanding_bytes && SCTP_STATE_ESTABLISHED == asoc->state) { + unsigned len = datasize + q->out_qlen; + + /* Check whether this chunk and all the rest of pending + * data will fit or delay in hopes of bundling a full + * sized packet. + */ + if (len < asoc->pmtu - SCTP_IP_OVERHEAD) { + retval = SCTP_XMIT_NAGLE_DELAY; + goto finish; + } + } + + /* Keep track of how many bytes are in flight over this transport. */ + transport->flight_size += datasize; + + /* Keep track of how many bytes are in flight to the receiver. */ + asoc->outqueue.outstanding_bytes += datasize; + + /* Update our view of the receiver's rwnd. */ + if (datasize < rwnd) + rwnd -= datasize; + else + rwnd = 0; + + asoc->peer.rwnd = rwnd; + +finish: + return retval; +} diff -urN linux-2.4.22-bk23/net/sctp/outqueue.c linux-2.4.22-bk24/net/sctp/outqueue.c --- linux-2.4.22-bk23/net/sctp/outqueue.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/outqueue.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,1637 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 Intel Corp. + * Copyright (c) 2001-2003 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions implement the sctp_outq class. The outqueue handles + * bundling and queueing of outgoing SCTP chunks. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Perry Melange + * Xingang Guo + * Hui Huang + * Sridhar Samudrala + * Jon Grimm + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include /* For struct list_head */ +#include +#include +#include /* For skb_set_owner_w */ + +#include + +/* Declare internal functions here. */ +static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn); +static void sctp_check_transmitted(struct sctp_outq *q, + struct list_head *transmitted_queue, + struct sctp_transport *transport, + sctp_sackhdr_t *sack, + __u32 highest_new_tsn); + +/* Add data to the front of the queue. */ +static inline void sctp_outq_head_data(struct sctp_outq *q, + struct sctp_chunk *ch) +{ + __skb_queue_head(&q->out, (struct sk_buff *)ch); + q->out_qlen += ch->skb->len; + return; +} + +/* Take data from the front of the queue. */ +static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q) +{ + struct sctp_chunk *ch; + ch = (struct sctp_chunk *)__skb_dequeue(&q->out); + if (ch) + q->out_qlen -= ch->skb->len; + return ch; +} +/* Add data chunk to the end of the queue. */ +static inline void sctp_outq_tail_data(struct sctp_outq *q, + struct sctp_chunk *ch) +{ + __skb_queue_tail(&q->out, (struct sk_buff *)ch); + q->out_qlen += ch->skb->len; + return; +} + +/* Insert a chunk behind chunk 'pos'. */ +static inline void sctp_outq_insert_data(struct sctp_outq *q, + struct sctp_chunk *ch, + struct sctp_chunk *pos) +{ + __skb_insert((struct sk_buff *)ch, (struct sk_buff *)pos->prev, + (struct sk_buff *)pos, pos->list); + q->out_qlen += ch->skb->len; +} + +/* Generate a new outqueue. */ +struct sctp_outq *sctp_outq_new(struct sctp_association *asoc) +{ + struct sctp_outq *q; + + q = t_new(struct sctp_outq, GFP_KERNEL); + if (q) { + sctp_outq_init(asoc, q); + q->malloced = 1; + } + return q; +} + +/* Initialize an existing sctp_outq. This does the boring stuff. + * You still need to define handlers if you really want to DO + * something with this structure... + */ +void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q) +{ + q->asoc = asoc; + skb_queue_head_init(&q->out); + skb_queue_head_init(&q->control); + INIT_LIST_HEAD(&q->retransmit); + INIT_LIST_HEAD(&q->sacked); + + q->init_output = NULL; + q->config_output = NULL; + q->append_output = NULL; + q->build_output = NULL; + q->force_output = NULL; + + q->outstanding_bytes = 0; + q->empty = 1; + + q->malloced = 0; + q->out_qlen = 0; +} + +/* Free the outqueue structure and any related pending chunks. + */ +void sctp_outq_teardown(struct sctp_outq *q) +{ + struct sctp_transport *transport; + struct list_head *lchunk, *pos, *temp; + sctp_chunk_t *chunk; + struct sctp_ulpevent *ev; + + /* Throw away unacknowledged chunks. */ + list_for_each(pos, &q->asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + while ((lchunk = sctp_list_dequeue(&transport->transmitted))) { + chunk = list_entry(lchunk, sctp_chunk_t, + transmitted_list); + + /* Generate a SEND FAILED event. */ + ev = sctp_ulpevent_make_send_failed(q->asoc, + chunk, SCTP_DATA_SENT, + q->error, GFP_ATOMIC); + if (ev) + sctp_ulpq_tail_event(&q->asoc->ulpq, ev); + + sctp_free_chunk(chunk); + } + } + + /* Throw away chunks that have been gap ACKed. */ + list_for_each_safe(lchunk, temp, &q->sacked) { + list_del(lchunk); + chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + sctp_free_chunk(chunk); + } + + /* Throw away any chunks in the retransmit queue. */ + list_for_each_safe(lchunk, temp, &q->retransmit) { + list_del(lchunk); + chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + sctp_free_chunk(chunk); + } + + /* Throw away any leftover data chunks. */ + while ((chunk = sctp_outq_dequeue_data(q))) { + + /* Generate a SEND FAILED event. */ + ev = sctp_ulpevent_make_send_failed(q->asoc, + chunk, SCTP_DATA_UNSENT, + q->error, GFP_ATOMIC); + if (ev) + sctp_ulpq_tail_event(&q->asoc->ulpq, ev); + + sctp_free_chunk(chunk); + } + + q->error = 0; + + /* Throw away any leftover control chunks. */ + while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->control))) + sctp_free_chunk(chunk); +} + +/* Free the outqueue structure and any related pending chunks. */ +void sctp_outq_free(struct sctp_outq *q) +{ + /* Throw away leftover chunks. */ + sctp_outq_teardown(q); + + /* If we were kmalloc()'d, free the memory. */ + if (q->malloced) + kfree(q); +} + +/* Put a new chunk in an sctp_outq. */ +int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk) +{ + int error = 0; + + SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n", + q, chunk, chunk && chunk->chunk_hdr ? + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) + : "Illegal Chunk"); + + /* If it is data, queue it up, otherwise, send it + * immediately. + */ + if (SCTP_CID_DATA == chunk->chunk_hdr->type) { + /* Is it OK to queue data chunks? */ + /* From 9. Termination of Association + * + * When either endpoint performs a shutdown, the + * association on each peer will stop accepting new + * data from its user and only deliver data in queue + * at the time of sending or receiving the SHUTDOWN + * chunk. + */ + switch (q->asoc->state) { + case SCTP_STATE_EMPTY: + case SCTP_STATE_CLOSED: + case SCTP_STATE_SHUTDOWN_PENDING: + case SCTP_STATE_SHUTDOWN_SENT: + case SCTP_STATE_SHUTDOWN_RECEIVED: + case SCTP_STATE_SHUTDOWN_ACK_SENT: + /* Cannot send after transport endpoint shutdown */ + error = -ESHUTDOWN; + break; + + default: + SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n", + q, chunk, chunk && chunk->chunk_hdr ? + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) + : "Illegal Chunk"); + + sctp_outq_tail_data(q, chunk); + if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) + SCTP_INC_STATS(SctpOutUnorderChunks); + else + SCTP_INC_STATS(SctpOutOrderChunks); + q->empty = 0; + break; + }; + } else { + __skb_queue_tail(&q->control, (struct sk_buff *) chunk); + SCTP_INC_STATS(SctpOutCtrlChunks); + } + + if (error < 0) + return error; + + error = sctp_outq_flush(q, 0); + + return error; +} + +/* Insert a chunk into the retransmit queue. Chunks on the retransmit + * queue are kept in order, based on the TSNs. + */ +void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q) +{ + struct list_head *rlchunk; + sctp_chunk_t *tchunk, *rchunk; + __u32 ttsn, rtsn; + int done = 0; + + tchunk = list_entry(tlchunk, sctp_chunk_t, transmitted_list); + ttsn = ntohl(tchunk->subh.data_hdr->tsn); + + list_for_each(rlchunk, &q->retransmit) { + rchunk = list_entry(rlchunk, sctp_chunk_t, transmitted_list); + rtsn = ntohl(rchunk->subh.data_hdr->tsn); + if (TSN_lt(ttsn, rtsn)) { + list_add(tlchunk, rlchunk->prev); + done = 1; + break; + } + } + if (!done) { + list_add_tail(tlchunk, &q->retransmit); + } +} + +/* Mark all the eligible packets on a transport for retransmission. */ +void sctp_retransmit_mark(struct sctp_outq *q, + struct sctp_transport *transport, + __u8 fast_retransmit) +{ + struct list_head *lchunk, *ltemp; + sctp_chunk_t *chunk; + + /* Walk through the specified transmitted queue. */ + list_for_each_safe(lchunk, ltemp, &transport->transmitted) { + chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + + /* If we are doing retransmission due to a fast retransmit, + * only the chunk's that are marked for fast retransmit + * should be added to the retransmit queue. If we are doing + * retransmission due to a timeout or pmtu discovery, only the + * chunks that are not yet acked should be added to the + * retransmit queue. + */ + if ((fast_retransmit && chunk->fast_retransmit) || + (!fast_retransmit && !chunk->tsn_gap_acked)) { + /* RFC 2960 6.2.1 Processing a Received SACK + * + * C) Any time a DATA chunk is marked for + * retransmission (via either T3-rtx timer expiration + * (Section 6.3.3) or via fast retransmit + * (Section 7.2.4)), add the data size of those + * chunks to the rwnd. + */ + q->asoc->peer.rwnd += sctp_data_size(chunk); + q->outstanding_bytes -= sctp_data_size(chunk); + transport->flight_size -= sctp_data_size(chunk); + + /* sctpimpguide-05 Section 2.8.2 + * M5) If a T3-rtx timer expires, the + * 'TSN.Missing.Report' of all affected TSNs is set + * to 0. + */ + chunk->tsn_missing_report = 0; + + /* If a chunk that is being used for RTT measurement + * has to be retransmitted, we cannot use this chunk + * anymore for RTT measurements. Reset rto_pending so + * that a new RTT measurement is started when a new + * data chunk is sent. + */ + if (chunk->rtt_in_progress) { + chunk->rtt_in_progress = 0; + transport->rto_pending = 0; + } + + /* Move the chunk to the retransmit queue. The chunks + * on the retransmit queue is always kept in order. + */ + list_del(lchunk); + sctp_retransmit_insert(lchunk, q); + } + } + + SCTP_DEBUG_PRINTK("%s: transport: %p, fast_retransmit: %d, " + "cwnd: %d, ssthresh: %d, flight_size: %d, " + "pba: %d\n", __FUNCTION__, + transport, fast_retransmit, + transport->cwnd, transport->ssthresh, + transport->flight_size, + transport->partial_bytes_acked); + +} + +/* Mark all the eligible packets on a transport for retransmission and force + * one packet out. + */ +void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, + sctp_retransmit_reason_t reason) +{ + int error = 0; + __u8 fast_retransmit = 0; + + switch(reason) { + case SCTP_RTXR_T3_RTX: + sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX); + /* Update the retran path if the T3-rtx timer has expired for + * the current retran path. + */ + if (transport == transport->asoc->peer.retran_path) + sctp_assoc_update_retran_path(transport->asoc); + break; + case SCTP_RTXR_FAST_RTX: + sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX); + fast_retransmit = 1; + break; + case SCTP_RTXR_PMTUD: + default: + break; + } + + sctp_retransmit_mark(q, transport, fast_retransmit); + + error = sctp_outq_flush(q, /* rtx_timeout */ 1); + + if (error) + q->asoc->base.sk->err = -error; +} + +/* + * Transmit DATA chunks on the retransmit queue. Upon return from + * sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which + * need to be transmitted by the caller. + * We assume that pkt->transport has already been set. + * + * The return value is a normal kernel error return value. + */ +static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, + int rtx_timeout, int *start_timer) +{ + struct list_head *lqueue; + struct list_head *lchunk; + struct sctp_transport *transport = pkt->transport; + sctp_xmit_t status; + sctp_chunk_t *chunk; + struct sctp_association *asoc; + int error = 0; + + asoc = q->asoc; + lqueue = &q->retransmit; + + /* RFC 2960 6.3.3 Handle T3-rtx Expiration + * + * E3) Determine how many of the earliest (i.e., lowest TSN) + * outstanding DATA chunks for the address for which the + * T3-rtx has expired will fit into a single packet, subject + * to the MTU constraint for the path corresponding to the + * destination transport address to which the retransmission + * is being sent (this may be different from the address for + * which the timer expires [see Section 6.4]). Call this value + * K. Bundle and retransmit those K DATA chunks in a single + * packet to the destination endpoint. + * + * [Just to be painfully clear, if we are retransmitting + * because a timeout just happened, we should send only ONE + * packet of retransmitted data.] + */ + lchunk = sctp_list_dequeue(lqueue); + + while (lchunk) { + chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + + /* Make sure that Gap Acked TSNs are not retransmitted. A + * simple approach is just to move such TSNs out of the + * way and into a 'transmitted' queue and skip to the + * next chunk. + */ + if (chunk->tsn_gap_acked) { + list_add_tail(lchunk, &transport->transmitted); + lchunk = sctp_list_dequeue(lqueue); + continue; + } + + /* Attempt to append this chunk to the packet. */ + status = (*q->append_output)(pkt, chunk); + + switch (status) { + case SCTP_XMIT_PMTU_FULL: + /* Send this packet. */ + if ((error = (*q->force_output)(pkt)) == 0) + *start_timer = 1; + + /* If we are retransmitting, we should only + * send a single packet. + */ + if (rtx_timeout) { + list_add(lchunk, lqueue); + lchunk = NULL; + } + + /* Bundle lchunk in the next round. */ + break; + + case SCTP_XMIT_RWND_FULL: + /* Send this packet. */ + if ((error = (*q->force_output)(pkt)) == 0) + *start_timer = 1; + + /* Stop sending DATA as there is no more room + * at the receiver. + */ + list_add(lchunk, lqueue); + lchunk = NULL; + break; + + default: + /* The append was successful, so add this chunk to + * the transmitted list. + */ + list_add_tail(lchunk, &transport->transmitted); + *start_timer = 1; + q->empty = 0; + + /* Retrieve a new chunk to bundle. */ + lchunk = sctp_list_dequeue(lqueue); + break; + }; + } + + return error; +} + +/* This routine either transmits the fragment or puts it on the output + * queue. 'pos' points to the next chunk in the output queue after the + * chunk that is currently in the process of fragmentation. + */ +void sctp_xmit_frag(struct sctp_outq *q, struct sctp_chunk *pos, + struct sctp_packet *packet, struct sctp_chunk *frag, __u32 tsn) +{ + struct sctp_transport *transport = packet->transport; + struct sk_buff_head *queue = &q->out; + sctp_xmit_t status; + int error; + + frag->subh.data_hdr->tsn = htonl(tsn); + frag->has_tsn = 1; + + /* An inner fragment may be smaller than the earlier one and may get + * in if we call q->build_output. This ensures that all the fragments + * are sent in order. + */ + if (!skb_queue_empty(queue)) { + SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. " + "adding 0x%x to outqueue\n", + ntohl(frag->subh.data_hdr->tsn)); + if (pos) + sctp_outq_insert_data(q, frag, pos); + else + sctp_outq_tail_data(q, frag); + return; + } + + /* Add the chunk fragment to the packet. */ + status = (*q->build_output)(packet, frag); + switch (status) { + case SCTP_XMIT_RWND_FULL: + /* RWND is full, so put the chunk in the output queue. */ + SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. " + "adding 0x%x to outqueue\n", + ntohl(frag->subh.data_hdr->tsn)); + if (pos) + sctp_outq_insert_data(q, frag, pos); + else + sctp_outq_tail_data(q, frag); + break; + + case SCTP_XMIT_OK: + error = (*q->force_output)(packet); + if (error < 0) { + /* Packet could not be transmitted, put the chunk in + * the output queue + */ + SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " + "failed. adding 0x%x to outqueue\n", + ntohl(frag->subh.data_hdr->tsn)); + if (pos) + sctp_outq_insert_data(q, frag, pos); + else + sctp_outq_tail_data(q, frag); + } else { + SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " + "success. 0x%x sent\n", + ntohl(frag->subh.data_hdr->tsn)); + list_add_tail(&frag->transmitted_list, + &transport->transmitted); + + sctp_transport_reset_timers(transport); + } + break; + + default: + BUG(); + }; +} + +/* This routine calls sctp_xmit_frag() for all the fragments of a message. + * The argument 'frag' point to the first fragment and it holds the list + * of all the other fragments in the 'frag_list' field. + */ +void sctp_xmit_fragmented_chunks(struct sctp_outq *q, struct sctp_packet *pkt, + sctp_chunk_t *frag) +{ + struct sctp_association *asoc = frag->asoc; + struct list_head *lfrag, *frag_list; + __u32 tsn; + int nfrags = 1; + struct sctp_chunk *pos; + + /* Count the number of fragments. */ + frag_list = &frag->frag_list; + list_for_each(lfrag, frag_list) { + nfrags++; + } + + /* Get a TSN block of nfrags TSNs. */ + tsn = sctp_association_get_tsn_block(asoc, nfrags); + + pos = (struct sctp_chunk *)skb_peek(&q->out); + /* Transmit the first fragment. */ + sctp_xmit_frag(q, pos, pkt, frag, tsn++); + + /* Transmit the rest of fragments. */ + frag_list = &frag->frag_list; + list_for_each(lfrag, frag_list) { + frag = list_entry(lfrag, sctp_chunk_t, frag_list); + sctp_xmit_frag(q, pos, pkt, frag, tsn++); + } +} + +/* This routine breaks the given chunk into 'max_frag_data_len' size + * fragments. It returns the first fragment with the frag_list field holding + * the remaining fragments. + */ +sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, + size_t max_frag_data_len) +{ + struct sctp_association *asoc = chunk->asoc; + void *data_ptr = chunk->subh.data_hdr; + struct sctp_sndrcvinfo *sinfo = &chunk->sinfo; + __u16 chunk_data_len = sctp_data_size(chunk); + __u16 ssn = ntohs(chunk->subh.data_hdr->ssn); + sctp_chunk_t *first_frag, *frag; + struct list_head *frag_list; + int nfrags; + __u8 old_flags, flags; + + /* nfrags = no. of max size fragments + any smaller last fragment. */ + nfrags = ((chunk_data_len / max_frag_data_len) + + ((chunk_data_len % max_frag_data_len) ? 1 : 0)); + + /* Start of the data in the chunk. */ + data_ptr += sizeof(sctp_datahdr_t); + + /* Are we fragmenting an already fragmented large message? */ + old_flags = chunk->chunk_hdr->flags; + if (old_flags & SCTP_DATA_FIRST_FRAG) + flags = SCTP_DATA_FIRST_FRAG; + else + flags = SCTP_DATA_MIDDLE_FRAG; + + /* Make the first fragment. */ + first_frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len, + data_ptr, flags, ssn); + + if (!first_frag) + goto err; + first_frag->has_ssn = 1; + /* All the fragments are added to the frag_list of the first chunk. */ + frag_list = &first_frag->frag_list; + + chunk_data_len -= max_frag_data_len; + data_ptr += max_frag_data_len; + + /* Make the middle fragments. */ + while (chunk_data_len > max_frag_data_len) { + frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len, + data_ptr, SCTP_DATA_MIDDLE_FRAG, + ssn); + if (!frag) + goto err; + frag->has_ssn = 1; + /* Add the middle fragment to the first fragment's + * frag_list. + */ + list_add_tail(&frag->frag_list, frag_list); + + chunk_data_len -= max_frag_data_len; + data_ptr += max_frag_data_len; + } + + if (old_flags & SCTP_DATA_LAST_FRAG) + flags = SCTP_DATA_LAST_FRAG; + else + flags = SCTP_DATA_MIDDLE_FRAG; + + /* Make the last fragment. */ + frag = sctp_make_datafrag(asoc, sinfo, chunk_data_len, data_ptr, + flags, ssn); + if (!frag) + goto err; + frag->has_ssn = 1; + + /* Add the last fragment to the first fragment's frag_list. */ + list_add_tail(&frag->frag_list, frag_list); + + /* Free the original chunk. */ + sctp_free_chunk(chunk); + + return first_frag; + +err: + /* Free any fragments that are created before the failure. */ + if (first_frag) { + struct list_head *flist, *lfrag; + + /* Free all the fragments off the first one. */ + flist = &first_frag->frag_list; + while (NULL != (lfrag = sctp_list_dequeue(flist))) { + frag = list_entry(lfrag, sctp_chunk_t, frag_list); + sctp_free_chunk(frag); + } + + /* Free the first fragment. */ + sctp_free_chunk(first_frag); + } + + return NULL; +} + +/* + * sctp_outq_flush - Try to flush an outqueue. + * + * Description: Send everything in q which we legally can, subject to + * congestion limitations. + * * Note: This function can be called from multiple contexts so appropriate + * locking concerns must be made. Today we use the sock lock to protect + * this function. + */ +int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) +{ + struct sctp_packet *packet; + struct sctp_packet singleton; + struct sctp_association *asoc = q->asoc; + int ecn_capable = asoc->peer.ecn_capable; + __u16 sport = asoc->base.bind_addr.port; + __u16 dport = asoc->peer.port; + __u32 vtag = asoc->peer.i.init_tag; + /* This is the ECNE handler for singleton packets. */ + sctp_packet_phandler_t *s_ecne_handler = NULL; + sctp_packet_phandler_t *ecne_handler = NULL; + struct sk_buff_head *queue; + struct sctp_transport *transport = NULL; + struct sctp_transport *new_transport; + sctp_chunk_t *chunk; + sctp_xmit_t status; + int error = 0; + int start_timer = 0; + + /* These transports have chunks to send. */ + struct list_head transport_list; + struct list_head *ltransport; + + INIT_LIST_HEAD(&transport_list); + packet = NULL; + + /* + * 6.10 Bundling + * ... + * When bundling control chunks with DATA chunks, an + * endpoint MUST place control chunks first in the outbound + * SCTP packet. The transmitter MUST transmit DATA chunks + * within a SCTP packet in increasing order of TSN. + * ... + */ + if (ecn_capable) { + s_ecne_handler = &sctp_get_no_prepend; + ecne_handler = &sctp_get_ecne_prepend; + } + + queue = &q->control; + while ((chunk = (sctp_chunk_t *)skb_dequeue(queue))) { + /* Pick the right transport to use. */ + new_transport = chunk->transport; + + if (!new_transport) { + new_transport = asoc->peer.active_path; + } else if (!new_transport->active) { + /* If the chunk is Heartbeat, send it to + * chunk->transport, even it's inactive. + */ + if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT) + new_transport = asoc->peer.active_path; + } + + /* Are we switching transports? + * Take care of transport locks. + */ + if (new_transport != transport) { + transport = new_transport; + if (list_empty(&transport->send_ready)) { + list_add_tail(&transport->send_ready, + &transport_list); + } + packet = &transport->packet; + (*q->config_output)(packet, vtag, + ecn_capable, ecne_handler); + } + + switch (chunk->chunk_hdr->type) { + /* + * 6.10 Bundling + * ... + * An endpoint MUST NOT bundle INIT, INIT ACK or SHUTDOWN + * COMPLETE with any other chunks. [Send them immediately.] + */ + case SCTP_CID_INIT: + case SCTP_CID_INIT_ACK: + case SCTP_CID_SHUTDOWN_COMPLETE: + (*q->init_output)(&singleton, transport, sport, dport); + (*q->config_output)(&singleton, vtag, ecn_capable, + s_ecne_handler); + (void) (*q->build_output)(&singleton, chunk); + error = (*q->force_output)(&singleton); + if (error < 0) + return error; + break; + + case SCTP_CID_ABORT: + case SCTP_CID_SACK: + case SCTP_CID_HEARTBEAT: + case SCTP_CID_HEARTBEAT_ACK: + case SCTP_CID_SHUTDOWN: + case SCTP_CID_SHUTDOWN_ACK: + case SCTP_CID_ERROR: + case SCTP_CID_COOKIE_ECHO: + case SCTP_CID_COOKIE_ACK: + case SCTP_CID_ECN_ECNE: + case SCTP_CID_ECN_CWR: + (void) (*q->build_output)(packet, chunk); + break; + + case SCTP_CID_ASCONF: + case SCTP_CID_ASCONF_ACK: + (void) (*q->build_output)(packet, chunk); + break; + + default: + /* We built a chunk with an illegal type! */ + BUG(); + }; + } + + /* Is it OK to send data chunks? */ + switch (asoc->state) { + case SCTP_STATE_COOKIE_ECHOED: + /* Only allow bundling when this packet has a COOKIE-ECHO + * chunk. + */ + if (!packet || !packet->has_cookie_echo) + break; + + /* fallthru */ + case SCTP_STATE_ESTABLISHED: + case SCTP_STATE_SHUTDOWN_PENDING: + case SCTP_STATE_SHUTDOWN_RECEIVED: + /* + * RFC 2960 6.1 Transmission of DATA Chunks + * + * C) When the time comes for the sender to transmit, + * before sending new DATA chunks, the sender MUST + * first transmit any outstanding DATA chunks which + * are marked for retransmission (limited by the + * current cwnd). + */ + if (!list_empty(&q->retransmit)) { + if (transport == asoc->peer.retran_path) + goto retran; + + /* Switch transports & prepare the packet. */ + + transport = asoc->peer.retran_path; + + if (list_empty(&transport->send_ready)) { + list_add_tail(&transport->send_ready, + &transport_list); + } + + packet = &transport->packet; + (*q->config_output)(packet, vtag, + ecn_capable, ecne_handler); + retran: + error = sctp_outq_flush_rtx(q, packet, + rtx_timeout, &start_timer); + + if (start_timer) + sctp_transport_reset_timers(transport); + + /* This can happen on COOKIE-ECHO resend. Only + * one chunk can get bundled with a COOKIE-ECHO. + */ + if (packet->has_cookie_echo) + goto sctp_flush_out; + + /* Don't send new data if there is still data + * waiting to retransmit. + */ + if (!list_empty(&q->retransmit)) + goto sctp_flush_out; + } + + /* Finally, transmit new packets. */ + start_timer = 0; + queue = &q->out; + + while ((chunk = sctp_outq_dequeue_data(q))) { + /* RFC 2960 6.5 Every DATA chunk MUST carry a valid + * stream identifier. + */ + if (chunk->sinfo.sinfo_stream >= + asoc->c.sinit_num_ostreams) { + struct sctp_ulpevent *ev; + + /* Generate a SEND FAILED event. */ + ev = sctp_ulpevent_make_send_failed(asoc, + chunk, SCTP_DATA_UNSENT, + SCTP_ERROR_INV_STRM, GFP_ATOMIC); + if (ev) + sctp_ulpq_tail_event(&asoc->ulpq, ev); + + /* Free the chunk. */ + sctp_free_chunk(chunk); + continue; + } + + /* Now do delayed assignment of SSN. This will + * probably change again when we start supporting + * large (> approximately 2^16) size messages. + */ + sctp_chunk_assign_ssn(chunk); + + /* If there is a specified transport, use it. + * Otherwise, we want to use the active path. + */ + new_transport = chunk->transport; + if (new_transport == NULL || + !new_transport->active) + new_transport = asoc->peer.active_path; + + /* Change packets if necessary. */ + if (new_transport != transport) { + transport = new_transport; + + /* Schedule to have this transport's + * packet flushed. + */ + if (list_empty(&transport->send_ready)) { + list_add_tail(&transport->send_ready, + &transport_list); + } + + packet = &transport->packet; + (*q->config_output)(packet, vtag, + ecn_capable, ecne_handler); + } + + SCTP_DEBUG_PRINTK("sctp_transmit_packet(%p, %p[%s]), ", + q, chunk, + chunk && chunk->chunk_hdr ? + sctp_cname(SCTP_ST_CHUNK( + chunk->chunk_hdr->type)) + : "Illegal Chunk"); + + SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head " + "%p skb->users %d.\n", + ntohl(chunk->subh.data_hdr->tsn), + chunk->skb ?chunk->skb->head : 0, + chunk->skb ? + atomic_read(&chunk->skb->users) : -1); + + /* Add the chunk to the packet. */ + status = (*q->build_output)(packet, chunk); + + switch (status) { + case SCTP_XMIT_PMTU_FULL: + case SCTP_XMIT_RWND_FULL: + case SCTP_XMIT_NAGLE_DELAY: + /* We could not append this chunk, so put + * the chunk back on the output queue. + */ + SCTP_DEBUG_PRINTK("sctp_outq_flush: could " + "not transmit TSN: 0x%x, status: %d\n", + ntohl(chunk->subh.data_hdr->tsn), + status); + sctp_outq_head_data(q, chunk); + goto sctp_flush_out; + break; + + case SCTP_XMIT_MUST_FRAG: { + sctp_chunk_t *frag; + + frag = sctp_fragment_chunk(chunk, + packet->transport->asoc->frag_point); + if (!frag) { + /* We could not fragment due to out of + * memory condition. Free the original + * chunk and return ENOMEM. + */ + sctp_free_chunk(chunk); + error = -ENOMEM; + return error; + } + + sctp_xmit_fragmented_chunks(q, packet, frag); + goto sctp_flush_out; + break; + } + + case SCTP_XMIT_OK: + break; + + default: + BUG(); + } + + /* BUG: We assume that the (*q->force_output()) + * call below will succeed all the time and add the + * chunk to the transmitted list and restart the + * timers. + * It is possible that the call can fail under OOM + * conditions. + * + * Is this really a problem? Won't this behave + * like a lost TSN? + */ + list_add_tail(&chunk->transmitted_list, + &transport->transmitted); + + sctp_transport_reset_timers(transport); + + q->empty = 0; + + /* Only let one DATA chunk get bundled with a + * COOKIE-ECHO chunk. + */ + if (packet->has_cookie_echo) + goto sctp_flush_out; + } + break; + + default: + /* Do nothing. */ + break; + } + +sctp_flush_out: + + /* Before returning, examine all the transports touched in + * this call. Right now, we bluntly force clear all the + * transports. Things might change after we implement Nagle. + * But such an examination is still required. + * + * --xguo + */ + while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) { + struct sctp_transport *t = list_entry(ltransport, + struct sctp_transport, + send_ready); + if (t != transport) + transport = t; + + packet = &transport->packet; + if (packet->size != SCTP_IP_OVERHEAD) + error = (*q->force_output)(packet); + } + + return error; +} + +/* Set the various output handling callbacks. */ +int sctp_outq_set_output_handlers(struct sctp_outq *q, + sctp_outq_ohandler_init_t init, + sctp_outq_ohandler_config_t config, + sctp_outq_ohandler_t append, + sctp_outq_ohandler_t build, + sctp_outq_ohandler_force_t force) +{ + q->init_output = init; + q->config_output = config; + q->append_output = append; + q->build_output = build; + q->force_output = force; + return 0; +} + +/* Update unack_data based on the incoming SACK chunk */ +static void sctp_sack_update_unack_data(struct sctp_association *assoc, + sctp_sackhdr_t *sack) +{ + sctp_sack_variable_t *frags; + __u16 unack_data; + int i; + + unack_data = assoc->next_tsn - assoc->ctsn_ack_point - 1; + + frags = sack->variable; + for (i = 0; i < ntohs(sack->num_gap_ack_blocks); i++) { + unack_data -= ((ntohs(frags[i].gab.end) - + ntohs(frags[i].gab.start) + 1)); + } + + assoc->unack_data = unack_data; +} + +/* Return the highest new tsn that is acknowledged by the given SACK chunk. */ +static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack, + struct sctp_association *asoc) +{ + struct list_head *ltransport, *lchunk; + struct sctp_transport *transport; + sctp_chunk_t *chunk; + __u32 highest_new_tsn, tsn; + struct list_head *transport_list = &asoc->peer.transport_addr_list; + + highest_new_tsn = ntohl(sack->cum_tsn_ack); + + list_for_each(ltransport, transport_list) { + transport = list_entry(ltransport, struct sctp_transport, + transports); + list_for_each(lchunk, &transport->transmitted) { + chunk = list_entry(lchunk, sctp_chunk_t, + transmitted_list); + tsn = ntohl(chunk->subh.data_hdr->tsn); + + if (!chunk->tsn_gap_acked && + TSN_lt(highest_new_tsn, tsn) && + sctp_acked(sack, tsn)) + highest_new_tsn = tsn; + } + } + + return highest_new_tsn; +} + +/* This is where we REALLY process a SACK. + * + * Process the SACK against the outqueue. Mostly, this just frees + * things off the transmitted queue. + */ +int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) +{ + struct sctp_association *asoc = q->asoc; + struct sctp_transport *transport; + sctp_chunk_t *tchunk; + struct list_head *lchunk, *transport_list, *pos; + sctp_sack_variable_t *frags = sack->variable; + __u32 sack_ctsn, ctsn, tsn; + __u32 highest_tsn, highest_new_tsn; + __u32 sack_a_rwnd; + int outstanding; + + /* Grab the association's destination address list. */ + transport_list = &asoc->peer.transport_addr_list; + + sack_ctsn = ntohl(sack->cum_tsn_ack); + + /* Get the highest TSN in the sack. */ + highest_tsn = sack_ctsn + + ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end); + + if (TSN_lt(asoc->highest_sacked, highest_tsn)) { + highest_new_tsn = highest_tsn; + asoc->highest_sacked = highest_tsn; + } else { + highest_new_tsn = sctp_highest_new_tsn(sack, asoc); + } + + /* Run through the retransmit queue. Credit bytes received + * and free those chunks that we can. + */ + sctp_check_transmitted(q, &q->retransmit, NULL, sack, highest_new_tsn); + + /* Run through the transmitted queue. + * Credit bytes received and free those chunks which we can. + * + * This is a MASSIVE candidate for optimization. + */ + list_for_each(pos, transport_list) { + transport = list_entry(pos, struct sctp_transport, + transports); + sctp_check_transmitted(q, &transport->transmitted, + transport, sack, highest_new_tsn); + } + + /* Move the Cumulative TSN Ack Point if appropriate. */ + if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn)) + asoc->ctsn_ack_point = sack_ctsn; + + /* Update unack_data field in the assoc. */ + sctp_sack_update_unack_data(asoc, sack); + + ctsn = asoc->ctsn_ack_point; + + SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n", + __FUNCTION__, sack_ctsn); + SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association " + "%p is 0x%x.\n", __FUNCTION__, asoc, ctsn); + + /* Throw away stuff rotting on the sack queue. */ + list_for_each(lchunk, &q->sacked) { + tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + tsn = ntohl(tchunk->subh.data_hdr->tsn); + if (TSN_lte(tsn, ctsn)) { + lchunk = lchunk->prev; + sctp_free_chunk(tchunk); + } + } + + /* ii) Set rwnd equal to the newly received a_rwnd minus the + * number of bytes still outstanding after processing the + * Cumulative TSN Ack and the Gap Ack Blocks. + */ + + sack_a_rwnd = ntohl(sack->a_rwnd); + outstanding = q->outstanding_bytes; + + if (outstanding < sack_a_rwnd) + sack_a_rwnd -= outstanding; + else + sack_a_rwnd = 0; + + asoc->peer.rwnd = sack_a_rwnd; + + /* See if all chunks are acked. + * Make sure the empty queue handler will get run later. + */ + q->empty = skb_queue_empty(&q->out) && list_empty(&q->retransmit); + if (!q->empty) + goto finish; + + list_for_each(pos, transport_list) { + transport = list_entry(pos, struct sctp_transport, + transports); + q->empty = q->empty && list_empty(&transport->transmitted); + if (!q->empty) + goto finish; + } + + SCTP_DEBUG_PRINTK("sack queue is empty.\n"); +finish: + return q->empty; +} + +/* Is the outqueue empty? */ +int sctp_outq_is_empty(const struct sctp_outq *q) +{ + return q->empty; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* Go through a transport's transmitted list or the assocication's retransmit + * list and move chunks that are acked by the Cumulative TSN Ack to q->sacked. + * The retransmit list will not have an associated transport. In case of a + * transmitted list with a transport, the transport's congestion, rto and fast + * retransmit parameters are also updated and if needed a fast retransmit + * process is started. + * + * I added coherent debug information output. --xguo + * + * Instead of printing 'sacked' or 'kept' for each TSN on the + * transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5. + * KEPT TSN6-TSN7, etc. + */ +static void sctp_check_transmitted(struct sctp_outq *q, + struct list_head *transmitted_queue, + struct sctp_transport *transport, + sctp_sackhdr_t *sack, + __u32 highest_new_tsn_in_sack) +{ + struct list_head *lchunk; + sctp_chunk_t *tchunk; + struct list_head tlist; + __u32 tsn; + __u32 sack_ctsn; + __u32 rtt; + __u8 restart_timer = 0; + __u8 do_fast_retransmit = 0; + int bytes_acked = 0; + + /* These state variables are for coherent debug output. --xguo */ + +#if SCTP_DEBUG + __u32 dbg_ack_tsn = 0; /* An ACKed TSN range starts here... */ + __u32 dbg_last_ack_tsn = 0; /* ...and finishes here. */ + __u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here... */ + __u32 dbg_last_kept_tsn = 0; /* ...and finishes here. */ + + /* 0 : The last TSN was ACKed. + * 1 : The last TSN was NOT ACKed (i.e. KEPT). + * -1: We need to initialize. + */ + int dbg_prt_state = -1; +#endif /* SCTP_DEBUG */ + + sack_ctsn = ntohl(sack->cum_tsn_ack); + + INIT_LIST_HEAD(&tlist); + + /* The while loop will skip empty transmitted queues. */ + while (NULL != (lchunk = sctp_list_dequeue(transmitted_queue))) { + tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + + tsn = ntohl(tchunk->subh.data_hdr->tsn); + if (sctp_acked(sack, tsn)) { + /* If this queue is the retransmit queue, the + * retransmit timer has already reclaimed + * the outstanding bytes for this chunk, so only + * count bytes associated with a transport. + */ + if (transport) { + /* If this chunk is being used for RTT + * measurement, calculate the RTT and update + * the RTO using this value. + * + * 6.3.1 C5) Karn's algorithm: RTT measurements + * MUST NOT be made using packets that were + * retransmitted (and thus for which it is + * ambiguous whether the reply was for the + * first instance of the packet or a later + * instance). + */ + if ((!tchunk->tsn_gap_acked) && + (1 == tchunk->num_times_sent) && + (tchunk->rtt_in_progress)) { + rtt = jiffies - tchunk->sent_at; + sctp_transport_update_rto(transport, + rtt); + } + } + if (TSN_lte(tsn, sack_ctsn)) { + /* RFC 2960 6.3.2 Retransmission Timer Rules + * + * R3) Whenever a SACK is received + * that acknowledges the DATA chunk + * with the earliest outstanding TSN + * for that address, restart T3-rtx + * timer for that address with its + * current RTO. + */ + restart_timer = 1; + + if (!tchunk->tsn_gap_acked) { + tchunk->tsn_gap_acked = 1; + bytes_acked += sctp_data_size(tchunk); + } + + list_add_tail(&tchunk->transmitted_list, + &q->sacked); + } else { + /* RFC2960 7.2.4, sctpimpguide-05 2.8.2 + * M2) Each time a SACK arrives reporting + * 'Stray DATA chunk(s)' record the highest TSN + * reported as newly acknowledged, call this + * value 'HighestTSNinSack'. A newly + * acknowledged DATA chunk is one not + * previously acknowledged in a SACK. + * + * When the SCTP sender of data receives a SACK + * chunk that acknowledges, for the first time, + * the receipt of a DATA chunk, all the still + * unacknowledged DATA chunks whose TSN is + * older than that newly acknowledged DATA + * chunk, are qualified as 'Stray DATA chunks'. + */ + if (!tchunk->tsn_gap_acked) { + tchunk->tsn_gap_acked = 1; + bytes_acked += sctp_data_size(tchunk); + } + list_add_tail(lchunk, &tlist); + } + +#if SCTP_DEBUG + switch (dbg_prt_state) { + case 0: /* last TSN was ACKed */ + if (dbg_last_ack_tsn + 1 == tsn) { + /* This TSN belongs to the + * current ACK range. + */ + break; + } + + if (dbg_last_ack_tsn != dbg_ack_tsn) { + /* Display the end of the + * current range. + */ + SCTP_DEBUG_PRINTK("-%08x", + dbg_last_ack_tsn); + } + + /* Start a new range. */ + SCTP_DEBUG_PRINTK(",%08x", tsn); + dbg_ack_tsn = tsn; + break; + + case 1: /* The last TSN was NOT ACKed. */ + if (dbg_last_kept_tsn != dbg_kept_tsn) { + /* Display the end of current range. */ + SCTP_DEBUG_PRINTK("-%08x", + dbg_last_kept_tsn); + } + + SCTP_DEBUG_PRINTK("\n"); + + /* FALL THROUGH... */ + default: + /* This is the first-ever TSN we examined. */ + /* Start a new range of ACK-ed TSNs. */ + SCTP_DEBUG_PRINTK("ACKed: %08x", tsn); + dbg_prt_state = 0; + dbg_ack_tsn = tsn; + }; + + dbg_last_ack_tsn = tsn; +#endif /* SCTP_DEBUG */ + + } else { + if (tchunk->tsn_gap_acked) { + SCTP_DEBUG_PRINTK("%s: Receiver reneged on " + "data TSN: 0x%x\n", + __FUNCTION__, + tsn); + tchunk->tsn_gap_acked = 0; + + bytes_acked -= sctp_data_size(tchunk); + + /* RFC 2960 6.3.2 Retransmission Timer Rules + * + * R4) Whenever a SACK is received missing a + * TSN that was previously acknowledged via a + * Gap Ack Block, start T3-rtx for the + * destination address to which the DATA + * chunk was originally + * transmitted if it is not already running. + */ + restart_timer = 1; + } + + list_add_tail(lchunk, &tlist); + +#if SCTP_DEBUG + /* See the above comments on ACK-ed TSNs. */ + switch (dbg_prt_state) { + case 1: + if (dbg_last_kept_tsn + 1 == tsn) + break; + + if (dbg_last_kept_tsn != dbg_kept_tsn) + SCTP_DEBUG_PRINTK("-%08x", + dbg_last_kept_tsn); + + SCTP_DEBUG_PRINTK(",%08x", tsn); + dbg_kept_tsn = tsn; + break; + + case 0: + if (dbg_last_ack_tsn != dbg_ack_tsn) + SCTP_DEBUG_PRINTK("-%08x", + dbg_last_ack_tsn); + SCTP_DEBUG_PRINTK("\n"); + + /* FALL THROUGH... */ + default: + SCTP_DEBUG_PRINTK("KEPT: %08x",tsn); + dbg_prt_state = 1; + dbg_kept_tsn = tsn; + }; + + dbg_last_kept_tsn = tsn; +#endif /* SCTP_DEBUG */ + } + } + +#if SCTP_DEBUG + /* Finish off the last range, displaying its ending TSN. */ + switch (dbg_prt_state) { + case 0: + if (dbg_last_ack_tsn != dbg_ack_tsn) { + SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_ack_tsn); + } else { + SCTP_DEBUG_PRINTK("\n"); + } + break; + + case 1: + if (dbg_last_kept_tsn != dbg_kept_tsn) { + SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_kept_tsn); + } else { + SCTP_DEBUG_PRINTK("\n"); + } + }; +#endif /* SCTP_DEBUG */ + if (transport) { + if (bytes_acked) { + /* 8.2. When an outstanding TSN is acknowledged, + * the endpoint shall clear the error counter of + * the destination transport address to which the + * DATA chunk was last sent. + * The association's overall error counter is + * also cleared. + */ + transport->error_count = 0; + transport->asoc->overall_error_count = 0; + + /* Mark the destination transport address as + * active if it is not so marked. + */ + if (!transport->active) { + sctp_assoc_control_transport( + transport->asoc, + transport, + SCTP_TRANSPORT_UP, + SCTP_RECEIVED_SACK); + } + + sctp_transport_raise_cwnd(transport, sack_ctsn, + bytes_acked); + + transport->flight_size -= bytes_acked; + q->outstanding_bytes -= bytes_acked; + } else { + /* RFC 2960 6.1, sctpimpguide-06 2.15.2 + * When a sender is doing zero window probing, it + * should not timeout the association if it continues + * to receive new packets from the receiver. The + * reason is that the receiver MAY keep its window + * closed for an indefinite time. + * A sender is doing zero window probing when the + * receiver's advertised window is zero, and there is + * only one data chunk in flight to the receiver. + */ + if ((0 == q->asoc->peer.rwnd) && + (!list_empty(&tlist)) && + (sack_ctsn+2 == q->asoc->next_tsn)) { + SCTP_DEBUG_PRINTK("%s: SACK received for zero " + "window probe: %u\n", + __FUNCTION__, sack_ctsn); + q->asoc->overall_error_count = 0; + transport->error_count = 0; + } + } + + /* RFC 2960 6.3.2 Retransmission Timer Rules + * + * R2) Whenever all outstanding data sent to an address have + * been acknowledged, turn off the T3-rtx timer of that + * address. + */ + if (!transport->flight_size) { + if (timer_pending(&transport->T3_rtx_timer) && + del_timer(&transport->T3_rtx_timer)) { + sctp_transport_put(transport); + } + } else if (restart_timer) { + if (!mod_timer(&transport->T3_rtx_timer, + jiffies + transport->rto)) + sctp_transport_hold(transport); + } + } + + /* Reconstruct the transmitted list with chunks that are not yet + * acked by the Cumulative TSN Ack. + */ + while (NULL != (lchunk = sctp_list_dequeue(&tlist))) { + tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + tsn = ntohl(tchunk->subh.data_hdr->tsn); + + /* RFC 2960 7.2.4, sctpimpguide-05 2.8.2 M3) Examine all + * 'Unacknowledged TSN's', if the TSN number of an + * 'Unacknowledged TSN' is smaller than the 'HighestTSNinSack' + * value, increment the 'TSN.Missing.Report' count on that + * chunk if it has NOT been fast retransmitted or marked for + * fast retransmit already. + * + * M4) If any DATA chunk is found to have a + * 'TSN.Missing.Report' + * value larger than or equal to 4, mark that chunk for + * retransmission and start the fast retransmit procedure. + */ + if ((!tchunk->fast_retransmit) && + (!tchunk->tsn_gap_acked) && + (TSN_lt(tsn, highest_new_tsn_in_sack))) { + tchunk->tsn_missing_report++; + SCTP_DEBUG_PRINTK("%s: TSN 0x%x missing counter: %d\n", + __FUNCTION__, tsn, + tchunk->tsn_missing_report); + } + if (tchunk->tsn_missing_report >= 4) { + tchunk->fast_retransmit = 1; + do_fast_retransmit = 1; + } + + list_add_tail(lchunk, transmitted_queue); + } + + if (transport) { + if (do_fast_retransmit) + sctp_retransmit(q, transport, SCTP_RTXR_FAST_RTX); + + SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, " + "ssthresh: %d, flight_size: %d, pba: %d\n", + __FUNCTION__, transport, transport->cwnd, + transport->ssthresh, transport->flight_size, + transport->partial_bytes_acked); + } +} + +/* Is the given TSN acked by this packet? */ +static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn) +{ + int i; + sctp_sack_variable_t *frags; + __u16 gap; + __u32 ctsn = ntohl(sack->cum_tsn_ack); + + if (TSN_lte(tsn, ctsn)) + goto pass; + + /* 3.3.4 Selective Acknowledgement (SACK) (3): + * + * Gap Ack Blocks: + * These fields contain the Gap Ack Blocks. They are repeated + * for each Gap Ack Block up to the number of Gap Ack Blocks + * defined in the Number of Gap Ack Blocks field. All DATA + * chunks with TSNs greater than or equal to (Cumulative TSN + * Ack + Gap Ack Block Start) and less than or equal to + * (Cumulative TSN Ack + Gap Ack Block End) of each Gap Ack + * Block are assumed to have been received correctly. + */ + + frags = sack->variable; + gap = tsn - ctsn; + for (i = 0; i < ntohs(sack->num_gap_ack_blocks); ++i) { + if (TSN_lte(ntohs(frags[i].gab.start), gap) && + TSN_lte(gap, ntohs(frags[i].gab.end))) + goto pass; + } + + return 0; +pass: + return 1; +} diff -urN linux-2.4.22-bk23/net/sctp/primitive.c linux-2.4.22-bk24/net/sctp/primitive.c --- linux-2.4.22-bk23/net/sctp/primitive.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/primitive.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,205 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions implement the SCTP primitive functions from Section 10. + * + * Note that the descriptions from the specification are USER level + * functions--this file is the functions which populate the struct proto + * for SCTP which is the BOTTOM of the sockets interface. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Narasimha Budihal + * Karl Knutson + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include /* For struct list_head */ +#include +#include +#include /* For struct timeval */ +#include +#include +#include + +#define DECLARE_PRIMITIVE(name) \ +/* This is called in the code as sctp_primitive_ ## name. */ \ +int sctp_primitive_ ## name(struct sctp_association *asoc, \ + void *arg) { \ + int error = 0; \ + sctp_event_t event_type; sctp_subtype_t subtype; \ + sctp_state_t state; \ + struct sctp_endpoint *ep; \ + \ + event_type = SCTP_EVENT_T_PRIMITIVE; \ + subtype = SCTP_ST_PRIMITIVE(SCTP_PRIMITIVE_ ## name); \ + state = asoc ? asoc->state : SCTP_STATE_CLOSED; \ + ep = asoc ? asoc->ep : NULL; \ + \ + error = sctp_do_sm(event_type, subtype, state, ep, asoc, \ + arg, GFP_KERNEL); \ + return error; \ +} + +/* 10.1 ULP-to-SCTP + * B) Associate + * + * Format: ASSOCIATE(local SCTP instance name, destination transport addr, + * outbound stream count) + * -> association id [,destination transport addr list] [,outbound stream + * count] + * + * This primitive allows the upper layer to initiate an association to a + * specific peer endpoint. + * + * This version assumes that asoc is fully populated with the initial + * parameters. We then return a traditional kernel indicator of + * success or failure. + */ + +/* This is called in the code as sctp_primitive_ASSOCIATE. */ + +DECLARE_PRIMITIVE(ASSOCIATE) + +/* 10.1 ULP-to-SCTP + * C) Shutdown + * + * Format: SHUTDOWN(association id) + * -> result + * + * Gracefully closes an association. Any locally queued user data + * will be delivered to the peer. The association will be terminated only + * after the peer acknowledges all the SCTP packets sent. A success code + * will be returned on successful termination of the association. If + * attempting to terminate the association results in a failure, an error + * code shall be returned. + */ + +DECLARE_PRIMITIVE(SHUTDOWN); + +/* 10.1 ULP-to-SCTP + * C) Abort + * + * Format: Abort(association id [, cause code]) + * -> result + * + * Ungracefully closes an association. Any locally queued user data + * will be discarded and an ABORT chunk is sent to the peer. A success + * code will be returned on successful abortion of the association. If + * attempting to abort the association results in a failure, an error + * code shall be returned. + */ + +DECLARE_PRIMITIVE(ABORT); + +/* 10.1 ULP-to-SCTP + * E) Send + * + * Format: SEND(association id, buffer address, byte count [,context] + * [,stream id] [,life time] [,destination transport address] + * [,unorder flag] [,no-bundle flag] [,payload protocol-id] ) + * -> result + * + * This is the main method to send user data via SCTP. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * o buffer address - the location where the user message to be + * transmitted is stored; + * + * o byte count - The size of the user data in number of bytes; + * + * Optional attributes: + * + * o context - an optional 32 bit integer that will be carried in the + * sending failure notification to the ULP if the transportation of + * this User Message fails. + * + * o stream id - to indicate which stream to send the data on. If not + * specified, stream 0 will be used. + * + * o life time - specifies the life time of the user data. The user data + * will not be sent by SCTP after the life time expires. This + * parameter can be used to avoid efforts to transmit stale + * user messages. SCTP notifies the ULP if the data cannot be + * initiated to transport (i.e. sent to the destination via SCTP's + * send primitive) within the life time variable. However, the + * user data will be transmitted if SCTP has attempted to transmit a + * chunk before the life time expired. + * + * o destination transport address - specified as one of the destination + * transport addresses of the peer endpoint to which this packet + * should be sent. Whenever possible, SCTP should use this destination + * transport address for sending the packets, instead of the current + * primary path. + * + * o unorder flag - this flag, if present, indicates that the user + * would like the data delivered in an unordered fashion to the peer + * (i.e., the U flag is set to 1 on all DATA chunks carrying this + * message). + * + * o no-bundle flag - instructs SCTP not to bundle this user data with + * other outbound DATA chunks. SCTP MAY still bundle even when + * this flag is present, when faced with network congestion. + * + * o payload protocol-id - A 32 bit unsigned integer that is to be + * passed to the peer indicating the type of payload protocol data + * being transmitted. This value is passed as opaque data by SCTP. + */ + +DECLARE_PRIMITIVE(SEND); + +/* 10.1 ULP-to-SCTP + * J) Request Heartbeat + * + * Format: REQUESTHEARTBEAT(association id, destination transport address) + * + * -> result + * + * Instructs the local endpoint to perform a HeartBeat on the specified + * destination transport address of the given association. The returned + * result should indicate whether the transmission of the HEARTBEAT + * chunk to the destination address is successful. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * o destination transport address - the transport address of the + * association on which a heartbeat should be issued. + */ + +DECLARE_PRIMITIVE(REQUESTHEARTBEAT); diff -urN linux-2.4.22-bk23/net/sctp/proc.c linux-2.4.22-bk24/net/sctp/proc.c --- linux-2.4.22-bk23/net/sctp/proc.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/proc.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,129 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 2003 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Sridhar Samudrala + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include + +static char *sctp_snmp_list[] = { +#define SCTP_SNMP_ENTRY(x) #x + SCTP_SNMP_ENTRY(SctpCurrEstab), + SCTP_SNMP_ENTRY(SctpActiveEstabs), + SCTP_SNMP_ENTRY(SctpPassiveEstabs), + SCTP_SNMP_ENTRY(SctpAborteds), + SCTP_SNMP_ENTRY(SctpShutdowns), + SCTP_SNMP_ENTRY(SctpOutOfBlues), + SCTP_SNMP_ENTRY(SctpChecksumErrors), + SCTP_SNMP_ENTRY(SctpOutCtrlChunks), + SCTP_SNMP_ENTRY(SctpOutOrderChunks), + SCTP_SNMP_ENTRY(SctpOutUnorderChunks), + SCTP_SNMP_ENTRY(SctpInCtrlChunks), + SCTP_SNMP_ENTRY(SctpInOrderChunks), + SCTP_SNMP_ENTRY(SctpInUnorderChunks), + SCTP_SNMP_ENTRY(SctpFragUsrMsgs), + SCTP_SNMP_ENTRY(SctpReasmUsrMsgs), + SCTP_SNMP_ENTRY(SctpOutSCTPPacks), + SCTP_SNMP_ENTRY(SctpInSCTPPacks), +#undef SCTP_SNMP_ENTRY +}; + +/* Return the current value of a particular entry in the mib by adding its + * per cpu counters. + */ +static unsigned long +fold_field(void *mib[], int nr) +{ + unsigned long res = 0; + int i; + int sz = sizeof(struct sctp_mib); + unsigned long* begin; + + sz /= sizeof(unsigned long); + begin = (unsigned long*) mib; + + for (i=0; iproc_fops = &sctp_snmp_seq_fops; + + return 0; +} + +/* Cleanup the proc fs entry for 'snmp' object. */ +void sctp_snmp_proc_exit(void) +{ + remove_proc_entry("snmp", proc_net_sctp); +} diff -urN linux-2.4.22-bk23/net/sctp/protocol.c linux-2.4.22-bk24/net/sctp/protocol.c --- linux-2.4.22-bk23/net/sctp/protocol.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/protocol.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,1082 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2002 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * Initialization/cleanup for SCTP protocol support. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Sridhar Samudrala + * Daisy Chang + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global data structures. */ +struct sctp_protocol sctp_proto; +struct proc_dir_entry *proc_net_sctp; +DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics); + +/* This is the global socket data structure used for responding to + * the Out-of-the-blue (OOTB) packets. A control sock will be created + * for this socket at the initialization time. + */ +static struct socket *sctp_ctl_socket; + +static struct sctp_pf *sctp_pf_inet6_specific; +static struct sctp_pf *sctp_pf_inet_specific; +static struct sctp_af *sctp_af_v4_specific; +static struct sctp_af *sctp_af_v6_specific; + +extern struct net_proto_family inet_family_ops; + +extern int sctp_snmp_proc_init(void); +extern int sctp_snmp_proc_exit(void); + +/* Return the address of the control sock. */ +struct sock *sctp_get_ctl_sock(void) +{ + return sctp_ctl_socket->sk; +} + +/* Set up the proc fs entry for the SCTP protocol. */ +__init int sctp_proc_init(void) +{ + int rc = 0; + + if (!proc_net_sctp) { + struct proc_dir_entry *ent; + ent = proc_mkdir("net/sctp", 0); + if (ent) { + ent->owner = THIS_MODULE; + proc_net_sctp = ent; + } else + rc = -ENOMEM; + } + + if (sctp_snmp_proc_init()) + rc = -ENOMEM; + + return rc; +} + +/* Clean up the proc fs entry for the SCTP protocol. */ +void sctp_proc_exit(void) +{ + + sctp_snmp_proc_exit(); + + if (proc_net_sctp) { + proc_net_sctp = NULL; + remove_proc_entry("net/sctp", 0); + } +} + +/* Private helper to extract ipv4 address and stash them in + * the protocol structure. + */ +static void sctp_v4_copy_addrlist(struct list_head *addrlist, + struct net_device *dev) +{ + struct in_device *in_dev; + struct in_ifaddr *ifa; + struct sockaddr_storage_list *addr; + + read_lock(&inetdev_lock); + if ((in_dev = __in_dev_get(dev)) == NULL) { + read_unlock(&inetdev_lock); + return; + } + + read_lock(&in_dev->lock); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + /* Add the address to the local list. */ + addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC); + if (addr) { + addr->a.v4.sin_family = AF_INET; + addr->a.v4.sin_port = 0; + addr->a.v4.sin_addr.s_addr = ifa->ifa_local; + list_add_tail(&addr->list, addrlist); + } + } + + read_unlock(&in_dev->lock); + read_unlock(&inetdev_lock); +} + +/* Extract our IP addresses from the system and stash them in the + * protocol structure. + */ +static void __sctp_get_local_addr_list(struct sctp_protocol *proto) +{ + struct net_device *dev; + struct list_head *pos; + struct sctp_af *af; + + read_lock(&dev_base_lock); + for (dev = dev_base; dev; dev = dev->next) { + list_for_each(pos, &proto->address_families) { + af = list_entry(pos, struct sctp_af, list); + af->copy_addrlist(&proto->local_addr_list, dev); + } + } + read_unlock(&dev_base_lock); +} + +static void sctp_get_local_addr_list(struct sctp_protocol *proto) +{ + unsigned long flags; + + sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags); + __sctp_get_local_addr_list(&sctp_proto); + sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags); +} + +/* Free the existing local addresses. */ +static void __sctp_free_local_addr_list(struct sctp_protocol *proto) +{ + struct sockaddr_storage_list *addr; + struct list_head *pos, *temp; + + list_for_each_safe(pos, temp, &proto->local_addr_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + list_del(pos); + kfree(addr); + } +} + +/* Free the existing local addresses. */ +static void sctp_free_local_addr_list(struct sctp_protocol *proto) +{ + unsigned long flags; + + sctp_spin_lock_irqsave(&proto->local_addr_lock, flags); + __sctp_free_local_addr_list(proto); + sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags); +} + +/* Copy the local addresses which are valid for 'scope' into 'bp'. */ +int sctp_copy_local_addr_list(struct sctp_protocol *proto, + struct sctp_bind_addr *bp, sctp_scope_t scope, + int gfp, int copy_flags) +{ + struct sockaddr_storage_list *addr; + int error = 0; + struct list_head *pos; + unsigned long flags; + + sctp_spin_lock_irqsave(&proto->local_addr_lock, flags); + list_for_each(pos, &proto->local_addr_list) { + addr = list_entry(pos, struct sockaddr_storage_list, list); + if (sctp_in_scope(&addr->a, scope)) { + /* Now that the address is in scope, check to see if + * the address type is really supported by the local + * sock as well as the remote peer. + */ + if ((((AF_INET == addr->a.sa.sa_family) && + (copy_flags & SCTP_ADDR4_PEERSUPP))) || + (((AF_INET6 == addr->a.sa.sa_family) && + (copy_flags & SCTP_ADDR6_ALLOWED) && + (copy_flags & SCTP_ADDR6_PEERSUPP)))) { + error = sctp_add_bind_addr(bp, &addr->a, + GFP_ATOMIC); + if (error) + goto end_copy; + } + } + } + +end_copy: + sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags); + return error; +} + +/* Initialize a sctp_addr from in incoming skb. */ +static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb, + int is_saddr) +{ + void *from; + __u16 *port; + struct sctphdr *sh; + + port = &addr->v4.sin_port; + addr->v4.sin_family = AF_INET; + + sh = (struct sctphdr *) skb->h.raw; + if (is_saddr) { + *port = ntohs(sh->source); + from = &skb->nh.iph->saddr; + } else { + *port = ntohs(sh->dest); + from = &skb->nh.iph->daddr; + } + memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr)); +} + +/* Initialize an sctp_addr from a socket. */ +static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk) +{ + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = (sk)->num; + addr->v4.sin_addr.s_addr = (sk)->rcv_saddr; +} + +/* Initialize sk->rcv_saddr from sctp_addr. */ +static void sctp_v4_to_sk_saddr(union sctp_addr *addr, struct sock *sk) +{ + (sk)->rcv_saddr = addr->v4.sin_addr.s_addr; +} + +/* Initialize sk->daddr from sctp_addr. */ +static void sctp_v4_to_sk_daddr(union sctp_addr *addr, struct sock *sk) +{ + (sk)->daddr = addr->v4.sin_addr.s_addr; +} + +/* Initialize a sctp_addr from a dst_entry. */ +static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst, + unsigned short port) +{ + struct rtable *rt = (struct rtable *)dst; + saddr->v4.sin_family = AF_INET; + saddr->v4.sin_port = port; + saddr->v4.sin_addr.s_addr = rt->rt_src; +} + +/* Compare two addresses exactly. */ +static int sctp_v4_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2) +{ + if (addr1->sa.sa_family != addr2->sa.sa_family) + return 0; + if (addr1->v4.sin_port != addr2->v4.sin_port) + return 0; + if (addr1->v4.sin_addr.s_addr != addr2->v4.sin_addr.s_addr) + return 0; + + return 1; +} + +/* Initialize addr struct to INADDR_ANY. */ +static void sctp_v4_inaddr_any(union sctp_addr *addr, unsigned short port) +{ + addr->v4.sin_family = AF_INET; + addr->v4.sin_addr.s_addr = INADDR_ANY; + addr->v4.sin_port = port; +} + +/* Is this a wildcard address? */ +static int sctp_v4_is_any(const union sctp_addr *addr) +{ + return INADDR_ANY == addr->v4.sin_addr.s_addr; +} + +/* This function checks if the address is a valid address to be used for + * SCTP binding. + * + * Output: + * Return 0 - If the address is a non-unicast or an illegal address. + * Return 1 - If the address is a unicast. + */ +static int sctp_v4_addr_valid(union sctp_addr *addr) +{ + /* Is this a non-unicast address or a unusable SCTP address? */ + if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) + return 0; + + return 1; +} + +/* Should this be available for binding? */ +static int sctp_v4_available(const union sctp_addr *addr) +{ + int ret = inet_addr_type(addr->v4.sin_addr.s_addr); + + /* FIXME: ip_nonlocal_bind sysctl support. */ + + if (addr->v4.sin_addr.s_addr != INADDR_ANY && ret != RTN_LOCAL) + return 0; + return 1; +} + +/* Checking the loopback, private and other address scopes as defined in + * RFC 1918. The IPv4 scoping is based on the draft for SCTP IPv4 + * scoping . + * + * Level 0 - unusable SCTP addresses + * Level 1 - loopback address + * Level 2 - link-local addresses + * Level 3 - private addresses. + * Level 4 - global addresses + * For INIT and INIT-ACK address list, let L be the level of + * of requested destination address, sender and receiver + * SHOULD include all of its addresses with level greater + * than or equal to L. + */ +static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) +{ + sctp_scope_t retval; + + /* Should IPv4 scoping be a sysctl configurable option + * so users can turn it off (default on) for certain + * unconventional networking environments? + */ + + /* Check for unusable SCTP addresses. */ + if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) { + retval = SCTP_SCOPE_UNUSABLE; + } else if (LOOPBACK(addr->v4.sin_addr.s_addr)) { + retval = SCTP_SCOPE_LOOPBACK; + } else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) { + retval = SCTP_SCOPE_LINK; + } else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) { + retval = SCTP_SCOPE_PRIVATE; + } else { + retval = SCTP_SCOPE_GLOBAL; + } + + return retval; +} + +/* Returns a valid dst cache entry for the given source and destination ip + * addresses. If an association is passed, trys to get a dst entry with a + * source address that matches an address in the bind address list. + */ +struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, + union sctp_addr *daddr, + union sctp_addr *saddr) +{ + struct rtable *rt; + struct rt_key key; + sctp_bind_addr_t *bp; + rwlock_t *addr_lock; + struct sockaddr_storage_list *laddr; + struct list_head *pos; + struct dst_entry *dst = NULL; + union sctp_addr dst_saddr; + + memset(&key, 0x0, sizeof(struct rt_key)); + key.dst = daddr->v4.sin_addr.s_addr; + + if (saddr) + key.src = saddr->v4.sin_addr.s_addr; + + SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ", + __FUNCTION__, NIPQUAD(key.dst), + NIPQUAD(key.src)); + + if (!ip_route_output_key(&rt, &key)) { + dst = &rt->u.dst; + } + + /* If there is no association or if a source address is passed, no + * more validation is required. + */ + if (!asoc || saddr) + goto out; + + bp = &asoc->base.bind_addr; + addr_lock = &asoc->base.addr_lock; + + if (dst) { + /* Walk through the bind address list and look for a bind + * address that matches the source address of the returned dst. + */ + sctp_read_lock(addr_lock); + list_for_each(pos, &bp->address_list) { + laddr = list_entry(pos, struct sockaddr_storage_list, + list); + sctp_v4_dst_saddr(&dst_saddr, dst, bp->port); + if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) + goto out_unlock; + } + sctp_read_unlock(addr_lock); + + /* None of the bound addresses match the source address of the + * dst. So release it. + */ + dst_release(dst); + dst = NULL; + } + + /* Walk through the bind address list and try to get a dst that + * matches a bind address as the source address. + */ + sctp_read_lock(addr_lock); + list_for_each(pos, &bp->address_list) { + laddr = list_entry(pos, struct sockaddr_storage_list, list); + + if (AF_INET == laddr->a.sa.sa_family) { + key.src = laddr->a.v4.sin_addr.s_addr; + if (!ip_route_output_key(&rt, &key)) { + dst = &rt->u.dst; + goto out_unlock; + } + } + } + +out_unlock: + sctp_read_unlock(addr_lock); +out: + if (dst) + SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n", + NIPQUAD(rt->rt_dst), NIPQUAD(rt->rt_src)); + else + SCTP_DEBUG_PRINTK("NO ROUTE\n"); + + return dst; +} + +/* For v4, the source address is cached in the route entry(dst). So no need + * to cache it separately and hence this is an empty routine. + */ +void sctp_v4_get_saddr(struct sctp_association *asoc, + struct dst_entry *dst, + union sctp_addr *daddr, + union sctp_addr *saddr) +{ + struct rtable *rt = (struct rtable *)dst; + + if (rt) { + saddr->v4.sin_family = AF_INET; + saddr->v4.sin_port = asoc->base.bind_addr.port; + saddr->v4.sin_addr.s_addr = rt->rt_src; + } +} + +/* What interface did this skb arrive on? */ +int sctp_v4_skb_iif(const struct sk_buff *skb) +{ + return ((struct rtable *)skb->dst)->rt_iif; +} + +/* Create and initialize a new sk for the socket returned by accept(). */ +struct sock *sctp_v4_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) +{ + struct sock *newsk; + struct inet_opt *inet = inet_sk(sk); + struct inet_opt *newinet; + + newsk = sk_alloc(PF_INET, GFP_KERNEL, sizeof(struct sock)); + if (!newsk) + goto out; + + sock_init_data(NULL, newsk); + sk_set_owner(newsk, THIS_MODULE); + + newsk->type = SOCK_STREAM; + + newsk->prot = sk->prot; + newsk->no_check = sk->no_check; + newsk->reuse = sk->reuse; + newsk->shutdown = sk->shutdown; + + newsk->destruct = inet_sock_destruct; + newsk->zapped = 0; + newsk->family = PF_INET; + newsk->protocol = IPPROTO_SCTP; + newsk->backlog_rcv = sk->prot->backlog_rcv; + + newinet = inet_sk(newsk); + + /* Initialize sk's sport, dport, rcv_saddr and daddr for + * getsockname() and getpeername() + */ + newsk->sport = sk->sport; + newsk->saddr = sk->saddr; + newsk->rcv_saddr = sk->rcv_saddr; + newsk->dport = htons(asoc->peer.port); + newsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr; + newinet->pmtudisc = inet->pmtudisc; + newinet->id = 0; + + newinet->ttl = sysctl_ip_default_ttl; + newinet->mc_loop = 1; + newinet->mc_ttl = 1; + newinet->mc_index = 0; + newinet->mc_list = NULL; + +#ifdef INET_REFCNT_DEBUG + atomic_inc(&inet_sock_nr); +#endif + + if (0 != newsk->prot->init(newsk)) { + inet_sock_release(newsk); + newsk = NULL; + } + +out: + return newsk; +} + +/* Event handler for inet address addition/deletion events. + * Basically, whenever there is an event, we re-build our local address list. + */ +static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, + void *ptr) +{ + unsigned long flags; + + sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags); + __sctp_free_local_addr_list(&sctp_proto); + __sctp_get_local_addr_list(&sctp_proto); + sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags); + + return NOTIFY_DONE; +} + +/* + * Initialize the control inode/socket with a control endpoint data + * structure. This endpoint is reserved exclusively for the OOTB processing. + */ +int sctp_ctl_sock_init(void) +{ + int err; + sa_family_t family; + + if (sctp_get_pf_specific(PF_INET6)) + family = PF_INET6; + else + family = PF_INET; + + err = sock_create(family, SOCK_SEQPACKET, IPPROTO_SCTP, + &sctp_ctl_socket); + if (err < 0) { + printk(KERN_ERR + "SCTP: Failed to create the SCTP control socket.\n"); + return err; + } + sctp_ctl_socket->sk->allocation = GFP_ATOMIC; + inet_sk(sctp_ctl_socket->sk)->ttl = MAXTTL; + + return 0; +} + +/* Register address family specific functions. */ +int sctp_register_af(struct sctp_af *af) +{ + switch (af->sa_family) { + case AF_INET: + if (sctp_af_v4_specific) + return 0; + sctp_af_v4_specific = af; + break; + case AF_INET6: + if (sctp_af_v6_specific) + return 0; + sctp_af_v6_specific = af; + break; + default: + return 0; + } + + INIT_LIST_HEAD(&af->list); + list_add_tail(&af->list, &sctp_proto.address_families); + return 1; +} + +/* Get the table of functions for manipulating a particular address + * family. + */ +struct sctp_af *sctp_get_af_specific(sa_family_t family) +{ + switch (family) { + case AF_INET: + return sctp_af_v4_specific; + case AF_INET6: + return sctp_af_v6_specific; + default: + return NULL; + } +} + +/* Common code to initialize a AF_INET msg_name. */ +static void sctp_inet_msgname(char *msgname, int *addr_len) +{ + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)msgname; + *addr_len = sizeof(struct sockaddr_in); + sin->sin_family = AF_INET; + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); +} + +/* Copy the primary address of the peer primary address as the msg_name. */ +static void sctp_inet_event_msgname(struct sctp_ulpevent *event, char *msgname, + int *addr_len) +{ + struct sockaddr_in *sin, *sinfrom; + + if (msgname) { + sctp_inet_msgname(msgname, addr_len); + sin = (struct sockaddr_in *)msgname; + sinfrom = &event->event_asoc->peer.primary_addr.v4; + sin->sin_port = htons(event->event_asoc->peer.port); + sin->sin_addr.s_addr = sinfrom->sin_addr.s_addr; + } +} + +/* Initialize and copy out a msgname from an inbound skb. */ +static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *len) +{ + struct sctphdr *sh; + struct sockaddr_in *sin; + + if (msgname) { + sctp_inet_msgname(msgname, len); + sin = (struct sockaddr_in *)msgname; + sh = (struct sctphdr *)skb->h.raw; + sin->sin_port = sh->source; + sin->sin_addr.s_addr = skb->nh.iph->saddr; + } +} + +/* Do we support this AF? */ +static int sctp_inet_af_supported(sa_family_t family) +{ + /* PF_INET only supports AF_INET addresses. */ + return (AF_INET == family); +} + +/* Address matching with wildcards allowed. */ +static int sctp_inet_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2, + struct sctp_opt *opt) +{ + /* PF_INET only supports AF_INET addresses. */ + if (addr1->sa.sa_family != addr2->sa.sa_family) + return 0; + if (INADDR_ANY == addr1->v4.sin_addr.s_addr || + INADDR_ANY == addr2->v4.sin_addr.s_addr) + return 1; + if (addr1->v4.sin_addr.s_addr == addr2->v4.sin_addr.s_addr) + return 1; + + return 0; +} + +/* Verify that provided sockaddr looks bindable. Common verification has + * already been taken care of. + */ +static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + return sctp_v4_available(addr); +} + +/* Verify that sockaddr looks sendable. Common verification has already + * been taken care of. + */ +static int sctp_inet_send_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + return 1; +} + +/* Fill in Supported Address Type information for INIT and INIT-ACK + * chunks. Returns number of addresses supported. + */ +static int sctp_inet_supported_addrs(const struct sctp_opt *opt, + __u16 *types) +{ + types[0] = SCTP_PARAM_IPV4_ADDRESS; + return 1; +} + +/* Wrapper routine that calls the ip transmit routine. */ +static inline int sctp_v4_xmit(struct sk_buff *skb, + struct sctp_transport *transport, int ipfragok) +{ + SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, " + "src:%u.%u.%u.%u, dst:%u.%u.%u.%u\n", + __FUNCTION__, skb, skb->len, + NIPQUAD(((struct rtable *)skb->dst)->rt_src), + NIPQUAD(((struct rtable *)skb->dst)->rt_dst)); + + SCTP_INC_STATS(SctpOutSCTPPacks); + return ip_queue_xmit(skb, ipfragok); +} + +struct sctp_af sctp_ipv4_specific; + +static struct sctp_pf sctp_pf_inet = { + .event_msgname = sctp_inet_event_msgname, + .skb_msgname = sctp_inet_skb_msgname, + .af_supported = sctp_inet_af_supported, + .cmp_addr = sctp_inet_cmp_addr, + .bind_verify = sctp_inet_bind_verify, + .send_verify = sctp_inet_send_verify, + .supported_addrs = sctp_inet_supported_addrs, + .create_accept_sk = sctp_v4_create_accept_sk, + .af = &sctp_ipv4_specific, +}; + +/* Notifier for inetaddr addition/deletion events. */ +struct notifier_block sctp_inetaddr_notifier = { + .notifier_call = sctp_inetaddr_event, +}; + +/* Socket operations. */ +struct proto_ops inet_seqpacket_ops = { + .family = PF_INET, + .release = inet_release, /* Needs to be wrapped... */ + .bind = inet_bind, + .connect = inet_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = inet_accept, + .getname = inet_getname, /* Semantics are different. */ + .poll = sctp_poll, + .ioctl = inet_ioctl, + .listen = sctp_inet_listen, + .shutdown = inet_shutdown, /* Looks harmless. */ + .setsockopt = inet_setsockopt, /* IP_SOL IP_OPTION is a problem. */ + .getsockopt = inet_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = inet_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +/* Registration with AF_INET family. */ +static struct inet_protosw sctp_seqpacket_protosw = { + .type = SOCK_SEQPACKET, + .protocol = IPPROTO_SCTP, + .prot = &sctp_prot, + .ops = &inet_seqpacket_ops, + .capability = -1, + .no_check = 0, + .flags = SCTP_PROTOSW_FLAG +}; +static struct inet_protosw sctp_stream_protosw = { + .type = SOCK_STREAM, + .protocol = IPPROTO_SCTP, + .prot = &sctp_prot, + .ops = &inet_seqpacket_ops, + .capability = -1, + .no_check = 0, + .flags = SCTP_PROTOSW_FLAG +}; + +/* Register with IP layer. */ +static struct inet_protocol sctp_protocol = { + .handler = sctp_rcv, + .err_handler = sctp_v4_err, + .protocol = IPPROTO_SCTP, + .name = "SCTP" +}; + +/* IPv4 address related functions. */ +struct sctp_af sctp_ipv4_specific = { + .sctp_xmit = sctp_v4_xmit, + .setsockopt = ip_setsockopt, + .getsockopt = ip_getsockopt, + .get_dst = sctp_v4_get_dst, + .get_saddr = sctp_v4_get_saddr, + .copy_addrlist = sctp_v4_copy_addrlist, + .from_skb = sctp_v4_from_skb, + .from_sk = sctp_v4_from_sk, + .to_sk_saddr = sctp_v4_to_sk_saddr, + .to_sk_daddr = sctp_v4_to_sk_daddr, + .dst_saddr = sctp_v4_dst_saddr, + .cmp_addr = sctp_v4_cmp_addr, + .addr_valid = sctp_v4_addr_valid, + .inaddr_any = sctp_v4_inaddr_any, + .is_any = sctp_v4_is_any, + .available = sctp_v4_available, + .scope = sctp_v4_scope, + .skb_iif = sctp_v4_skb_iif, + .net_header_len = sizeof(struct iphdr), + .sockaddr_len = sizeof(struct sockaddr_in), + .sa_family = AF_INET, +}; + +struct sctp_pf *sctp_get_pf_specific(sa_family_t family) { + + switch (family) { + case PF_INET: + return sctp_pf_inet_specific; + case PF_INET6: + return sctp_pf_inet6_specific; + default: + return NULL; + } +} + +/* Register the PF specific function table. */ +int sctp_register_pf(struct sctp_pf *pf, sa_family_t family) +{ + switch (family) { + case PF_INET: + if (sctp_pf_inet_specific) + return 0; + sctp_pf_inet_specific = pf; + break; + case PF_INET6: + if (sctp_pf_inet6_specific) + return 0; + sctp_pf_inet6_specific = pf; + break; + default: + return 0; + } + return 1; +} + +static int __init init_sctp_mibs(void) +{ + return 0; +} + +static void cleanup_sctp_mibs(void) +{ +} + +/* Initialize the universe into something sensible. */ +__init int sctp_init(void) +{ + int i; + int status = 0; + + /* SCTP_DEBUG sanity check. */ + if (!sctp_sanity_check()) + return -EINVAL; + + /* Add SCTP to inet_protos hash table. */ + inet_add_protocol(&sctp_protocol); + + /* Add SCTP(TCP and UDP style) to inetsw linked list. */ + inet_register_protosw(&sctp_seqpacket_protosw); + inet_register_protosw(&sctp_stream_protosw); + + /* Allocate and initialise sctp mibs. */ + status = init_sctp_mibs(); + if (status) + goto err_init_mibs; + + /* Initialize proc fs directory. */ + sctp_proc_init(); + + /* Initialize object count debugging. */ + sctp_dbg_objcnt_init(); + + /* Initialize the SCTP specific PF functions. */ + sctp_register_pf(&sctp_pf_inet, PF_INET); + /* + * 14. Suggested SCTP Protocol Parameter Values + */ + /* The following protocol parameters are RECOMMENDED: */ + /* RTO.Initial - 3 seconds */ + sctp_proto.rto_initial = SCTP_RTO_INITIAL; + /* RTO.Min - 1 second */ + sctp_proto.rto_min = SCTP_RTO_MIN; + /* RTO.Max - 60 seconds */ + sctp_proto.rto_max = SCTP_RTO_MAX; + /* RTO.Alpha - 1/8 */ + sctp_proto.rto_alpha = SCTP_RTO_ALPHA; + /* RTO.Beta - 1/4 */ + sctp_proto.rto_beta = SCTP_RTO_BETA; + + /* Valid.Cookie.Life - 60 seconds */ + sctp_proto.valid_cookie_life = 60 * HZ; + + /* Whether Cookie Preservative is enabled(1) or not(0) */ + sctp_proto.cookie_preserve_enable = 1; + + /* Max.Burst - 4 */ + sctp_proto.max_burst = SCTP_MAX_BURST; + + /* Association.Max.Retrans - 10 attempts + * Path.Max.Retrans - 5 attempts (per destination address) + * Max.Init.Retransmits - 8 attempts + */ + sctp_proto.max_retrans_association = 10; + sctp_proto.max_retrans_path = 5; + sctp_proto.max_retrans_init = 8; + + /* HB.interval - 30 seconds */ + sctp_proto.hb_interval = 30 * HZ; + + /* Implementation specific variables. */ + + /* Initialize default stream count setup information. */ + sctp_proto.max_instreams = SCTP_DEFAULT_INSTREAMS; + sctp_proto.max_outstreams = SCTP_DEFAULT_OUTSTREAMS; + + /* Allocate and initialize the association hash table. */ + sctp_proto.assoc_hashsize = 4096; + sctp_proto.assoc_hashbucket = (sctp_hashbucket_t *) + kmalloc(4096 * sizeof(sctp_hashbucket_t), GFP_KERNEL); + if (!sctp_proto.assoc_hashbucket) { + printk(KERN_ERR "SCTP: Failed association hash alloc.\n"); + status = -ENOMEM; + goto err_ahash_alloc; + } + for (i = 0; i < sctp_proto.assoc_hashsize; i++) { + sctp_proto.assoc_hashbucket[i].lock = RW_LOCK_UNLOCKED; + sctp_proto.assoc_hashbucket[i].chain = NULL; + } + + /* Allocate and initialize the endpoint hash table. */ + sctp_proto.ep_hashsize = 64; + sctp_proto.ep_hashbucket = (sctp_hashbucket_t *) + kmalloc(64 * sizeof(sctp_hashbucket_t), GFP_KERNEL); + if (!sctp_proto.ep_hashbucket) { + printk(KERN_ERR "SCTP: Failed endpoint_hash alloc.\n"); + status = -ENOMEM; + goto err_ehash_alloc; + } + + for (i = 0; i < sctp_proto.ep_hashsize; i++) { + sctp_proto.ep_hashbucket[i].lock = RW_LOCK_UNLOCKED; + sctp_proto.ep_hashbucket[i].chain = NULL; + } + + /* Allocate and initialize the SCTP port hash table. */ + sctp_proto.port_hashsize = 4096; + sctp_proto.port_hashtable = (sctp_bind_hashbucket_t *) + kmalloc(4096 * sizeof(sctp_bind_hashbucket_t), GFP_KERNEL); + if (!sctp_proto.port_hashtable) { + printk(KERN_ERR "SCTP: Failed bind hash alloc."); + status = -ENOMEM; + goto err_bhash_alloc; + } + + sctp_proto.port_alloc_lock = SPIN_LOCK_UNLOCKED; + sctp_proto.port_rover = sysctl_local_port_range[0] - 1; + for (i = 0; i < sctp_proto.port_hashsize; i++) { + sctp_proto.port_hashtable[i].lock = SPIN_LOCK_UNLOCKED; + sctp_proto.port_hashtable[i].chain = NULL; + } + + sctp_sysctl_register(); + + INIT_LIST_HEAD(&sctp_proto.address_families); + sctp_register_af(&sctp_ipv4_specific); + + status = sctp_v6_init(); + if (status) + goto err_v6_init; + + /* Initialize the control inode/socket for handling OOTB packets. */ + if ((status = sctp_ctl_sock_init())) { + printk (KERN_ERR + "SCTP: Failed to initialize the SCTP control sock.\n"); + goto err_ctl_sock_init; + } + + /* Initialize the local address list. */ + INIT_LIST_HEAD(&sctp_proto.local_addr_list); + sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED; + + /* Register notifier for inet address additions/deletions. */ + register_inetaddr_notifier(&sctp_inetaddr_notifier); + + sctp_get_local_addr_list(&sctp_proto); + + __unsafe(THIS_MODULE); + return 0; + +err_ctl_sock_init: + sctp_v6_exit(); +err_v6_init: + sctp_sysctl_unregister(); + list_del(&sctp_ipv4_specific.list); + kfree(sctp_proto.port_hashtable); +err_bhash_alloc: + kfree(sctp_proto.ep_hashbucket); +err_ehash_alloc: + kfree(sctp_proto.assoc_hashbucket); +err_ahash_alloc: + sctp_dbg_objcnt_exit(); + sctp_proc_exit(); + cleanup_sctp_mibs(); +err_init_mibs: + inet_del_protocol(&sctp_protocol); + inet_unregister_protosw(&sctp_seqpacket_protosw); + inet_unregister_protosw(&sctp_stream_protosw); + return status; +} + +/* Exit handler for the SCTP protocol. */ +__exit void sctp_exit(void) +{ + /* BUG. This should probably do something useful like clean + * up all the remaining associations and all that memory. + */ + + /* Unregister notifier for inet address additions/deletions. */ + unregister_inetaddr_notifier(&sctp_inetaddr_notifier); + + /* Free the local address list. */ + sctp_free_local_addr_list(&sctp_proto); + + /* Free the control endpoint. */ + sock_release(sctp_ctl_socket); + + sctp_v6_exit(); + sctp_sysctl_unregister(); + list_del(&sctp_ipv4_specific.list); + + kfree(sctp_proto.assoc_hashbucket); + kfree(sctp_proto.ep_hashbucket); + kfree(sctp_proto.port_hashtable); + + sctp_dbg_objcnt_exit(); + sctp_proc_exit(); + cleanup_sctp_mibs(); + + inet_del_protocol(&sctp_protocol); + inet_unregister_protosw(&sctp_seqpacket_protosw); + inet_unregister_protosw(&sctp_stream_protosw); +} + +module_init(sctp_init); +module_exit(sctp_exit); + +MODULE_AUTHOR("Linux Kernel SCTP developers "); +MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)"); +MODULE_LICENSE("GPL"); diff -urN linux-2.4.22-bk23/net/sctp/sla1.c linux-2.4.22-bk24/net/sctp/sla1.c --- linux-2.4.22-bk23/net/sctp/sla1.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/sla1.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,281 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * + * This file is part of the SCTP kernel reference Implementation + * + * (It's really SHA-1 but Hey I was tired when I created this + * file, and on a plane to France :-) + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Randall Stewart + * kmorneau@cisco.com + * qxie1@email.mot.com + * + * Based on: + * Randy Stewart, et al. SCTP Reference Implementation which is licenced + * under the GPL. + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include /* for memcpy */ +#include /* dead chicken for in.h */ +#include /* for htonl and ntohl */ + +#include + +void SLA1_Init(struct SLA_1_Context *ctx) +{ + /* Init the SLA-1 context structure. */ + ctx->A = 0; + ctx->B = 0; + ctx->C = 0; + ctx->D = 0; + ctx->E = 0; + ctx->H0 = H0INIT; + ctx->H1 = H1INIT; + ctx->H2 = H2INIT; + ctx->H3 = H3INIT; + ctx->H4 = H4INIT; + ctx->TEMP = 0; + memset(ctx->words, 0, sizeof(ctx->words)); + ctx->howManyInBlock = 0; + ctx->runningTotal = 0; +} + +void SLA1processABlock(struct SLA_1_Context *ctx,unsigned int *block) +{ + int i; + /* init the W0-W15 to the block of words being + * hashed. + */ + /* step a) */ + + for (i = 0; i < 16; i++) + ctx->words[i] = ntohl(block[i]); + + /* now init the rest based on the SLA-1 formula, step b) */ + for (i = 16; i < 80; i++) + ctx->words[i] = + CSHIFT(1, ((ctx->words[(i-3)]) ^ + (ctx->words[(i-8)]) ^ + (ctx->words[(i-14)]) ^ + (ctx->words[(i-16)]))); + + /* step c) */ + ctx->A = ctx->H0; + ctx->B = ctx->H1; + ctx->C = ctx->H2; + ctx->D = ctx->H3; + ctx->E = ctx->H4; + + /* step d) */ + for (i = 0; i < 80; i++) { + if (i < 20) { + ctx->TEMP = ((CSHIFT(5, ctx->A)) + + (F1(ctx->B, ctx->C, ctx->D)) + + (ctx->E) + + ctx->words[i] + + K1 + ); + } else if (i < 40) { + ctx->TEMP = ((CSHIFT(5, ctx->A)) + + (F2(ctx->B, ctx->C, ctx->D)) + + (ctx->E) + + (ctx->words[i]) + + K2 + ); + } else if (i < 60) { + ctx->TEMP = ((CSHIFT(5, ctx->A)) + + (F3(ctx->B, ctx->C, ctx->D)) + + (ctx->E) + + (ctx->words[i]) + + K3 + ); + } else { + ctx->TEMP = ((CSHIFT(5, ctx->A)) + + (F4(ctx->B, ctx->C, ctx->D)) + + (ctx->E) + + (ctx->words[i]) + + K4 + ); + } + ctx->E = ctx->D; + ctx->D = ctx->C; + ctx->C = CSHIFT(30, ctx->B); + ctx->B = ctx->A; + ctx->A = ctx->TEMP; + } + + /* step e) */ + ctx->H0 = (ctx->H0) + (ctx->A); + ctx->H1 = (ctx->H1) + (ctx->B); + ctx->H2 = (ctx->H2) + (ctx->C); + ctx->H3 = (ctx->H3) + (ctx->D); + ctx->H4 = (ctx->H4) + (ctx->E); +} + +void SLA1_Process(struct SLA_1_Context *ctx, const unsigned char *ptr, int siz) +{ + int numberLeft, leftToFill; + + numberLeft = siz; + while (numberLeft > 0) { + leftToFill = sizeof(ctx->SLAblock) - ctx->howManyInBlock; + if (leftToFill > numberLeft) { + /* can only partially fill up this one */ + memcpy(&ctx->SLAblock[ctx->howManyInBlock], + ptr, numberLeft); + ctx->howManyInBlock += siz; + ctx->runningTotal += siz; + break; + } else { + /* block is now full, process it */ + memcpy(&ctx->SLAblock[ctx->howManyInBlock], + ptr, leftToFill); + SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock); + numberLeft -= leftToFill; + ctx->runningTotal += leftToFill; + ctx->howManyInBlock = 0; + } + } +} + +void SLA1_Final(struct SLA_1_Context *ctx, unsigned char *digestBuf) +{ + /* if any left in block fill with padding + * and process. Then transfer the digest to + * the pointer. At the last block some special + * rules need to apply. We must add a 1 bit + * following the message, then we pad with + * 0's. The total size is encoded as a 64 bit + * number at the end. Now if the last buffer has + * more than 55 octets in it we cannot fit + * the 64 bit number + 10000000 pad on the end + * and must add the 10000000 pad, pad the rest + * of the message with 0's and then create a + * all 0 message with just the 64 bit size + * at the end and run this block through by itself. + * Also the 64 bit int must be in network byte + * order. + */ + int i, leftToFill; + unsigned int *ptr; + + if (ctx->howManyInBlock > 55) { + /* special case, we need to process two + * blocks here. One for the current stuff + * plus possibly the pad. The other for + * the size. + */ + leftToFill = sizeof(ctx->SLAblock) - ctx->howManyInBlock; + if (leftToFill == 0) { + /* Should not really happen but I am paranoid */ + /* Not paranoid enough! It is possible for leftToFill + * to become negative! AAA!!!! This is another reason + * to pick MD5 :-)... + */ + SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock); + /* init last block, a bit different then the rest :-) */ + ctx->SLAblock[0] = 0x80; + for (i = 1; i < sizeof(ctx->SLAblock); i++) { + ctx->SLAblock[i] = 0x0; + } + } else if (leftToFill == 1) { + ctx->SLAblock[ctx->howManyInBlock] = 0x80; + SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock); + /* init last block */ + memset(ctx->SLAblock, 0, sizeof(ctx->SLAblock)); + } else { + ctx->SLAblock[ctx->howManyInBlock] = 0x80; + for (i = (ctx->howManyInBlock + 1); + i < sizeof(ctx->SLAblock); + i++) { + ctx->SLAblock[i] = 0x0; + } + SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock); + /* init last block */ + memset(ctx->SLAblock, 0, sizeof(ctx->SLAblock)); + } + /* This is in bits so multiply by 8 */ + ctx->runningTotal *= 8; + ptr = (unsigned int *) &ctx->SLAblock[60]; + *ptr = htonl(ctx->runningTotal); + SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock); + } else { + /* easy case, we just pad this + * message to size - end with 0 + * add the magic 0x80 to the next + * word and then put the network byte + * order size in the last spot and + * process the block. + */ + ctx->SLAblock[ctx->howManyInBlock] = 0x80; + for (i = (ctx->howManyInBlock + 1); + i < sizeof(ctx->SLAblock); + i++) { + ctx->SLAblock[i] = 0x0; + } + /* get last int spot */ + ctx->runningTotal *= 8; + ptr = (unsigned int *) &ctx->SLAblock[60]; + *ptr = htonl(ctx->runningTotal); + SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock); + } + /* Now at this point all we need do is transfer the + * digest back to the user + */ + digestBuf[3] = (ctx->H0 & 0xff); + digestBuf[2] = ((ctx->H0 >> 8) & 0xff); + digestBuf[1] = ((ctx->H0 >> 16) & 0xff); + digestBuf[0] = ((ctx->H0 >> 24) & 0xff); + + digestBuf[7] = (ctx->H1 & 0xff); + digestBuf[6] = ((ctx->H1 >> 8) & 0xff); + digestBuf[5] = ((ctx->H1 >> 16) & 0xff); + digestBuf[4] = ((ctx->H1 >> 24) & 0xff); + + digestBuf[11] = (ctx->H2 & 0xff); + digestBuf[10] = ((ctx->H2 >> 8) & 0xff); + digestBuf[9] = ((ctx->H2 >> 16) & 0xff); + digestBuf[8] = ((ctx->H2 >> 24) & 0xff); + + digestBuf[15] = (ctx->H3 & 0xff); + digestBuf[14] = ((ctx->H3 >> 8) & 0xff); + digestBuf[13] = ((ctx->H3 >> 16) & 0xff); + digestBuf[12] = ((ctx->H3 >> 24) & 0xff); + + digestBuf[19] = (ctx->H4 & 0xff); + digestBuf[18] = ((ctx->H4 >> 8) & 0xff); + digestBuf[17] = ((ctx->H4 >> 16) & 0xff); + digestBuf[16] = ((ctx->H4 >> 24) & 0xff); +} diff -urN linux-2.4.22-bk23/net/sctp/sm_make_chunk.c linux-2.4.22-bk24/net/sctp/sm_make_chunk.c --- linux-2.4.22-bk23/net/sctp/sm_make_chunk.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/sm_make_chunk.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,2199 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2002 Intel Corp. + * Copyright (c) 2001-2002 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * This file includes part of the implementation of the add-IP extension, + * based on June 29, 2001, + * for the SCTP kernel reference Implementation. + * + * These functions work with the state functions in sctp_sm_statefuns.c + * to implement the state operations. These functions implement the + * steps which require modifying existing data structures. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * C. Robin + * Jon Grimm + * Xingang Guo + * Dajiang Zhang + * Sridhar Samudrala + * Daisy Chang + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for get_random_bytes */ +#include +#include + +/* What was the inbound interface for this chunk? */ +int sctp_chunk_iif(const struct sctp_chunk *chunk) +{ + struct sctp_af *af; + int iif = 0; + + af = sctp_get_af_specific(ipver2af(chunk->skb->nh.iph->version)); + if (af) + iif = af->skb_iif(chunk->skb); + + return iif; +} + +/* RFC 2960 3.3.2 Initiation (INIT) (1) + * + * Note 2: The ECN capable field is reserved for future use of + * Explicit Congestion Notification. + */ +static const sctp_ecn_capable_param_t ecap_param = { + { + SCTP_PARAM_ECN_CAPABLE, + __constant_htons(sizeof(sctp_ecn_capable_param_t)), + } +}; + +/* A helper to initialize to initialize an op error inside a + * provided chunk, as most cause codes will be embedded inside an + * abort chunk. + */ +void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code, + const void *payload, size_t paylen) +{ + sctp_errhdr_t err; + int padlen; + __u16 len; + + /* Cause code constants are now defined in network order. */ + err.cause = cause_code; + len = sizeof(sctp_errhdr_t) + paylen; + padlen = len % 4; + err.length = htons(len); + len += padlen; + sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); + chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload); +} + +/* 3.3.2 Initiation (INIT) (1) + * + * This chunk is used to initiate a SCTP association between two + * endpoints. The format of the INIT chunk is shown below: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type = 1 | Chunk Flags | Chunk Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Initiate Tag | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Advertised Receiver Window Credit (a_rwnd) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of Outbound Streams | Number of Inbound Streams | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Initial TSN | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \ \ + * / Optional/Variable-Length Parameters / + * \ \ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * The INIT chunk contains the following parameters. Unless otherwise + * noted, each parameter MUST only be included once in the INIT chunk. + * + * Fixed Parameters Status + * ---------------------------------------------- + * Initiate Tag Mandatory + * Advertised Receiver Window Credit Mandatory + * Number of Outbound Streams Mandatory + * Number of Inbound Streams Mandatory + * Initial TSN Mandatory + * + * Variable Parameters Status Type Value + * ------------------------------------------------------------- + * IPv4 Address (Note 1) Optional 5 + * IPv6 Address (Note 1) Optional 6 + * Cookie Preservative Optional 9 + * Reserved for ECN Capable (Note 2) Optional 32768 (0x8000) + * Host Name Address (Note 3) Optional 11 + * Supported Address Types (Note 4) Optional 12 + */ +sctp_chunk_t *sctp_make_init(const struct sctp_association *asoc, + const sctp_bind_addr_t *bp, + int gfp, int vparam_len) +{ + sctp_inithdr_t init; + union sctp_params addrs; + size_t chunksize; + sctp_chunk_t *retval = NULL; + int num_types, addrs_len = 0; + struct sctp_opt *sp; + sctp_supported_addrs_param_t sat; + __u16 types[2]; + + /* RFC 2960 3.3.2 Initiation (INIT) (1) + * + * Note 1: The INIT chunks can contain multiple addresses that + * can be IPv4 and/or IPv6 in any combination. + */ + retval = NULL; + + /* Convert the provided bind address list to raw format. */ + addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, gfp); + + init.init_tag = htonl(asoc->c.my_vtag); + init.a_rwnd = htonl(asoc->rwnd); + init.num_outbound_streams = htons(asoc->c.sinit_num_ostreams); + init.num_inbound_streams = htons(asoc->c.sinit_max_instreams); + init.initial_tsn = htonl(asoc->c.initial_tsn); + + /* How many address types are needed? */ + sp = sctp_sk(asoc->base.sk); + num_types = sp->pf->supported_addrs(sp, types); + + chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types); + chunksize += sizeof(ecap_param); + chunksize += vparam_len; + + /* RFC 2960 3.3.2 Initiation (INIT) (1) + * + * Note 3: An INIT chunk MUST NOT contain more than one Host + * Name address parameter. Moreover, the sender of the INIT + * MUST NOT combine any other address types with the Host Name + * address in the INIT. The receiver of INIT MUST ignore any + * other address types if the Host Name address parameter is + * present in the received INIT chunk. + * + * PLEASE DO NOT FIXME [This version does not support Host Name.] + */ + + retval = sctp_make_chunk(asoc, SCTP_CID_INIT, 0, chunksize); + if (!retval) + goto nodata; + + retval->subh.init_hdr = + sctp_addto_chunk(retval, sizeof(init), &init); + retval->param_hdr.v = + sctp_addto_chunk(retval, addrs_len, addrs.v); + + /* RFC 2960 3.3.2 Initiation (INIT) (1) + * + * Note 4: This parameter, when present, specifies all the + * address types the sending endpoint can support. The absence + * of this parameter indicates that the sending endpoint can + * support any address type. + */ + sat.param_hdr.type = SCTP_PARAM_SUPPORTED_ADDRESS_TYPES; + sat.param_hdr.length = htons(SCTP_SAT_LEN(num_types)); + sctp_addto_chunk(retval, sizeof(sat), &sat); + sctp_addto_chunk(retval, num_types * sizeof(__u16), &types); + + sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); +nodata: + if (addrs.v) + kfree(addrs.v); + return retval; +} + +sctp_chunk_t *sctp_make_init_ack(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + int gfp, int unkparam_len) +{ + sctp_inithdr_t initack; + sctp_chunk_t *retval; + union sctp_params addrs; + int addrs_len; + sctp_cookie_param_t *cookie; + int cookie_len; + size_t chunksize; + + retval = NULL; + + /* Note: there may be no addresses to embed. */ + addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, gfp); + + initack.init_tag = htonl(asoc->c.my_vtag); + initack.a_rwnd = htonl(asoc->rwnd); + initack.num_outbound_streams = htons(asoc->c.sinit_num_ostreams); + initack.num_inbound_streams = htons(asoc->c.sinit_max_instreams); + initack.initial_tsn = htonl(asoc->c.initial_tsn); + + /* FIXME: We really ought to build the cookie right + * into the packet instead of allocating more fresh memory. + */ + cookie = sctp_pack_cookie(asoc->ep, asoc, chunk, &cookie_len, + addrs.v, addrs_len); + if (!cookie) + goto nomem_cookie; + + /* Calculate the total size of allocation, include the reserved + * space for reporting unknown parameters if it is specified. + */ + chunksize = sizeof(initack) + addrs_len + cookie_len + unkparam_len; + + /* Tell peer that we'll do ECN only if peer advertised such cap. */ + if (asoc->peer.ecn_capable) + chunksize += sizeof(ecap_param); + + /* Now allocate and fill out the chunk. */ + retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize); + if (!retval) + goto nomem_chunk; + + /* Per the advice in RFC 2960 6.4, send this reply to + * the source of the INIT packet. + */ + retval->transport = chunk->transport; + retval->subh.init_hdr = + sctp_addto_chunk(retval, sizeof(initack), &initack); + retval->param_hdr.v = sctp_addto_chunk(retval, addrs_len, addrs.v); + sctp_addto_chunk(retval, cookie_len, cookie); + if (asoc->peer.ecn_capable) + sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); + + /* We need to remove the const qualifier at this point. */ + retval->asoc = (struct sctp_association *) asoc; + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it received the DATA or control chunk + * to which it is replying. + * + * [INIT ACK back to where the INIT came from.] + */ + if (chunk) + retval->transport = chunk->transport; + +nomem_chunk: + kfree(cookie); +nomem_cookie: + if (addrs.v) + kfree(addrs.v); + return retval; +} + +/* 3.3.11 Cookie Echo (COOKIE ECHO) (10): + * + * This chunk is used only during the initialization of an association. + * It is sent by the initiator of an association to its peer to complete + * the initialization process. This chunk MUST precede any DATA chunk + * sent within the association, but MAY be bundled with one or more DATA + * chunks in the same packet. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type = 10 |Chunk Flags | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * / Cookie / + * \ \ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Chunk Flags: 8 bit + * + * Set to zero on transmit and ignored on receipt. + * + * Length: 16 bits (unsigned integer) + * + * Set to the size of the chunk in bytes, including the 4 bytes of + * the chunk header and the size of the Cookie. + * + * Cookie: variable size + * + * This field must contain the exact cookie received in the + * State Cookie parameter from the previous INIT ACK. + * + * An implementation SHOULD make the cookie as small as possible + * to insure interoperability. + */ +sctp_chunk_t *sctp_make_cookie_echo(const struct sctp_association *asoc, + const sctp_chunk_t *chunk) +{ + sctp_chunk_t *retval; + void *cookie; + int cookie_len; + + cookie = asoc->peer.cookie; + cookie_len = asoc->peer.cookie_len; + + /* Build a cookie echo chunk. */ + retval = sctp_make_chunk(asoc, SCTP_CID_COOKIE_ECHO, 0, cookie_len); + if (!retval) + goto nodata; + retval->subh.cookie_hdr = + sctp_addto_chunk(retval, cookie_len, cookie); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [COOKIE ECHO back to where the INIT ACK came from.] + */ + if (chunk) + retval->transport = chunk->transport; + +nodata: + return retval; +} + +/* 3.3.12 Cookie Acknowledgement (COOKIE ACK) (11): + * + * This chunk is used only during the initialization of an + * association. It is used to acknowledge the receipt of a COOKIE + * ECHO chunk. This chunk MUST precede any DATA or SACK chunk sent + * within the association, but MAY be bundled with one or more DATA + * chunks or SACK chunk in the same SCTP packet. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type = 11 |Chunk Flags | Length = 4 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Chunk Flags: 8 bits + * + * Set to zero on transmit and ignored on receipt. + */ +sctp_chunk_t *sctp_make_cookie_ack(const struct sctp_association *asoc, + const sctp_chunk_t *chunk) +{ + sctp_chunk_t *retval; + + retval = sctp_make_chunk(asoc, SCTP_CID_COOKIE_ACK, 0, 0); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [COOKIE ACK back to where the COOKIE ECHO came from.] + */ + if (retval && chunk) + retval->transport = chunk->transport; + + return retval; +} + +/* + * Appendix A: Explicit Congestion Notification: + * CWR: + * + * RFC 2481 details a specific bit for a sender to send in the header of + * its next outbound TCP segment to indicate to its peer that it has + * reduced its congestion window. This is termed the CWR bit. For + * SCTP the same indication is made by including the CWR chunk. + * This chunk contains one data element, i.e. the TSN number that + * was sent in the ECNE chunk. This element represents the lowest + * TSN number in the datagram that was originally marked with the + * CE bit. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Chunk Type=13 | Flags=00000000| Chunk Length = 8 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Lowest TSN Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Note: The CWR is considered a Control chunk. + */ +sctp_chunk_t *sctp_make_cwr(const struct sctp_association *asoc, + const __u32 lowest_tsn, + const sctp_chunk_t *chunk) +{ + sctp_chunk_t *retval; + sctp_cwrhdr_t cwr; + + cwr.lowest_tsn = htonl(lowest_tsn); + retval = sctp_make_chunk(asoc, SCTP_CID_ECN_CWR, 0, + sizeof(sctp_cwrhdr_t)); + + if (!retval) + goto nodata; + + retval->subh.ecn_cwr_hdr = + sctp_addto_chunk(retval, sizeof(cwr), &cwr); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [Report a reduced congestion window back to where the ECNE + * came from.] + */ + if (chunk) + retval->transport = chunk->transport; + +nodata: + return retval; +} + +/* Make an ECNE chunk. This is a congestion experienced report. */ +sctp_chunk_t *sctp_make_ecne(const struct sctp_association *asoc, + const __u32 lowest_tsn) +{ + sctp_chunk_t *retval; + sctp_ecnehdr_t ecne; + + ecne.lowest_tsn = htonl(lowest_tsn); + retval = sctp_make_chunk(asoc, SCTP_CID_ECN_ECNE, 0, + sizeof(sctp_ecnehdr_t)); + if (!retval) + goto nodata; + retval->subh.ecne_hdr = + sctp_addto_chunk(retval, sizeof(ecne), &ecne); + +nodata: + return retval; +} + +/* Make a DATA chunk for the given association from the provided + * parameters. However, do not populate the data payload. + */ +sctp_chunk_t *sctp_make_datafrag_empty(struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + int data_len, __u8 flags, __u16 ssn) +{ + sctp_chunk_t *retval; + sctp_datahdr_t dp; + int chunk_len; + + /* We assign the TSN as LATE as possible, not here when + * creating the chunk. + */ + dp.tsn= 1000000; /* This marker is a debugging aid. */ + dp.stream = htons(sinfo->sinfo_stream); + dp.ppid = htonl(sinfo->sinfo_ppid); + dp.ssn = htons(ssn); + + /* Set the flags for an unordered send. */ + if (sinfo->sinfo_flags & MSG_UNORDERED) + flags |= SCTP_DATA_UNORDERED; + + chunk_len = sizeof(dp) + data_len; + retval = sctp_make_chunk(asoc, SCTP_CID_DATA, flags, chunk_len); + if (!retval) + goto nodata; + + retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp); + memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo)); + +nodata: + return retval; +} + +/* Make a DATA chunk for the given association. Populate the data + * payload. + */ +sctp_chunk_t *sctp_make_datafrag(struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + int data_len, const __u8 *data, + __u8 flags, __u16 ssn) +{ + sctp_chunk_t *retval; + + retval = sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn); + if (retval) + sctp_addto_chunk(retval, data_len, data); + + return retval; +} + +/* Make a DATA chunk for the given association to ride on stream id + * 'stream', with a payload id of 'payload', and a body of 'data'. + */ +sctp_chunk_t *sctp_make_data(struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + int data_len, const __u8 *data) +{ + sctp_chunk_t *retval = NULL; + + retval = sctp_make_data_empty(asoc, sinfo, data_len); + if (retval) + sctp_addto_chunk(retval, data_len, data); + return retval; +} + +/* Make a DATA chunk for the given association to ride on stream id + * 'stream', with a payload id of 'payload', and a body big enough to + * hold 'data_len' octets of data. We use this version when we need + * to build the message AFTER allocating memory. + */ +sctp_chunk_t *sctp_make_data_empty(struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + int data_len) +{ + __u8 flags = SCTP_DATA_NOT_FRAG; + + return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0); +} + +/* Create a selective ackowledgement (SACK) for the given + * association. This reports on which TSN's we've seen to date, + * including duplicates and gaps. + */ +sctp_chunk_t *sctp_make_sack(const struct sctp_association *asoc) +{ + sctp_chunk_t *retval; + sctp_sackhdr_t sack; + sctp_gap_ack_block_t gab; + int length; + __u32 ctsn; + struct sctp_tsnmap_iter iter; + __u16 num_gabs, num_dup_tsns; + struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; + + ctsn = sctp_tsnmap_get_ctsn(map); + SCTP_DEBUG_PRINTK("sackCTSNAck sent is 0x%x.\n", ctsn); + + /* Count the number of Gap Ack Blocks. */ + num_gabs = 0; + + if (sctp_tsnmap_has_gap(map)) { + sctp_tsnmap_iter_init(map, &iter); + while (sctp_tsnmap_next_gap_ack(map, &iter, + &gab.start, &gab.end)) + num_gabs++; + } + + num_dup_tsns = sctp_tsnmap_num_dups(map); + + /* Initialize the SACK header. */ + sack.cum_tsn_ack = htonl(ctsn); + sack.a_rwnd = htonl(asoc->a_rwnd); + sack.num_gap_ack_blocks = htons(num_gabs); + sack.num_dup_tsns = htons(num_dup_tsns); + + length = sizeof(sack) + + sizeof(sctp_gap_ack_block_t) * num_gabs + + sizeof(__u32) * num_dup_tsns; + + /* Create the chunk. */ + retval = sctp_make_chunk(asoc, SCTP_CID_SACK, 0, length); + if (!retval) + goto nodata; + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, etc.) to the same destination transport + * address from which it received the DATA or control chunk to + * which it is replying. This rule should also be followed if + * the endpoint is bundling DATA chunks together with the + * reply chunk. + * + * However, when acknowledging multiple DATA chunks received + * in packets from different source addresses in a single + * SACK, the SACK chunk may be transmitted to one of the + * destination transport addresses from which the DATA or + * control chunks being acknowledged were received. + * + * [BUG: We do not implement the following paragraph. + * Perhaps we should remember the last transport we used for a + * SACK and avoid that (if possible) if we have seen any + * duplicates. --piggy] + * + * When a receiver of a duplicate DATA chunk sends a SACK to a + * multi- homed endpoint it MAY be beneficial to vary the + * destination address and not use the source address of the + * DATA chunk. The reason being that receiving a duplicate + * from a multi-homed endpoint might indicate that the return + * path (as specified in the source address of the DATA chunk) + * for the SACK is broken. + * + * [Send to the address from which we last received a DATA chunk.] + */ + retval->transport = asoc->peer.last_data_from; + + retval->subh.sack_hdr = + sctp_addto_chunk(retval, sizeof(sack), &sack); + + /* Put the Gap Ack Blocks into the chunk. */ + if (num_gabs) { + sctp_tsnmap_iter_init(map, &iter); + while(sctp_tsnmap_next_gap_ack(map, &iter, + &gab.start, &gab.end)) { + gab.start = htons(gab.start); + gab.end = htons(gab.end); + sctp_addto_chunk(retval, sizeof(sctp_gap_ack_block_t), + &gab); + } + } + + /* Register the duplicates. */ + sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns, + sctp_tsnmap_get_dups(map)); + +nodata: + return retval; +} + +/* Make a SHUTDOWN chunk. */ +sctp_chunk_t *sctp_make_shutdown(const struct sctp_association *asoc) +{ + sctp_chunk_t *retval; + sctp_shutdownhdr_t shut; + __u32 ctsn; + + ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); + shut.cum_tsn_ack = htonl(ctsn); + + retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN, 0, + sizeof(sctp_shutdownhdr_t)); + if (!retval) + goto nodata; + + retval->subh.shutdown_hdr = + sctp_addto_chunk(retval, sizeof(shut), &shut); +nodata: + return retval; +} + +sctp_chunk_t *sctp_make_shutdown_ack(const struct sctp_association *asoc, + const sctp_chunk_t *chunk) +{ + sctp_chunk_t *retval; + + retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_ACK, 0, 0); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [ACK back to where the SHUTDOWN came from.] + */ + if (retval && chunk) + retval->transport = chunk->transport; + + return retval; +} + +sctp_chunk_t *sctp_make_shutdown_complete(const struct sctp_association *asoc, + const sctp_chunk_t *chunk) +{ + sctp_chunk_t *retval; + __u8 flags = 0; + + /* Maybe set the T-bit if we have no association. */ + flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T; + + retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags, 0); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [Report SHUTDOWN COMPLETE back to where the SHUTDOWN ACK + * came from.] + */ + if (retval && chunk) + retval->transport = chunk->transport; + + return retval; +} + +/* Create an ABORT. Note that we set the T bit if we have no + * association. + */ +sctp_chunk_t *sctp_make_abort(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + const size_t hint) +{ + sctp_chunk_t *retval; + __u8 flags = 0; + + /* Maybe set the T-bit if we have no association. */ + flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T; + + retval = sctp_make_chunk(asoc, SCTP_CID_ABORT, flags, hint); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [ABORT back to where the offender came from.] + */ + if (retval && chunk) + retval->transport = chunk->transport; + + return retval; +} + +/* Helper to create ABORT with a NO_USER_DATA error. */ +sctp_chunk_t *sctp_make_abort_no_data(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, __u32 tsn) +{ + sctp_chunk_t *retval; + __u32 payload; + + retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + + sizeof(tsn)); + + if (!retval) + goto no_mem; + + /* Put the tsn back into network byte order. */ + payload = htonl(tsn); + sctp_init_cause(retval, SCTP_ERROR_NO_DATA, (const void *)&payload, + sizeof(payload)); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [ABORT back to where the offender came from.] + */ + if (chunk) + retval->transport = chunk->transport; + +no_mem: + return retval; +} + +/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ +sctp_chunk_t *sctp_make_abort_user(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + const struct msghdr *msg) +{ + sctp_chunk_t *retval; + void *payload = NULL, *payoff; + size_t paylen; + struct iovec *iov = msg->msg_iov; + int iovlen = msg->msg_iovlen; + + paylen = get_user_iov_size(iov, iovlen); + retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen); + if (!retval) + goto err_chunk; + + if (paylen) { + /* Put the msg_iov together into payload. */ + payload = kmalloc(paylen, GFP_ATOMIC); + if (!payload) + goto err_payload; + payoff = payload; + + for (; iovlen > 0; --iovlen) { + if (copy_from_user(payoff, iov->iov_base,iov->iov_len)) + goto err_copy; + payoff += iov->iov_len; + iov++; + } + } + + sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen); + + if (paylen) + kfree(payload); + + return retval; + +err_copy: + kfree(payload); +err_payload: + sctp_free_chunk(retval); + retval = NULL; +err_chunk: + return retval; +} + +/* Make a HEARTBEAT chunk. */ +sctp_chunk_t *sctp_make_heartbeat(const struct sctp_association *asoc, + const struct sctp_transport *transport, + const void *payload, const size_t paylen) +{ + sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, + 0, paylen); + + if (!retval) + goto nodata; + + /* Cast away the 'const', as this is just telling the chunk + * what transport it belongs to. + */ + retval->transport = (struct sctp_transport *) transport; + retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload); + +nodata: + return retval; +} + +sctp_chunk_t *sctp_make_heartbeat_ack(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + const void *payload, const size_t paylen) +{ + sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT_ACK, + 0, paylen); + + if (!retval) + goto nodata; + retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload); + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, * etc.) to the same destination transport + * address from which it * received the DATA or control chunk + * to which it is replying. + * + * [HBACK back to where the HEARTBEAT came from.] + */ + if (chunk) + retval->transport = chunk->transport; + +nodata: + return retval; +} + +/* Create an Operation Error chunk with the specified space reserved. + * This routine can be used for containing multiple causes in the chunk. + */ +sctp_chunk_t *sctp_make_op_error_space(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + size_t size) +{ + sctp_chunk_t *retval; + + retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0, + sizeof(sctp_errhdr_t) + size); + if (!retval) + goto nodata; + + /* RFC 2960 6.4 Multi-homed SCTP Endpoints + * + * An endpoint SHOULD transmit reply chunks (e.g., SACK, + * HEARTBEAT ACK, etc.) to the same destination transport + * address from which it received the DATA or control chunk + * to which it is replying. + * + */ + if (chunk) + retval->transport = chunk->transport; + +nodata: + return retval; +} + +/* Create an Operation Error chunk. */ +sctp_chunk_t *sctp_make_op_error(const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + __u16 cause_code, const void *payload, + size_t paylen) +{ + sctp_chunk_t *retval = sctp_make_op_error_space(asoc, chunk, paylen); + + if (!retval) + goto nodata; + + sctp_init_cause(retval, cause_code, payload, paylen); + +nodata: + return retval; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* Turn an skb into a chunk. + * FIXME: Eventually move the structure directly inside the skb->cb[]. + */ +sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, + const struct sctp_association *asoc, + struct sock *sk) +{ + sctp_chunk_t *retval = t_new(sctp_chunk_t, GFP_ATOMIC); + + if (!retval) + goto nodata; + memset(retval, 0, sizeof(sctp_chunk_t)); + + if (!sk) { + SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb); + } + + retval->skb = skb; + retval->asoc = (struct sctp_association *)asoc; + retval->num_times_sent = 0; + retval->has_tsn = 0; + retval->has_ssn = 0; + retval->rtt_in_progress = 0; + retval->sent_at = jiffies; + retval->singleton = 1; + retval->end_of_packet = 0; + retval->ecn_ce_done = 0; + retval->pdiscard = 0; + + /* sctpimpguide-05.txt Section 2.8.2 + * M1) Each time a new DATA chunk is transmitted + * set the 'TSN.Missing.Report' count for that TSN to 0. The + * 'TSN.Missing.Report' count will be used to determine missing chunks + * and when to fast retransmit. + */ + retval->tsn_missing_report = 0; + retval->tsn_gap_acked = 0; + retval->fast_retransmit = 0; + + /* Polish the bead hole. */ + INIT_LIST_HEAD(&retval->transmitted_list); + INIT_LIST_HEAD(&retval->frag_list); + SCTP_DBG_OBJCNT_INC(chunk); + +nodata: + return retval; +} + +/* Set chunk->source and dest based on the IP header in chunk->skb. */ +void sctp_init_addrs(sctp_chunk_t *chunk, union sctp_addr *src, + union sctp_addr *dest) +{ + memcpy(&chunk->source, src, sizeof(union sctp_addr)); + memcpy(&chunk->dest, dest, sizeof(union sctp_addr)); +} + +/* Extract the source address from a chunk. */ +const union sctp_addr *sctp_source(const sctp_chunk_t *chunk) +{ + /* If we have a known transport, use that. */ + if (chunk->transport) { + return &chunk->transport->ipaddr; + } else { + /* Otherwise, extract it from the IP header. */ + return &chunk->source; + } +} + +/* Create a new chunk, setting the type and flags headers from the + * arguments, reserving enough space for a 'paylen' byte payload. + */ +sctp_chunk_t *sctp_make_chunk(const struct sctp_association *asoc, + __u8 type, __u8 flags, int paylen) +{ + sctp_chunk_t *retval; + sctp_chunkhdr_t *chunk_hdr; + struct sk_buff *skb; + struct sock *sk; + + /* No need to allocate LL here, as this is only a chunk. */ + skb = alloc_skb(WORD_ROUND(sizeof(sctp_chunkhdr_t) + paylen), + GFP_ATOMIC); + if (!skb) + goto nodata; + + /* Make room for the chunk header. */ + chunk_hdr = (sctp_chunkhdr_t *)skb_put(skb, sizeof(sctp_chunkhdr_t)); + chunk_hdr->type = type; + chunk_hdr->flags = flags; + chunk_hdr->length = htons(sizeof(sctp_chunkhdr_t)); + + sk = asoc ? asoc->base.sk : NULL; + retval = sctp_chunkify(skb, asoc, sk); + if (!retval) { + kfree_skb(skb); + goto nodata; + } + + retval->chunk_hdr = chunk_hdr; + retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(sctp_chunkhdr_t); + + /* Set the skb to the belonging sock for accounting. */ + skb->sk = sk; + + return retval; + +nodata: + return NULL; +} + +/* Release the memory occupied by a chunk. */ +void sctp_free_chunk(sctp_chunk_t *chunk) +{ + /* Make sure that we are not on any list. */ + skb_unlink((struct sk_buff *) chunk); + list_del(&chunk->transmitted_list); + + /* Free the chunk skb data and the SCTP_chunk stub itself. */ + dev_kfree_skb(chunk->skb); + + kfree(chunk); + SCTP_DBG_OBJCNT_DEC(chunk); +} + + +/* Append bytes to the end of a chunk. Will panic if chunk is not big + * enough. + */ +void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data) +{ + void *target; + void *padding; + int chunklen = ntohs(chunk->chunk_hdr->length); + int padlen = chunklen % 4; + + padding = skb_put(chunk->skb, padlen); + target = skb_put(chunk->skb, len); + + memset(padding, 0, padlen); + memcpy(target, data, len); + + /* Adjust the chunk length field. */ + chunk->chunk_hdr->length = htons(chunklen + padlen + len); + chunk->chunk_end = chunk->skb->tail; + + return target; +} + +/* Append bytes from user space to the end of a chunk. Will panic if + * chunk is not big enough. + * Returns a kernel err value. + */ +static int sctp_user_addto_chunk(sctp_chunk_t *chunk, int off, int len, + struct iovec *data) +{ + __u8 *target; + int err = 0; + + /* Make room in chunk for data. */ + target = skb_put(chunk->skb, len); + + /* Copy data (whole iovec) into chunk */ + if ((err = memcpy_fromiovecend(target, data, off, len))) + goto out; + + /* Adjust the chunk length field. */ + chunk->chunk_hdr->length = + htons(ntohs(chunk->chunk_hdr->length) + len); + chunk->chunk_end = chunk->skb->tail; + +out: + return err; +} + +/* A data chunk can have a maximum payload of (2^16 - 20). Break + * down any such message into smaller chunks. Opportunistically, fragment + * the chunks down to the current MTU constraints. We may get refragmented + * later if the PMTU changes, but it is _much better_ to fragment immediately + * with a reasonable guess than always doing our fragmentation on the + * soft-interrupt. + */ + + +int sctp_datachunks_from_user(struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + struct msghdr *msg, int msg_len, + struct sk_buff_head *chunks) +{ + int max, whole, i, offset, over, err; + int len, first_len; + sctp_chunk_t *chunk; + __u8 frag; + + /* What is a reasonable fragmentation point right now? */ + max = asoc->pmtu; + if (max < SCTP_MIN_PMTU) + max = SCTP_MIN_PMTU; + max -= SCTP_IP_OVERHEAD; + + /* Make sure not beyond maximum chunk size. */ + if (max > SCTP_MAX_CHUNK_LEN) + max = SCTP_MAX_CHUNK_LEN; + + /* Subtract out the overhead of a data chunk header. */ + max -= sizeof(struct sctp_data_chunk); + + whole = 0; + first_len = max; + + /* Encourage Cookie-ECHO bundling. */ + if (asoc->state < SCTP_STATE_COOKIE_ECHOED) { + whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN); + + /* Account for the DATA to be bundled with the COOKIE-ECHO. */ + if (whole) { + first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN; + msg_len -= first_len; + whole = 1; + } + } + + /* How many full sized? How many bytes leftover? */ + whole += msg_len / max; + over = msg_len % max; + offset = 0; + + if (whole && over) + SCTP_INC_STATS_USER(SctpFragUsrMsgs); + + /* Create chunks for all the full sized DATA chunks. */ + for (i=0, len=first_len; i < whole; i++) { + frag = SCTP_DATA_MIDDLE_FRAG; + + if (0 == i) + frag |= SCTP_DATA_FIRST_FRAG; + + if ((i == (whole - 1)) && !over) + frag |= SCTP_DATA_LAST_FRAG; + + chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0); + + if (!chunk) + goto nomem; + err = sctp_user_addto_chunk(chunk, offset, len, msg->msg_iov); + if (err < 0) + goto errout; + + offset += len; + + /* Put the chunk->skb back into the form expected by send. */ + __skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr + - (__u8 *)chunk->skb->data); + + __skb_queue_tail(chunks, (struct sk_buff *)chunk); + + /* The first chunk, the first chunk was likely short + * to allow bundling, so reset to full size. + */ + if (0 == i) + len = max; + } + + /* .. now the leftover bytes. */ + if (over) { + if (!whole) + frag = SCTP_DATA_NOT_FRAG; + else + frag = SCTP_DATA_LAST_FRAG; + + chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0); + + if (!chunk) + goto nomem; + + err = sctp_user_addto_chunk(chunk, offset, over, msg->msg_iov); + + /* Put the chunk->skb back into the form expected by send. */ + __skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr + - (__u8 *)chunk->skb->data); + if (err < 0) + goto errout; + + __skb_queue_tail(chunks, (struct sk_buff *)chunk); + } + err = 0; + goto out; + +nomem: + err = -ENOMEM; +errout: + while ((chunk = (sctp_chunk_t *)__skb_dequeue(chunks))) + sctp_free_chunk(chunk); +out: + return err; +} + +/* Helper function to assign a TSN if needed. This assumes that both + * the data_hdr and association have already been assigned. + */ +void sctp_chunk_assign_ssn(sctp_chunk_t *chunk) +{ + __u16 ssn; + __u16 sid; + + if (chunk->has_ssn) + return; + + /* This is the last possible instant to assign a SSN. */ + if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) { + ssn = 0; + } else { + sid = htons(chunk->subh.data_hdr->stream); + if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG) + ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid); + else + ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid); + ssn = htons(ssn); + } + + chunk->subh.data_hdr->ssn = ssn; + chunk->has_ssn = 1; +} + +/* Helper function to assign a TSN if needed. This assumes that both + * the data_hdr and association have already been assigned. + */ +void sctp_chunk_assign_tsn(sctp_chunk_t *chunk) +{ + if (!chunk->has_tsn) { + /* This is the last possible instant to + * assign a TSN. + */ + chunk->subh.data_hdr->tsn = + htonl(sctp_association_get_next_tsn(chunk->asoc)); + chunk->has_tsn = 1; + } +} + +/* Create a CLOSED association to use with an incoming packet. */ +struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, + struct sctp_chunk *chunk, int gfp) +{ + struct sctp_association *asoc; + struct sk_buff *skb; + sctp_scope_t scope; + + /* Create the bare association. */ + scope = sctp_scope(sctp_source(chunk)); + asoc = sctp_association_new(ep, ep->base.sk, scope, gfp); + if (!asoc) + goto nodata; + asoc->temp = 1; + skb = chunk->skb; + /* Create an entry for the source address of the packet. */ + /* FIXME: Use the af specific helpers. */ + switch (skb->nh.iph->version) { + case 4: + asoc->c.peer_addr.v4.sin_family = AF_INET; + asoc->c.peer_addr.v4.sin_port = ntohs(chunk->sctp_hdr->source); + asoc->c.peer_addr.v4.sin_addr.s_addr = skb->nh.iph->saddr; + break; + + case 6: + asoc->c.peer_addr.v6.sin6_family = AF_INET6; + asoc->c.peer_addr.v6.sin6_port + = ntohs(chunk->sctp_hdr->source); + asoc->c.peer_addr.v6.sin6_flowinfo = 0; /* BUG BUG BUG */ + asoc->c.peer_addr.v6.sin6_addr = skb->nh.ipv6h->saddr; + asoc->c.peer_addr.v6.sin6_scope_id = + ((struct inet6_skb_parm *)skb->cb)->iif; + break; + + default: + /* Yikes! I never heard of this kind of address. */ + goto fail; + }; + +nodata: + return asoc; + +fail: + sctp_association_free(asoc); + return NULL; +} + +/* Build a cookie representing asoc. + * This INCLUDES the param header needed to put the cookie in the INIT ACK. + */ +sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_chunk_t *init_chunk, + int *cookie_len, + const __u8 *raw_addrs, int addrs_len) +{ + sctp_cookie_param_t *retval; + sctp_signed_cookie_t *cookie; + struct scatterlist sg; + int headersize, bodysize; + unsigned int keylen; + char *key; + + headersize = sizeof(sctp_paramhdr_t) + SCTP_SECRET_SIZE; + bodysize = sizeof(sctp_cookie_t) + + ntohs(init_chunk->chunk_hdr->length) + addrs_len; + + /* Pad out the cookie to a multiple to make the signature + * functions simpler to write. + */ + if (bodysize % SCTP_COOKIE_MULTIPLE) + bodysize += SCTP_COOKIE_MULTIPLE + - (bodysize % SCTP_COOKIE_MULTIPLE); + *cookie_len = headersize + bodysize; + + retval = (sctp_cookie_param_t *)kmalloc(*cookie_len, GFP_ATOMIC); + + if (!retval) { + *cookie_len = 0; + goto nodata; + } + + /* Clear this memory since we are sending this data structure + * out on the network. + */ + memset(retval, 0x00, *cookie_len); + cookie = (sctp_signed_cookie_t *) retval->body; + + /* Set up the parameter header. */ + retval->p.type = SCTP_PARAM_STATE_COOKIE; + retval->p.length = htons(*cookie_len); + + /* Copy the cookie part of the association itself. */ + cookie->c = asoc->c; + + /* Save the raw address list length in the cookie. */ + cookie->c.raw_addr_list_len = addrs_len; + + /* Set an expiration time for the cookie. */ + do_gettimeofday(&cookie->c.expiration); + tv_add(&asoc->cookie_life, &cookie->c.expiration); + + /* Copy the peer's init packet. */ + memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr, + ntohs(init_chunk->chunk_hdr->length)); + + /* Copy the raw local address list of the association. */ + memcpy((__u8 *)&cookie->c.peer_init[0] + + ntohs(init_chunk->chunk_hdr->length), raw_addrs, addrs_len); + + if (sctp_sk(ep->base.sk)->hmac) { + /* Sign the message. */ + sg.page = virt_to_page(&cookie->c); + sg.offset = (unsigned long)(&cookie->c) % PAGE_SIZE; + sg.length = bodysize; + keylen = SCTP_SECRET_SIZE; + key = (char *)ep->secret_key[ep->current_key]; + + sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, + &sg, 1, cookie->signature); + } else + sctp_hash_digest(ep->secret_key[ep->current_key], + SCTP_SECRET_SIZE, (__u8 *) &cookie->c, + bodysize, cookie->signature); + +nodata: + return retval; +} + +/* Unpack the cookie from COOKIE ECHO chunk, recreating the association. */ +struct sctp_association *sctp_unpack_cookie( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + sctp_chunk_t *chunk, int gfp, + int *error, sctp_chunk_t **errp) +{ + struct sctp_association *retval = NULL; + sctp_signed_cookie_t *cookie; + sctp_cookie_t *bear_cookie; + int headersize, bodysize, fixed_size; + __u8 digest[SCTP_SIGNATURE_SIZE]; + int secret; + struct scatterlist sg; + unsigned int keylen; + char *key; + sctp_scope_t scope; + struct sk_buff *skb = chunk->skb; + + headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE; + bodysize = ntohs(chunk->chunk_hdr->length) - headersize; + fixed_size = headersize + sizeof(sctp_cookie_t); + + /* Verify that the chunk looks like it even has a cookie. + * There must be enough room for our cookie and our peer's + * INIT chunk. + */ + if (ntohs(chunk->chunk_hdr->length) < + (fixed_size + sizeof(sctp_chunkhdr_t))) + goto malformed; + + /* Verify that the cookie has been padded out. */ + if (bodysize % SCTP_COOKIE_MULTIPLE) + goto malformed; + + /* Process the cookie. */ + cookie = chunk->subh.cookie_hdr; + bear_cookie = &cookie->c; + + if (!sctp_sk(ep->base.sk)->hmac) { + secret = ep->current_key; + sctp_hash_digest(ep->secret_key[secret], SCTP_SECRET_SIZE, + (__u8 *) bear_cookie, bodysize, digest); + + if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { + /* Try the previous key. */ + secret = ep->last_key; + sctp_hash_digest(ep->secret_key[secret], + SCTP_SECRET_SIZE, (__u8 *) bear_cookie, + bodysize, digest); + + if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { + /* Yikes! Still bad signature! */ + *error = -SCTP_IERROR_BAD_SIG; + goto fail; + } + + } + + goto no_hmac; + } + + /* Check the signature. */ + keylen = SCTP_SECRET_SIZE; + sg.page = virt_to_page(bear_cookie); + sg.offset = (unsigned long)(bear_cookie) % PAGE_SIZE; + sg.length = bodysize; + key = (char *)ep->secret_key[ep->current_key]; + + memset(digest, 0x00, sizeof(digest)); + sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, + 1, digest); + + if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { + /* Try the previous key. */ + key = (char *)ep->secret_key[ep->last_key]; + memset(digest, 0x00, sizeof(digest)); + sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, + &sg, 1, digest); + + if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { + /* Yikes! Still bad signature! */ + *error = -SCTP_IERROR_BAD_SIG; + goto fail; + } + } + +no_hmac: + /* Check to see if the cookie is stale. If there is already + * an association, there is no need to check cookie's expiration + * for init collision case of lost COOKIE ACK. + */ + if (!asoc && tv_lt(bear_cookie->expiration, skb->stamp)) { + __u16 len; + /* + * Section 3.3.10.3 Stale Cookie Error (3) + * + * Cause of error + * --------------- + * Stale Cookie Error: Indicates the receipt of a valid State + * Cookie that has expired. + */ + len = ntohs(chunk->chunk_hdr->length); + *errp = sctp_make_op_error_space(asoc, chunk, len); + if (*errp) { + suseconds_t usecs = (skb->stamp.tv_sec - + bear_cookie->expiration.tv_sec) * 1000000L + + skb->stamp.tv_usec - + bear_cookie->expiration.tv_usec; + + usecs = htonl(usecs); + sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE, + &usecs, sizeof(usecs)); + *error = -SCTP_IERROR_STALE_COOKIE; + } else + *error = -SCTP_IERROR_NOMEM; + + goto fail; + } + + /* Make a new base association. */ + scope = sctp_scope(sctp_source(chunk)); + retval = sctp_association_new(ep, ep->base.sk, scope, gfp); + if (!retval) { + *error = -SCTP_IERROR_NOMEM; + goto fail; + } + + /* Set up our peer's port number. */ + retval->peer.port = ntohs(chunk->sctp_hdr->source); + + /* Populate the association from the cookie. */ + retval->c = *bear_cookie; + + if (sctp_assoc_set_bind_addr_from_cookie(retval, bear_cookie, + GFP_ATOMIC) < 0) { + *error = -SCTP_IERROR_NOMEM; + goto fail; + } + + /* Also, add the destination address. */ + if (list_empty(&retval->base.bind_addr.address_list)) { + sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, + GFP_ATOMIC); + } + + retval->next_tsn = retval->c.initial_tsn; + retval->ctsn_ack_point = retval->next_tsn - 1; + + /* The INIT stuff will be done by the side effects. */ + return retval; + +fail: + if (retval) + sctp_association_free(retval); + + return NULL; + +malformed: + /* Yikes! The packet is either corrupt or deliberately + * malformed. + */ + *error = -SCTP_IERROR_MALFORMED; + goto fail; +} + +/******************************************************************** + * 3rd Level Abstractions + ********************************************************************/ + +struct __sctp_missing { + __u32 num_missing; + __u16 type; +} __attribute__((packed));; + +/* + * Report a missing mandatory parameter. + */ +static int sctp_process_missing_param(const struct sctp_association *asoc, + sctp_param_t paramtype, + sctp_chunk_t *chunk, + sctp_chunk_t **errp) +{ + struct __sctp_missing report; + __u16 len; + + len = WORD_ROUND(sizeof(report)); + + /* Make an ERROR chunk, preparing enough room for + * returning multiple unknown parameters. + */ + if (!*errp) + *errp = sctp_make_op_error_space(asoc, chunk, len); + + if (*errp) { + report.num_missing = htonl(1); + report.type = paramtype; + sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, + &report, sizeof(report)); + } + + /* Stop processing this chunk. */ + return 0; +} + +/* Report an Invalid Mandatory Parameter. */ +static int sctp_process_inv_mandatory(const struct sctp_association *asoc, + sctp_chunk_t *chunk, + sctp_chunk_t **errp) +{ + /* Invalid Mandatory Parameter Error has no payload. */ + + if (!*errp) + *errp = sctp_make_op_error_space(asoc, chunk, 0); + + if (*errp) + sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, NULL, 0); + + /* Stop processing this chunk. */ + return 0; +} + +/* Do not attempt to handle the HOST_NAME parm. However, do + * send back an indicator to the peer. + */ +static int sctp_process_hn_param(const struct sctp_association *asoc, + union sctp_params param, + sctp_chunk_t *chunk, + sctp_chunk_t **errp) +{ + __u16 len = ntohs(param.p->length); + + /* Make an ERROR chunk. */ + if (!*errp) + *errp = sctp_make_op_error_space(asoc, chunk, len); + + if (*errp) + sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED, + param.v, len); + + /* Stop processing this chunk. */ + return 0; +} + +/* RFC 3.2.1 & the Implementers Guide 2.2. + * + * The Parameter Types are encoded such that the + * highest-order two bits specify the action that must be + * taken if the processing endpoint does not recognize the + * Parameter Type. + * + * 00 - Stop processing this SCTP chunk and discard it, + * do not process any further chunks within it. + * + * 01 - Stop processing this SCTP chunk and discard it, + * do not process any further chunks within it, and report + * the unrecognized parameter in an 'Unrecognized + * Parameter Type' (in either an ERROR or in the INIT ACK). + * + * 10 - Skip this parameter and continue processing. + * + * 11 - Skip this parameter and continue processing but + * report the unrecognized parameter in an + * 'Unrecognized Parameter Type' (in either an ERROR or in + * the INIT ACK). + * + * Return value: + * 0 - discard the chunk + * 1 - continue with the chunk + */ +static int sctp_process_unk_param(const struct sctp_association *asoc, + union sctp_params param, + sctp_chunk_t *chunk, + sctp_chunk_t **errp) +{ + int retval = 1; + + switch (param.p->type & SCTP_PARAM_ACTION_MASK) { + case SCTP_PARAM_ACTION_DISCARD: + retval = 0; + break; + case SCTP_PARAM_ACTION_DISCARD_ERR: + retval = 0; + /* Make an ERROR chunk, preparing enough room for + * returning multiple unknown parameters. + */ + if (NULL == *errp) + *errp = sctp_make_op_error_space(asoc, chunk, + ntohs(chunk->chunk_hdr->length)); + + if (*errp) + sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, + param.v, + WORD_ROUND(ntohs(param.p->length))); + + break; + case SCTP_PARAM_ACTION_SKIP: + break; + case SCTP_PARAM_ACTION_SKIP_ERR: + /* Make an ERROR chunk, preparing enough room for + * returning multiple unknown parameters. + */ + if (NULL == *errp) + *errp = sctp_make_op_error_space(asoc, chunk, + ntohs(chunk->chunk_hdr->length)); + + if (*errp) { + sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, + param.v, + WORD_ROUND(ntohs(param.p->length))); + } else { + /* If there is no memory for generating the ERROR + * report as specified, an ABORT will be triggered + * to the peer and the association won't be + * established. + */ + retval = 0; + } + + break; + default: + break; + } + + return retval; +} + +/* Find unrecognized parameters in the chunk. + * Return values: + * 0 - discard the chunk + * 1 - continue with the chunk + */ +static int sctp_verify_param(const struct sctp_association *asoc, + union sctp_params param, + sctp_cid_t cid, + sctp_chunk_t *chunk, + sctp_chunk_t **err_chunk) +{ + int retval = 1; + + /* FIXME - This routine is not looking at each parameter per the + * chunk type, i.e., unrecognized parameters should be further + * identified based on the chunk id. + */ + + switch (param.p->type) { + case SCTP_PARAM_IPV4_ADDRESS: + case SCTP_PARAM_IPV6_ADDRESS: + case SCTP_PARAM_COOKIE_PRESERVATIVE: + case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: + case SCTP_PARAM_STATE_COOKIE: + case SCTP_PARAM_HEARTBEAT_INFO: + case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: + case SCTP_PARAM_ECN_CAPABLE: + break; + + case SCTP_PARAM_HOST_NAME_ADDRESS: + /* Tell the peer, we won't support this param. */ + return sctp_process_hn_param(asoc, param, chunk, err_chunk); + default: + SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n", + ntohs(param.p->type), cid); + return sctp_process_unk_param(asoc, param, chunk, err_chunk); + + break; + } + return retval; +} + +/* Verify the INIT packet before we process it. */ +int sctp_verify_init(const struct sctp_association *asoc, + sctp_cid_t cid, + sctp_init_chunk_t *peer_init, + sctp_chunk_t *chunk, + sctp_chunk_t **errp) +{ + union sctp_params param; + int has_cookie = 0; + + /* Verify stream values are non-zero. */ + if ((0 == peer_init->init_hdr.num_outbound_streams) || + (0 == peer_init->init_hdr.num_inbound_streams)) { + + sctp_process_inv_mandatory(asoc, chunk, errp); + return 0; + } + + /* Check for missing mandatory parameters. */ + sctp_walk_params(param, peer_init, init_hdr.params) { + + if (SCTP_PARAM_STATE_COOKIE == param.p->type) + has_cookie = 1; + + } /* for (loop through all parameters) */ + + /* The only missing mandatory param possible today is + * the state cookie for an INIT-ACK chunk. + */ + if ((SCTP_CID_INIT_ACK == cid) && !has_cookie) { + sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE, + chunk, errp); + return 0; + } + + /* Find unrecognized parameters. */ + + sctp_walk_params(param, peer_init, init_hdr.params) { + + if (!sctp_verify_param(asoc, param, cid, chunk, errp)) + return 0; + + } /* for (loop through all parameters) */ + + return 1; +} + +/* Unpack the parameters in an INIT packet into an association. + * Returns 0 on failure, else success. + * FIXME: This is an association method. + */ +int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, + const union sctp_addr *peer_addr, + sctp_init_chunk_t *peer_init, int gfp) +{ + union sctp_params param; + struct sctp_transport *transport; + struct list_head *pos, *temp; + char *cookie; + + /* We must include the address that the INIT packet came from. + * This is the only address that matters for an INIT packet. + * When processing a COOKIE ECHO, we retrieve the from address + * of the INIT from the cookie. + */ + + /* This implementation defaults to making the first transport + * added as the primary transport. The source address seems to + * be a a better choice than any of the embedded addresses. + */ + if (peer_addr) + if(!sctp_assoc_add_peer(asoc, peer_addr, gfp)) + goto nomem; + + /* Process the initialization parameters. */ + + sctp_walk_params(param, peer_init, init_hdr.params) { + + if (!sctp_process_param(asoc, param, peer_addr, gfp)) + goto clean_up; + } + + /* The fixed INIT headers are always in network byte + * order. + */ + asoc->peer.i.init_tag = + ntohl(peer_init->init_hdr.init_tag); + asoc->peer.i.a_rwnd = + ntohl(peer_init->init_hdr.a_rwnd); + asoc->peer.i.num_outbound_streams = + ntohs(peer_init->init_hdr.num_outbound_streams); + asoc->peer.i.num_inbound_streams = + ntohs(peer_init->init_hdr.num_inbound_streams); + asoc->peer.i.initial_tsn = + ntohl(peer_init->init_hdr.initial_tsn); + + /* Apply the upper bounds for output streams based on peer's + * number of inbound streams. + */ + if (asoc->c.sinit_num_ostreams > + ntohs(peer_init->init_hdr.num_inbound_streams)) { + asoc->c.sinit_num_ostreams = + ntohs(peer_init->init_hdr.num_inbound_streams); + } + + if (asoc->c.sinit_max_instreams > + ntohs(peer_init->init_hdr.num_outbound_streams)) { + asoc->c.sinit_max_instreams = + ntohs(peer_init->init_hdr.num_outbound_streams); + } + + /* Copy Initiation tag from INIT to VT_peer in cookie. */ + asoc->c.peer_vtag = asoc->peer.i.init_tag; + + /* Peer Rwnd : Current calculated value of the peer's rwnd. */ + asoc->peer.rwnd = asoc->peer.i.a_rwnd; + + /* Copy cookie in case we need to resend COOKIE-ECHO. */ + cookie = asoc->peer.cookie; + if (cookie) { + asoc->peer.cookie = kmalloc(asoc->peer.cookie_len, gfp); + if (!asoc->peer.cookie) + goto clean_up; + memcpy(asoc->peer.cookie, cookie, asoc->peer.cookie_len); + } + + /* RFC 2960 7.2.1 The initial value of ssthresh MAY be arbitrarily + * high (for example, implementations MAY use the size of the receiver + * advertised window). + */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + transport->ssthresh = asoc->peer.i.a_rwnd; + } + + /* Set up the TSN tracking pieces. */ + sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, + asoc->peer.i.initial_tsn); + + /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number + * + * The stream sequence number in all the streams shall start + * from 0 when the association is established. Also, when the + * stream sequence number reaches the value 65535 the next + * stream sequence number shall be set to 0. + */ + + /* Allocate storage for the negotiated streams. */ + asoc->ssnmap = sctp_ssnmap_new(asoc->peer.i.num_outbound_streams, + asoc->c.sinit_num_ostreams, gfp); + if (!asoc->ssnmap) + goto nomem_ssnmap; + + /* ADDIP Section 4.1 ASCONF Chunk Procedures + * + * When an endpoint has an ASCONF signaled change to be sent to the + * remote endpoint it should do the following: + * ... + * A2) A serial number should be assigned to the Chunk. The serial + * number should be a monotonically increasing number. All serial + * numbers are defined to be initialized at the start of the + * association to the same value as the Initial TSN. + */ + asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1; + return 1; + +nomem_ssnmap: +clean_up: + /* Release the transport structures. */ + list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + list_del(pos); + sctp_transport_free(transport); + } +nomem: + return 0; +} + + +/* Update asoc with the option described in param. + * + * RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT + * + * asoc is the association to update. + * param is the variable length parameter to use for update. + * cid tells us if this is an INIT, INIT ACK or COOKIE ECHO. + * If the current packet is an INIT we want to minimize the amount of + * work we do. In particular, we should not build transport + * structures for the addresses. + */ +int sctp_process_param(struct sctp_association *asoc, union sctp_params param, + const union sctp_addr *peer_addr, int gfp) +{ + union sctp_addr addr; + int i; + __u16 sat; + int retval = 1; + sctp_scope_t scope; + time_t stale; + + /* We maintain all INIT parameters in network byte order all the + * time. This allows us to not worry about whether the parameters + * came from a fresh INIT, and INIT ACK, or were stored in a cookie. + */ + switch (param.p->type) { + case SCTP_PARAM_IPV6_ADDRESS: + if( PF_INET6 != asoc->base.sk->family) + break; + /* Fall through. */ + case SCTP_PARAM_IPV4_ADDRESS: + sctp_param2sockaddr(&addr, param.addr, asoc->peer.port, 0); + scope = sctp_scope(peer_addr); + if (sctp_in_scope(&addr, scope)) + if (!sctp_assoc_add_peer(asoc, &addr, gfp)) + return 0; + break; + + case SCTP_PARAM_COOKIE_PRESERVATIVE: + if (!sctp_proto.cookie_preserve_enable) + break; + + stale = ntohl(param.life->lifespan_increment); + + /* Suggested Cookie Life span increment's unit is msec, + * (1/1000sec). + */ + asoc->cookie_life.tv_sec += stale / 1000; + asoc->cookie_life.tv_usec += (stale % 1000) * 1000; + break; + + case SCTP_PARAM_HOST_NAME_ADDRESS: + SCTP_DEBUG_PRINTK("unimplemented SCTP_HOST_NAME_ADDRESS\n"); + break; + + case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: + /* Turn off the default values first so we'll know which + * ones are really set by the peer. + */ + asoc->peer.ipv4_address = 0; + asoc->peer.ipv6_address = 0; + + /* Cycle through address types; avoid divide by 0. */ + sat = ntohs(param.p->length) - sizeof(sctp_paramhdr_t); + if (sat) + sat /= sizeof(__u16); + + for (i = 0; i < sat; ++i) { + switch (param.sat->types[i]) { + case SCTP_PARAM_IPV4_ADDRESS: + asoc->peer.ipv4_address = 1; + break; + + case SCTP_PARAM_IPV6_ADDRESS: + asoc->peer.ipv6_address = 1; + break; + + case SCTP_PARAM_HOST_NAME_ADDRESS: + asoc->peer.hostname_address = 1; + break; + + default: /* Just ignore anything else. */ + break; + }; + } + break; + + case SCTP_PARAM_STATE_COOKIE: + asoc->peer.cookie_len = + ntohs(param.p->length) - sizeof(sctp_paramhdr_t); + asoc->peer.cookie = param.cookie->body; + break; + + case SCTP_PARAM_HEARTBEAT_INFO: + /* Would be odd to receive, but it causes no problems. */ + break; + + case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: + /* Rejected during verify stage. */ + break; + + case SCTP_PARAM_ECN_CAPABLE: + asoc->peer.ecn_capable = 1; + break; + + default: + /* Any unrecognized parameters should have been caught + * and handled by sctp_verify_param() which should be + * called prior to this routine. Simply log the error + * here. + */ + SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n", + ntohs(param.p->type), asoc); + break; + }; + + return retval; +} + +/* Select a new verification tag. */ +__u32 sctp_generate_tag(const struct sctp_endpoint *ep) +{ + /* I believe that this random number generator complies with RFC1750. + * A tag of 0 is reserved for special cases (e.g. INIT). + */ + __u32 x; + + do { + get_random_bytes(&x, sizeof(__u32)); + } while (x == 0); + + return x; +} + +/* Select an initial TSN to send during startup. */ +__u32 sctp_generate_tsn(const struct sctp_endpoint *ep) +{ + __u32 retval; + + get_random_bytes(&retval, sizeof(__u32)); + return retval; +} + +/******************************************************************** + * 4th Level Abstractions + ********************************************************************/ + +/* Convert from an SCTP IP parameter to a union sctp_addr. */ +void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *param, + __u16 port, int iif) +{ + switch(param->v4.param_hdr.type) { + case SCTP_PARAM_IPV4_ADDRESS: + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = port; + addr->v4.sin_addr.s_addr = param->v4.addr.s_addr; + break; + + case SCTP_PARAM_IPV6_ADDRESS: + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_port = port; + addr->v6.sin6_flowinfo = 0; /* BUG */ + addr->v6.sin6_addr = param->v6.addr; + addr->v6.sin6_scope_id = iif; + break; + + default: + SCTP_DEBUG_PRINTK("Illegal address type %d\n", + ntohs(param->v4.param_hdr.type)); + break; + }; +} + +/* Convert an IP address in an SCTP param into a sockaddr_in. */ +/* Returns true if a valid conversion was possible. */ +int sctp_addr2sockaddr(union sctp_params p, union sctp_addr *sa) +{ + switch (p.p->type) { + case SCTP_PARAM_IPV4_ADDRESS: + sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr); + sa->v4.sin_family = AF_INET; + break; + + case SCTP_PARAM_IPV6_ADDRESS: + *((struct in6_addr *)&sa->v4.sin_addr) + = p.v6->addr; + sa->v4.sin_family = AF_INET6; + break; + + default: + return 0; + }; + + return 1; +} + +/* Convert a sockaddr_in to an IP address in an SCTP param. + * Returns len if a valid conversion was possible. + */ +int sockaddr2sctp_addr(const union sctp_addr *sa, sctp_addr_param_t *p) +{ + int len = 0; + + switch (sa->v4.sin_family) { + case AF_INET: + p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS; + p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t)); + len = sizeof(sctp_ipv4addr_param_t); + p->v4.addr.s_addr = sa->v4.sin_addr.s_addr; + break; + + case AF_INET6: + p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS; + p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t)); + len = sizeof(sctp_ipv6addr_param_t); + p->v6.addr = *(&sa->v6.sin6_addr); + break; + + default: + printk(KERN_WARNING "sockaddr2sctp_addr: Illegal family %d.\n", + sa->v4.sin_family); + return 0; + }; + + return len; +} diff -urN linux-2.4.22-bk23/net/sctp/sm_sideeffect.c linux-2.4.22-bk24/net/sctp/sm_sideeffect.c --- linux-2.4.22-bk23/net/sctp/sm_sideeffect.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/sm_sideeffect.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,1185 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2002 International Business Machines Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions work with the state functions in sctp_sm_statefuns.c + * to implement that state operations. These functions implement the + * steps which require modifying existing data structures. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Hui Huang + * Dajiang Zhang + * Daisy Chang + * Sridhar Samudrala + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include + +/******************************************************************** + * Helper functions + ********************************************************************/ + +/* A helper function for delayed processing of INET ECN CE bit. */ +static void sctp_do_ecn_ce_work(struct sctp_association *asoc, + __u32 lowest_tsn) +{ + /* Save the TSN away for comparison when we receive CWR */ + + asoc->last_ecne_tsn = lowest_tsn; + asoc->need_ecne = 1; +} + +/* Helper function for delayed processing of SCTP ECNE chunk. */ +/* RFC 2960 Appendix A + * + * RFC 2481 details a specific bit for a sender to send in + * the header of its next outbound TCP segment to indicate to + * its peer that it has reduced its congestion window. This + * is termed the CWR bit. For SCTP the same indication is made + * by including the CWR chunk. This chunk contains one data + * element, i.e. the TSN number that was sent in the ECNE chunk. + * This element represents the lowest TSN number in the datagram + * that was originally marked with the CE bit. + */ +static sctp_chunk_t *sctp_do_ecn_ecne_work(struct sctp_association *asoc, + __u32 lowest_tsn, + sctp_chunk_t *chunk) +{ + sctp_chunk_t *repl; + + /* Our previously transmitted packet ran into some congestion + * so we should take action by reducing cwnd and ssthresh + * and then ACK our peer that we we've done so by + * sending a CWR. + */ + + /* First, try to determine if we want to actually lower + * our cwnd variables. Only lower them if the ECNE looks more + * recent than the last response. + */ + if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) { + struct sctp_transport *transport; + + /* Find which transport's congestion variables + * need to be adjusted. + */ + transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn); + + /* Update the congestion variables. */ + if (transport) + sctp_transport_lower_cwnd(transport, + SCTP_LOWER_CWND_ECNE); + asoc->last_cwr_tsn = lowest_tsn; + } + + /* Always try to quiet the other end. In case of lost CWR, + * resend last_cwr_tsn. + */ + repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk); + + /* If we run out of memory, it will look like a lost CWR. We'll + * get back in sync eventually. + */ + return repl; +} + +/* Helper function to do delayed processing of ECN CWR chunk. */ +static void sctp_do_ecn_cwr_work(struct sctp_association *asoc, + __u32 lowest_tsn) +{ + /* Turn off ECNE getting auto-prepended to every outgoing + * packet + */ + asoc->need_ecne = 0; +} + +/* Generate SACK if necessary. We call this at the end of a packet. */ +int sctp_gen_sack(struct sctp_association *asoc, int force, + sctp_cmd_seq_t *commands) +{ + __u32 ctsn, max_tsn_seen; + struct sctp_chunk *sack; + int error = 0; + + if (force) + asoc->peer.sack_needed = 1; + + ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); + max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); + + /* From 12.2 Parameters necessary per association (i.e. the TCB): + * + * Ack State : This flag indicates if the next received packet + * : is to be responded to with a SACK. ... + * : When DATA chunks are out of order, SACK's + * : are not delayed (see Section 6). + * + * [This is actually not mentioned in Section 6, but we + * implement it here anyway. --piggy] + */ + if (max_tsn_seen != ctsn) + asoc->peer.sack_needed = 1; + + /* From 6.2 Acknowledgement on Reception of DATA Chunks: + * + * Section 4.2 of [RFC2581] SHOULD be followed. Specifically, + * an acknowledgement SHOULD be generated for at least every + * second packet (not every second DATA chunk) received, and + * SHOULD be generated within 200 ms of the arrival of any + * unacknowledged DATA chunk. ... + */ + if (!asoc->peer.sack_needed) { + /* We will need a SACK for the next packet. */ + asoc->peer.sack_needed = 1; + goto out; + } else { + if (asoc->a_rwnd > asoc->rwnd) + asoc->a_rwnd = asoc->rwnd; + sack = sctp_make_sack(asoc); + if (!sack) + goto nomem; + + asoc->peer.sack_needed = 0; + + error = sctp_outq_tail(&asoc->outqueue, sack); + + /* Stop the SACK timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + } +out: + return error; +nomem: + error = -ENOMEM; + return error; +} + +/* When the T3-RTX timer expires, it calls this function to create the + * relevant state machine event. + */ +void sctp_generate_t3_rtx_event(unsigned long peer) +{ + int error; + struct sctp_transport *transport = (struct sctp_transport *) peer; + struct sctp_association *asoc = transport->asoc; + + /* Check whether a task is in the sock. */ + + sctp_bh_lock_sock(asoc->base.sk); + if (sock_owned_by_user(asoc->base.sk)) { + SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__); + + /* Try again later. */ + if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20))) + sctp_transport_hold(transport); + goto out_unlock; + } + + /* Is this transport really dead and just waiting around for + * the timer to let go of the reference? + */ + if (transport->dead) + goto out_unlock; + + /* Run through the state machine. */ + error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX), + asoc->state, + asoc->ep, asoc, + transport, GFP_ATOMIC); + + if (error) + asoc->base.sk->err = -error; + +out_unlock: + sctp_bh_unlock_sock(asoc->base.sk); + sctp_transport_put(transport); +} + +/* This is a sa interface for producing timeout events. It works + * for timeouts which use the association as their parameter. + */ +static void sctp_generate_timeout_event(struct sctp_association *asoc, + sctp_event_timeout_t timeout_type) +{ + int error = 0; + + sctp_bh_lock_sock(asoc->base.sk); + if (sock_owned_by_user(asoc->base.sk)) { + SCTP_DEBUG_PRINTK("%s:Sock is busy: timer %d\n", + __FUNCTION__, + timeout_type); + + /* Try again later. */ + if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20))) + sctp_association_hold(asoc); + goto out_unlock; + } + + /* Is this association really dead and just waiting around for + * the timer to let go of the reference? + */ + if (asoc->base.dead) + goto out_unlock; + + /* Run through the state machine. */ + error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + SCTP_ST_TIMEOUT(timeout_type), + asoc->state, asoc->ep, asoc, + (void *)timeout_type, GFP_ATOMIC); + + if (error) + asoc->base.sk->err = -error; + +out_unlock: + sctp_bh_unlock_sock(asoc->base.sk); + sctp_association_put(asoc); +} + +void sctp_generate_t1_cookie_event(unsigned long data) +{ + struct sctp_association *asoc = (struct sctp_association *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE); +} + +void sctp_generate_t1_init_event(unsigned long data) +{ + struct sctp_association *asoc = (struct sctp_association *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT); +} + +void sctp_generate_t2_shutdown_event(unsigned long data) +{ + struct sctp_association *asoc = (struct sctp_association *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN); +} + +void sctp_generate_t5_shutdown_guard_event(unsigned long data) +{ + struct sctp_association *asoc = (struct sctp_association *)data; + sctp_generate_timeout_event(asoc, + SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD); + +} /* sctp_generate_t5_shutdown_guard_event() */ + +void sctp_generate_autoclose_event(unsigned long data) +{ + struct sctp_association *asoc = (struct sctp_association *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE); +} + +/* Generate a heart beat event. If the sock is busy, reschedule. Make + * sure that the transport is still valid. + */ +void sctp_generate_heartbeat_event(unsigned long data) +{ + int error = 0; + struct sctp_transport *transport = (struct sctp_transport *) data; + struct sctp_association *asoc = transport->asoc; + + sctp_bh_lock_sock(asoc->base.sk); + if (sock_owned_by_user(asoc->base.sk)) { + SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__); + + /* Try again later. */ + if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20))) + sctp_transport_hold(transport); + goto out_unlock; + } + + /* Is this structure just waiting around for us to actually + * get destroyed? + */ + if (transport->dead) + goto out_unlock; + + error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT), + asoc->state, asoc->ep, asoc, + transport, GFP_ATOMIC); + + if (error) + asoc->base.sk->err = -error; + +out_unlock: + sctp_bh_unlock_sock(asoc->base.sk); + sctp_transport_put(transport); +} + +/* Inject a SACK Timeout event into the state machine. */ +void sctp_generate_sack_event(unsigned long data) +{ + struct sctp_association *asoc = (struct sctp_association *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); +} + +sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { + NULL, + sctp_generate_t1_cookie_event, + sctp_generate_t1_init_event, + sctp_generate_t2_shutdown_event, + NULL, + sctp_generate_t5_shutdown_guard_event, + sctp_generate_heartbeat_event, + sctp_generate_sack_event, + sctp_generate_autoclose_event, +}; + + +/* RFC 2960 8.2 Path Failure Detection + * + * When its peer endpoint is multi-homed, an endpoint should keep a + * error counter for each of the destination transport addresses of the + * peer endpoint. + * + * Each time the T3-rtx timer expires on any address, or when a + * HEARTBEAT sent to an idle address is not acknowledged within a RTO, + * the error counter of that destination address will be incremented. + * When the value in the error counter exceeds the protocol parameter + * 'Path.Max.Retrans' of that destination address, the endpoint should + * mark the destination transport address as inactive, and a + * notification SHOULD be sent to the upper layer. + * + */ +static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, + struct sctp_transport *transport) +{ + /* The check for association's overall error counter exceeding the + * threshold is done in the state function. + */ + asoc->overall_error_count++; + + if (transport->active && + (transport->error_count++ >= transport->error_threshold)) { + SCTP_DEBUG_PRINTK("transport_strike: transport " + "IP:%d.%d.%d.%d failed.\n", + NIPQUAD(transport->ipaddr.v4.sin_addr)); + sctp_assoc_control_transport(asoc, transport, + SCTP_TRANSPORT_DOWN, + SCTP_FAILED_THRESHOLD); + } + + /* E2) For the destination address for which the timer + * expires, set RTO <- RTO * 2 ("back off the timer"). The + * maximum value discussed in rule C7 above (RTO.max) may be + * used to provide an upper bound to this doubling operation. + */ + transport->rto = min((transport->rto * 2), transport->asoc->rto_max); +} + +/* Worker routine to handle INIT command failure. */ +static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, + struct sctp_association *asoc, + unsigned error) +{ + struct sctp_ulpevent *event; + + event = sctp_ulpevent_make_assoc_change(asoc,0, SCTP_CANT_STR_ASSOC, + 0, 0, 0, GFP_ATOMIC); + + if (event) + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(event)); + + /* SEND_FAILED sent later when cleaning up the association. */ + asoc->outqueue.error = error; + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); +} + +/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ +static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, + struct sctp_association *asoc, + sctp_event_t event_type, + sctp_subtype_t subtype, + struct sctp_chunk *chunk, + unsigned error) +{ + struct sctp_ulpevent *event; + + /* Cancel any partial delivery in progress. */ + sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); + + event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, + (__u16)error, 0, 0, + GFP_ATOMIC); + if (event) + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(event)); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + /* SEND_FAILED sent later when cleaning up the association. */ + asoc->outqueue.error = error; + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); +} + +/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT + * inside the cookie. In reality, this is only used for INIT-ACK processing + * since all other cases use "temporary" associations and can do all + * their work in statefuns directly. + */ +static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, + struct sctp_association *asoc, + struct sctp_chunk *chunk, + sctp_init_chunk_t *peer_init, int gfp) +{ + int error; + + /* We only process the init as a sideeffect in a single + * case. This is when we process the INIT-ACK. If we + * fail during INIT processing (due to malloc problems), + * just return the error and stop processing the stack. + */ + if (!sctp_process_init(asoc, chunk->chunk_hdr->type, + sctp_source(chunk), peer_init, gfp)) + error = -ENOMEM; + else + error = 0; + + return error; +} + +/* Helper function to break out starting up of heartbeat timers. */ +static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc) +{ + struct sctp_transport *t; + struct list_head *pos; + + /* Start a heartbeat timer for each transport on the association. + * hold a reference on the transport to make sure none of + * the needed data structures go away. + */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + + if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) + sctp_transport_hold(t); + } +} + +static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc) +{ + struct sctp_transport *t; + struct list_head *pos; + + /* Stop all heartbeat timers. */ + + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + if (del_timer(&t->hb_timer)) + sctp_transport_put(t); + } +} + +/* Helper function to update the heartbeat timer. */ +static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc, + struct sctp_transport *t) +{ + /* Update the heartbeat timer. */ + if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) + sctp_transport_hold(t); +} + +/* Helper function to handle the reception of an HEARTBEAT ACK. */ +static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc, + struct sctp_transport *t, + sctp_chunk_t *chunk) +{ + sctp_sender_hb_info_t *hbinfo; + + /* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the + * HEARTBEAT should clear the error counter of the destination + * transport address to which the HEARTBEAT was sent. + * The association's overall error count is also cleared. + */ + t->error_count = 0; + t->asoc->overall_error_count = 0; + + /* Mark the destination transport address as active if it is not so + * marked. + */ + if (!t->active) + sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, + SCTP_HEARTBEAT_SUCCESS); + + /* The receiver of the HEARTBEAT ACK should also perform an + * RTT measurement for that destination transport address + * using the time value carried in the HEARTBEAT ACK chunk. + */ + hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; + sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); +} + +/* Helper function to do a transport reset at the expiry of the hearbeat + * timer. + */ +static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc, + struct sctp_transport *t) +{ + sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); + + /* Mark one strike against a transport. */ + sctp_do_8_2_transport_strike(asoc, t); +} + +/* Helper function to process the process SACK command. */ +static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc, + sctp_sackhdr_t *sackh) +{ + int err; + + if (sctp_outq_sack(&asoc->outqueue, sackh)) { + /* There are no more TSNs awaiting SACK. */ + err = sctp_do_sm(SCTP_EVENT_T_OTHER, + SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN), + asoc->state, asoc->ep, asoc, NULL, + GFP_ATOMIC); + } else { + /* Windows may have opened, so we need + * to check if we have DATA to transmit + */ + err = sctp_outq_flush(&asoc->outqueue, 0); + } + + return err; +} + +/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set + * the transport for a shutdown chunk. + */ +static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc, + sctp_chunk_t *chunk) +{ + struct sctp_transport *t; + + t = sctp_assoc_choose_shutdown_transport(asoc); + asoc->shutdown_last_sent_to = t; + asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto; + chunk->transport = t; +} + +/* Helper function to change the state of an association. */ +static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, + sctp_state_t state) +{ + + struct sock *sk = asoc->base.sk; + struct sctp_opt *sp = sctp_sk(sk); + + asoc->state = state; + asoc->state_timestamp = jiffies; + + if (SCTP_SOCKET_TCP == sp->type) { + /* Change the sk->state of a TCP-style socket that has + * sucessfully completed a connect() call. + */ + if ((SCTP_STATE_ESTABLISHED == asoc->state) && + (SCTP_SS_CLOSED == sk->state)) + sk->state = SCTP_SS_ESTABLISHED; + + /* Set the RCV_SHUTDOWN flag when a SHUTDOWN is received. */ + if (SCTP_STATE_SHUTDOWN_RECEIVED == asoc->state) + sk->shutdown |= RCV_SHUTDOWN; + + } + + if ((SCTP_STATE_ESTABLISHED == asoc->state) || + (SCTP_STATE_CLOSED == asoc->state) || + (SCTP_STATE_SHUTDOWN_RECEIVED == asoc->state)) { + /* Wake up any processes waiting in the asoc's wait queue in + * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). + */ + if (waitqueue_active(&asoc->wait)) + wake_up_interruptible(&asoc->wait); + + /* Wake up any processes waiting in the sk's sleep queue of + * a TCP-style or UDP-style peeled-off socket in + * sctp_wait_for_accept() or sctp_wait_for_packet(). + * For a UDP-style socket, the waiters are woken up by the + * notifications. + */ + if (SCTP_SOCKET_UDP != sp->type) + sk->state_change(sk); + } + +} + +/* Helper function to delete an association. */ +static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, + struct sctp_association *asoc) +{ + struct sock *sk = asoc->base.sk; + + /* If it is a non-temporary association belonging to a TCP-style + * listening socket, do not free it so that accept() can pick it + * up later. + */ + if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) && + (SCTP_SS_LISTENING == sk->state) && (!asoc->temp)) + return; + + sctp_unhash_established(asoc); + sctp_association_free(asoc); +} + +/* These three macros allow us to pull the debugging code out of the + * main flow of sctp_do_sm() to keep attention focused on the real + * functionality there. + */ +#define DEBUG_PRE \ + SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \ + "ep %p, %s, %s, asoc %p[%s], %s\n", \ + ep, sctp_evttype_tbl[event_type], \ + (*debug_fn)(subtype), asoc, \ + sctp_state_tbl[state], state_fn->name) + +#define DEBUG_POST \ + SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \ + "asoc %p, status: %s\n", \ + asoc, sctp_status_tbl[status]) + +#define DEBUG_POST_SFX \ + SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ + error, asoc, \ + sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ + sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED]) + +/* + * This is the master state machine processing function. + * + * If you want to understand all of lksctp, this is a + * good place to start. + */ +int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + int gfp) +{ + sctp_cmd_seq_t commands; + sctp_sm_table_entry_t *state_fn; + sctp_disposition_t status; + int error = 0; + typedef const char *(printfn_t)(sctp_subtype_t); + + static printfn_t *table[] = { + NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, + }; + printfn_t *debug_fn __attribute__ ((unused)) = table[event_type]; + + /* Look up the state function, run it, and then process the + * side effects. These three steps are the heart of lksctp. + */ + state_fn = sctp_sm_lookup_event(event_type, state, subtype); + + sctp_init_cmd_seq(&commands); + + DEBUG_PRE; + status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands); + DEBUG_POST; + + error = sctp_side_effects(event_type, subtype, state, + ep, asoc, event_arg, status, + &commands, gfp); + DEBUG_POST_SFX; + + return error; +} + +#undef DEBUG_PRE +#undef DEBUG_POST + +/***************************************************************** + * This the master state function side effect processing function. + *****************************************************************/ +int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int gfp) +{ + int error; + + /* FIXME - Most of the dispositions left today would be categorized + * as "exceptional" dispositions. For those dispositions, it + * may not be proper to run through any of the commands at all. + * For example, the command interpreter might be run only with + * disposition SCTP_DISPOSITION_CONSUME. + */ + if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state, + ep, asoc, + event_arg, status, + commands, gfp))) + goto bail; + + switch (status) { + case SCTP_DISPOSITION_DISCARD: + SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, " + "event_type %d, event_id %d\n", + state, event_type, subtype.chunk); + break; + + case SCTP_DISPOSITION_NOMEM: + /* We ran out of memory, so we need to discard this + * packet. + */ + /* BUG--we should now recover some memory, probably by + * reneging... + */ + error = -ENOMEM; + break; + + case SCTP_DISPOSITION_DELETE_TCB: + /* This should now be a command. */ + break; + + case SCTP_DISPOSITION_CONSUME: + case SCTP_DISPOSITION_ABORT: + /* + * We should no longer have much work to do here as the + * real work has been done as explicit commands above. + */ + break; + + case SCTP_DISPOSITION_VIOLATION: + printk(KERN_ERR "sctp protocol violation state %d " + "chunkid %d\n", state, subtype.chunk); + break; + + case SCTP_DISPOSITION_NOT_IMPL: + printk(KERN_WARNING "sctp unimplemented feature in state %d, " + "event_type %d, event_id %d\n", + state, event_type, subtype.chunk); + break; + + case SCTP_DISPOSITION_BUG: + printk(KERN_ERR "sctp bug in state %d, " + "event_type %d, event_id %d\n", + state, event_type, subtype.chunk); + BUG(); + break; + + default: + printk(KERN_ERR "sctp impossible disposition %d " + "in state %d, event_type %d, event_id %d\n", + status, state, event_type, subtype.chunk); + BUG(); + break; + }; + +bail: + return error; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* This is the side-effect interpreter. */ +int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, struct sctp_endpoint *ep, + struct sctp_association *asoc, void *event_arg, + sctp_disposition_t status, sctp_cmd_seq_t *commands, + int gfp) +{ + int error = 0; + int force; + sctp_cmd_t *cmd; + sctp_chunk_t *new_obj; + sctp_chunk_t *chunk = NULL; + struct sctp_packet *packet; + struct list_head *pos; + struct timer_list *timer; + unsigned long timeout; + struct sctp_transport *t; + sctp_sackhdr_t sackh; + + if(SCTP_EVENT_T_TIMEOUT != event_type) + chunk = (sctp_chunk_t *) event_arg; + + /* Note: This whole file is a huge candidate for rework. + * For example, each command could either have its own handler, so + * the loop would look like: + * while (cmds) + * cmd->handle(x, y, z) + * --jgrimm + */ + while (NULL != (cmd = sctp_next_cmd(commands))) { + switch (cmd->verb) { + case SCTP_CMD_NOP: + /* Do nothing. */ + break; + + case SCTP_CMD_NEW_ASOC: + /* Register a new association. */ + asoc = cmd->obj.ptr; + /* Register with the endpoint. */ + sctp_endpoint_add_asoc(ep, asoc); + sctp_hash_established(asoc); + break; + + case SCTP_CMD_UPDATE_ASSOC: + sctp_assoc_update(asoc, cmd->obj.ptr); + break; + + case SCTP_CMD_PURGE_OUTQUEUE: + sctp_outq_teardown(&asoc->outqueue); + break; + + case SCTP_CMD_DELETE_TCB: + /* Delete the current association. */ + sctp_cmd_delete_tcb(commands, asoc); + asoc = NULL; + break; + + case SCTP_CMD_NEW_STATE: + /* Enter a new state. */ + sctp_cmd_new_state(commands, asoc, cmd->obj.state); + break; + + case SCTP_CMD_REPORT_TSN: + /* Record the arrival of a TSN. */ + sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32); + break; + + case SCTP_CMD_GEN_SACK: + /* Generate a Selective ACK. + * The argument tells us whether to just count + * the packet and MAYBE generate a SACK, or + * force a SACK out. + */ + force = cmd->obj.i32; + error = sctp_gen_sack(asoc, force, commands); + break; + + case SCTP_CMD_PROCESS_SACK: + /* Process an inbound SACK. */ + error = sctp_cmd_process_sack(commands, asoc, + cmd->obj.ptr); + break; + + case SCTP_CMD_GEN_INIT_ACK: + /* Generate an INIT ACK chunk. */ + new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC, + 0); + if (!new_obj) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + break; + + case SCTP_CMD_PEER_INIT: + /* Process a unified INIT from the peer. + * Note: Only used during INIT-ACK processing. If + * there is an error just return to the outter + * layer which will bail. + */ + error = sctp_cmd_process_init(commands, asoc, chunk, + cmd->obj.ptr, gfp); + break; + + case SCTP_CMD_GEN_COOKIE_ECHO: + /* Generate a COOKIE ECHO chunk. */ + new_obj = sctp_make_cookie_echo(asoc, chunk); + if (!new_obj) { + if (cmd->obj.ptr) + sctp_free_chunk(cmd->obj.ptr); + goto nomem; + } + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + + /* If there is an ERROR chunk to be sent along with + * the COOKIE_ECHO, send it, too. + */ + if (cmd->obj.ptr) + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(cmd->obj.ptr)); + break; + + case SCTP_CMD_GEN_SHUTDOWN: + /* Generate SHUTDOWN when in SHUTDOWN_SENT state. + * Reset error counts. + */ + asoc->overall_error_count = 0; + + /* Generate a SHUTDOWN chunk. */ + new_obj = sctp_make_shutdown(asoc); + if (!new_obj) + goto nomem; + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + break; + + case SCTP_CMD_CHUNK_ULP: + /* Send a chunk to the sockets layer. */ + SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", + "chunk_up:", cmd->obj.ptr, + "ulpq:", &asoc->ulpq); + sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr, + GFP_ATOMIC); + break; + + case SCTP_CMD_EVENT_ULP: + /* Send a notification to the sockets layer. */ + SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", + "event_up:",cmd->obj.ptr, + "ulpq:",&asoc->ulpq); + sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr); + break; + + case SCTP_CMD_REPLY: + /* Send a chunk to our peer. */ + error = sctp_outq_tail(&asoc->outqueue, + cmd->obj.ptr); + break; + + case SCTP_CMD_SEND_PKT: + /* Send a full packet to our peer. */ + packet = cmd->obj.ptr; + sctp_packet_transmit(packet); + sctp_ootb_pkt_free(packet); + break; + + case SCTP_CMD_RETRAN: + /* Mark a transport for retransmission. */ + sctp_retransmit(&asoc->outqueue, cmd->obj.transport, + SCTP_RTXR_T3_RTX); + break; + + case SCTP_CMD_TRANSMIT: + /* Kick start transmission. */ + error = sctp_outq_flush(&asoc->outqueue, 0); + break; + + case SCTP_CMD_ECN_CE: + /* Do delayed CE processing. */ + sctp_do_ecn_ce_work(asoc, cmd->obj.u32); + break; + + case SCTP_CMD_ECN_ECNE: + /* Do delayed ECNE processing. */ + new_obj = sctp_do_ecn_ecne_work(asoc, cmd->obj.u32, + chunk); + if (new_obj) + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + break; + + case SCTP_CMD_ECN_CWR: + /* Do delayed CWR processing. */ + sctp_do_ecn_cwr_work(asoc, cmd->obj.u32); + break; + + case SCTP_CMD_SETUP_T2: + sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr); + break; + + case SCTP_CMD_TIMER_START: + timer = &asoc->timers[cmd->obj.to]; + timeout = asoc->timeouts[cmd->obj.to]; + if (!timeout) + BUG(); + + timer->expires = jiffies + timeout; + sctp_association_hold(asoc); + add_timer(timer); + break; + + case SCTP_CMD_TIMER_RESTART: + timer = &asoc->timers[cmd->obj.to]; + timeout = asoc->timeouts[cmd->obj.to]; + if (!mod_timer(timer, jiffies + timeout)) + sctp_association_hold(asoc); + break; + + case SCTP_CMD_TIMER_STOP: + timer = &asoc->timers[cmd->obj.to]; + if (timer_pending(timer) && del_timer(timer)) + sctp_association_put(asoc); + break; + + case SCTP_CMD_INIT_RESTART: + /* Do the needed accounting and updates + * associated with restarting an initialization + * timer. + */ + asoc->counters[SCTP_COUNTER_INIT_ERROR]++; + asoc->timeouts[cmd->obj.to] *= 2; + if (asoc->timeouts[cmd->obj.to] > + asoc->max_init_timeo) { + asoc->timeouts[cmd->obj.to] = + asoc->max_init_timeo; + } + + /* If we've sent any data bundled with + * COOKIE-ECHO we need to resend. + */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, + transports); + sctp_retransmit_mark(&asoc->outqueue, t, 0); + } + + sctp_add_cmd_sf(commands, + SCTP_CMD_TIMER_RESTART, + SCTP_TO(cmd->obj.to)); + break; + + case SCTP_CMD_INIT_FAILED: + sctp_cmd_init_failed(commands, asoc, cmd->obj.u32); + break; + + case SCTP_CMD_ASSOC_FAILED: + sctp_cmd_assoc_failed(commands, asoc, event_type, + subtype, chunk, cmd->obj.u32); + break; + + case SCTP_CMD_COUNTER_INC: + asoc->counters[cmd->obj.counter]++; + break; + + case SCTP_CMD_COUNTER_RESET: + asoc->counters[cmd->obj.counter] = 0; + break; + + case SCTP_CMD_REPORT_DUP: + sctp_tsnmap_mark_dup(&asoc->peer.tsn_map, + cmd->obj.u32); + break; + + case SCTP_CMD_REPORT_BAD_TAG: + SCTP_DEBUG_PRINTK("vtag mismatch!\n"); + break; + + case SCTP_CMD_STRIKE: + /* Mark one strike against a transport. */ + sctp_do_8_2_transport_strike(asoc, cmd->obj.transport); + break; + + case SCTP_CMD_TRANSPORT_RESET: + t = cmd->obj.transport; + sctp_cmd_transport_reset(commands, asoc, t); + break; + + case SCTP_CMD_TRANSPORT_ON: + t = cmd->obj.transport; + sctp_cmd_transport_on(commands, asoc, t, chunk); + break; + + case SCTP_CMD_HB_TIMERS_START: + sctp_cmd_hb_timers_start(commands, asoc); + break; + + case SCTP_CMD_HB_TIMER_UPDATE: + t = cmd->obj.transport; + sctp_cmd_hb_timer_update(commands, asoc, t); + break; + + case SCTP_CMD_HB_TIMERS_STOP: + sctp_cmd_hb_timers_stop(commands, asoc); + break; + + case SCTP_CMD_REPORT_ERROR: + error = cmd->obj.error; + break; + + case SCTP_CMD_PROCESS_CTSN: + /* Dummy up a SACK for processing. */ + sackh.cum_tsn_ack = cmd->obj.u32; + sackh.a_rwnd = 0; + sackh.num_gap_ack_blocks = 0; + sackh.num_dup_tsns = 0; + sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, + SCTP_SACKH(&sackh)); + break; + + case SCTP_CMD_DISCARD_PACKET: + /* We need to discard the whole packet. */ + chunk->pdiscard = 1; + break; + + case SCTP_CMD_RTO_PENDING: + t = cmd->obj.transport; + t->rto_pending = 1; + break; + + case SCTP_CMD_PART_DELIVER: + sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr, + GFP_ATOMIC); + break; + + case SCTP_CMD_RENEGE: + sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr, + GFP_ATOMIC); + break; + + default: + printk(KERN_WARNING "Impossible command: %u, %p\n", + cmd->verb, cmd->obj.ptr); + break; + }; + if (error) + return error; + } + + return error; + +nomem: + error = -ENOMEM; + return error; +} + diff -urN linux-2.4.22-bk23/net/sctp/sm_statefuns.c linux-2.4.22-bk24/net/sctp/sm_statefuns.c --- linux-2.4.22-bk23/net/sctp/sm_statefuns.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/sm_statefuns.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,4545 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2002 International Business Machines, Corp. + * Copyright (c) 2001-2002 Intel Corp. + * Copyright (c) 2002 Nokia Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * This is part of the SCTP Linux Kernel Reference Implementation. + * + * These are the state functions for the state machine. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Mathew Kotowsky + * Sridhar Samudrala + * Jon Grimm + * Hui Huang + * Dajiang Zhang + * Daisy Chang + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/********************************************************** + * These are the state functions for handling chunk events. + **********************************************************/ + +/* + * Process the final SHUTDOWN COMPLETE. + * + * Section: 4 (C) (diagram), 9.2 + * Upon reception of the SHUTDOWN COMPLETE chunk the endpoint will verify + * that it is in SHUTDOWN-ACK-SENT state, if it is not the chunk should be + * discarded. If the endpoint is in the SHUTDOWN-ACK-SENT state the endpoint + * should stop the T2-shutdown timer and remove all knowledge of the + * association (and thus the association enters the CLOSED state). + * + * Verification Tag: 8.5.1(C) + * C) Rules for packet carrying SHUTDOWN COMPLETE: + * ... + * - The receiver of a SHUTDOWN COMPLETE shall accept the packet if the + * Verification Tag field of the packet matches its own tag OR it is + * set to its peer's tag and the T bit is set in the Chunk Flags. + * Otherwise, the receiver MUST silently discard the packet and take + * no further action. An endpoint MUST ignore the SHUTDOWN COMPLETE if + * it is not in the SHUTDOWN-ACK-SENT state. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + struct sctp_ulpevent *ev; + + /* RFC 2960 6.10 Bundling + * + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* RFC 2960 10.2 SCTP-to-ULP + * + * H) SHUTDOWN COMPLETE notification + * + * When SCTP completes the shutdown procedures (section 9.2) this + * notification is passed to the upper layer. + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP, + 0, 0, 0, GFP_ATOMIC); + if (!ev) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + /* Upon reception of the SHUTDOWN COMPLETE chunk the endpoint + * will verify that it is in SHUTDOWN-ACK-SENT state, if it is + * not the chunk should be discarded. If the endpoint is in + * the SHUTDOWN-ACK-SENT state the endpoint should stop the + * T2-shutdown timer and remove all knowledge of the + * association (and thus the association enters the CLOSED + * state). + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + SCTP_INC_STATS(SctpShutdowns); + SCTP_DEC_STATS(SctpCurrEstab); + + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal INIT chunk. + * We are the side that is being asked for an association. + * + * Section: 5.1 Normal Establishment of an Association, B + * B) "Z" shall respond immediately with an INIT ACK chunk. The + * destination IP address of the INIT ACK MUST be set to the source + * IP address of the INIT to which this INIT ACK is responding. In + * the response, besides filling in other parameters, "Z" must set the + * Verification Tag field to Tag_A, and also provide its own + * Verification Tag (Tag_Z) in the Initiate Tag field. + * + * Verification Tag: No checking. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *repl; + struct sctp_association *new_asoc; + sctp_chunk_t *err_chunk; + struct sctp_packet *packet; + sctp_unrecognized_param_t *unk_param; + struct sock *sk; + int len; + + /* 6.10 Bundling + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* If the packet is an OOTB packet which is temporarily on the + * control endpoint, respond with an ABORT. + */ + if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + + sk = ep->base.sk; + /* If the endpoint is not listening or if the number of associations + * on the TCP-style socket exceed the max backlog, respond with an + * ABORT. + */ + if ((SCTP_SS_LISTENING != sk->state) || + ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) && + (sk->ack_backlog >= sk->max_ack_backlog))) + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + + /* Verify the INIT chunk before processing it. */ + err_chunk = NULL; + if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, + (sctp_init_chunk_t *)chunk->chunk_hdr, chunk, + &err_chunk)) { + /* This chunk contains fatal error. It is to be discarded. + * Send an ABORT, with causes if there is any. + */ + if (err_chunk) { + packet = sctp_abort_pkt_new(ep, asoc, arg, + (__u8 *)(err_chunk->chunk_hdr) + + sizeof(sctp_chunkhdr_t), + ntohs(err_chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t)); + + sctp_free_chunk(err_chunk); + + if (packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + SCTP_INC_STATS(SctpOutCtrlChunks); + return SCTP_DISPOSITION_CONSUME; + } else { + return SCTP_DISPOSITION_NOMEM; + } + } else { + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, + commands); + } + } + + /* Grab the INIT header. */ + chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data; + + /* Tag the variable length parameters. */ + chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); + + new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC); + if (!new_asoc) + goto nomem; + + /* The call, sctp_process_init(), can fail on memory allocation. */ + if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), + (sctp_init_chunk_t *)chunk->chunk_hdr, + GFP_ATOMIC)) + goto nomem_init; + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + + /* B) "Z" shall respond immediately with an INIT ACK chunk. */ + + /* If there are errors need to be reported for unknown parameters, + * make sure to reserve enough room in the INIT ACK for them. + */ + len = 0; + if (err_chunk) + len = ntohs(err_chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t); + + if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0) + goto nomem_ack; + + repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len); + if (!repl) + goto nomem_ack; + + /* If there are errors need to be reported for unknown parameters, + * include them in the outgoing INIT ACK as "Unrecognized parameter" + * parameter. + */ + if (err_chunk) { + /* Get the "Unrecognized parameter" parameter(s) out of the + * ERROR chunk generated by sctp_verify_init(). Since the + * error cause code for "unknown parameter" and the + * "Unrecognized parameter" type is the same, we can + * construct the parameters in INIT ACK by copying the + * ERROR causes over. + */ + unk_param = (sctp_unrecognized_param_t *) + ((__u8 *)(err_chunk->chunk_hdr) + + sizeof(sctp_chunkhdr_t)); + /* Replace the cause code with the "Unrecognized parameter" + * parameter type. + */ + sctp_addto_chunk(repl, len, unk_param); + sctp_free_chunk(err_chunk); + } + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* + * Note: After sending out INIT ACK with the State Cookie parameter, + * "Z" MUST NOT allocate any resources, nor keep any states for the + * new association. Otherwise, "Z" will be vulnerable to resource + * attacks. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; + +nomem_ack: + if (err_chunk) + sctp_free_chunk(err_chunk); +nomem_init: + sctp_association_free(new_asoc); +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal INIT ACK chunk. + * We are the side that is initiating the association. + * + * Section: 5.1 Normal Establishment of an Association, C + * C) Upon reception of the INIT ACK from "Z", "A" shall stop the T1-init + * timer and leave COOKIE-WAIT state. "A" shall then send the State + * Cookie received in the INIT ACK chunk in a COOKIE ECHO chunk, start + * the T1-cookie timer, and enter the COOKIE-ECHOED state. + * + * Note: The COOKIE ECHO chunk can be bundled with any pending outbound + * DATA chunks, but it MUST be the first chunk in the packet and + * until the COOKIE ACK is returned the sender MUST NOT send any + * other packets to the peer. + * + * Verification Tag: 3.3.3 + * If the value of the Initiate Tag in a received INIT ACK chunk is + * found to be 0, the receiver MUST treat it as an error and close the + * association by transmitting an ABORT. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_init_chunk_t *initchunk; + __u32 init_tag; + sctp_chunk_t *err_chunk; + struct sctp_packet *packet; + sctp_disposition_t ret; + + /* 6.10 Bundling + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* Grab the INIT header. */ + chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; + + init_tag = ntohl(chunk->subh.init_hdr->init_tag); + + /* Verification Tag: 3.3.3 + * If the value of the Initiate Tag in a received INIT ACK + * chunk is found to be 0, the receiver MUST treat it as an + * error and close the association by transmitting an ABORT. + */ + if (!init_tag) { + sctp_chunk_t *reply = sctp_make_abort(asoc, chunk, 0); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + SCTP_INC_STATS(SctpAborteds); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* Verify the INIT chunk before processing it. */ + err_chunk = NULL; + if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, + (sctp_init_chunk_t *)chunk->chunk_hdr, chunk, + &err_chunk)) { + + SCTP_INC_STATS(SctpAborteds); + + /* This chunk contains fatal error. It is to be discarded. + * Send an ABORT, with causes if there is any. + */ + if (err_chunk) { + packet = sctp_abort_pkt_new(ep, asoc, arg, + (__u8 *)(err_chunk->chunk_hdr) + + sizeof(sctp_chunkhdr_t), + ntohs(err_chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t)); + + sctp_free_chunk(err_chunk); + + if (packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + SCTP_INC_STATS(SctpOutCtrlChunks); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, + SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; + } else { + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, + SCTP_NULL()); + return SCTP_DISPOSITION_NOMEM; + } + } else { + ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg, + commands); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, + SCTP_NULL()); + return ret; + } + } + + /* Tag the variable length parameters. Note that we never + * convert the parameters in an INIT chunk. + */ + chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); + + initchunk = (sctp_init_chunk_t *) chunk->chunk_hdr; + + sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT, + SCTP_PEER_INIT(initchunk)); + + /* 5.1 C) "A" shall stop the T1-init timer and leave + * COOKIE-WAIT state. "A" shall then ... start the T1-cookie + * timer, and enter the COOKIE-ECHOED state. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_RESET, + SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR)); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_COOKIE_ECHOED)); + + /* 5.1 C) "A" shall then send the State Cookie received in the + * INIT ACK chunk in a COOKIE ECHO chunk, ... + */ + /* If there is any errors to report, send the ERROR chunk generated + * for unknown parameters as well. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO, + SCTP_CHUNK(err_chunk)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal COOKIE ECHO chunk. + * We are the side that is being asked for an association. + * + * Section: 5.1 Normal Establishment of an Association, D + * D) Upon reception of the COOKIE ECHO chunk, Endpoint "Z" will reply + * with a COOKIE ACK chunk after building a TCB and moving to + * the ESTABLISHED state. A COOKIE ACK chunk may be bundled with + * any pending DATA chunks (and/or SACK chunks), but the COOKIE ACK + * chunk MUST be the first chunk in the packet. + * + * IMPLEMENTATION NOTE: An implementation may choose to send the + * Communication Up notification to the SCTP user upon reception + * of a valid COOKIE ECHO chunk. + * + * Verification Tag: 8.5.1 Exceptions in Verification Tag Rules + * D) Rules for packet carrying a COOKIE ECHO + * + * - When sending a COOKIE ECHO, the endpoint MUST use the value of the + * Initial Tag received in the INIT ACK. + * + * - The receiver of a COOKIE ECHO follows the procedures in Section 5. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + struct sctp_association *new_asoc; + sctp_init_chunk_t *peer_init; + sctp_chunk_t *repl; + struct sctp_ulpevent *ev; + int error = 0; + sctp_chunk_t *err_chk_p; + + /* If the packet is an OOTB packet which is temporarily on the + * control endpoint, respond with an ABORT. + */ + if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) + return sctp_sf_ootb(ep, asoc, type, arg, commands); + + /* "Decode" the chunk. We have no optional parameters so we + * are in good shape. + */ + chunk->subh.cookie_hdr = + (sctp_signed_cookie_t *)chunk->skb->data; + skb_pull(chunk->skb, + ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t)); + + /* 5.1 D) Upon reception of the COOKIE ECHO chunk, Endpoint + * "Z" will reply with a COOKIE ACK chunk after building a TCB + * and moving to the ESTABLISHED state. + */ + new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error, + &err_chk_p); + + /* FIXME: + * If the re-build failed, what is the proper error path + * from here? + * + * [We should abort the association. --piggy] + */ + if (!new_asoc) { + /* FIXME: Several errors are possible. A bad cookie should + * be silently discarded, but think about logging it too. + */ + switch (error) { + case -SCTP_IERROR_NOMEM: + goto nomem; + + case -SCTP_IERROR_STALE_COOKIE: + sctp_send_stale_cookie_err(ep, asoc, chunk, commands, + err_chk_p); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + case -SCTP_IERROR_BAD_SIG: + default: + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + }; + } + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + SCTP_INC_STATS(SctpCurrEstab); + SCTP_INC_STATS(SctpPassiveEstabs); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); + + if (new_asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + /* Re-build the bind address for the association is done in + * the sctp_unpack_cookie() already. + */ + /* This is a brand-new association, so these are not yet side + * effects--it is safe to run them here. + */ + peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; + + if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + &chunk->subh.cookie_hdr->c.peer_addr, + peer_init, GFP_ATOMIC)) + goto nomem_init; + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem_repl; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * D) IMPLEMENTATION NOTE: An implementation may choose to + * send the Communication Up notification to the SCTP user + * upon reception of a valid COOKIE ECHO chunk. + */ + ev = sctp_ulpevent_make_assoc_change(new_asoc, 0, SCTP_COMM_UP, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem_ev; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + return SCTP_DISPOSITION_CONSUME; + +nomem_ev: + sctp_free_chunk(repl); +nomem_repl: +nomem_init: + sctp_association_free(new_asoc); +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal COOKIE ACK chunk. + * We are the side that is being asked for an association. + * + * RFC 2960 5.1 Normal Establishment of an Association + * + * E) Upon reception of the COOKIE ACK, endpoint "A" will move from the + * COOKIE-ECHOED state to the ESTABLISHED state, stopping the T1-cookie + * timer. It may also notify its ULP about the successful + * establishment of the association with a Communication Up + * notification (see Section 10). + * + * Verification Tag: + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_ulpevent *ev; + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * E) Upon reception of the COOKIE ACK, endpoint "A" will move + * from the COOKIE-ECHOED state to the ESTABLISHED state, + * stopping the T1-cookie timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + SCTP_INC_STATS(SctpCurrEstab); + SCTP_INC_STATS(SctpActiveEstabs); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); + if (asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + /* It may also notify its ULP about the successful + * establishment of the association with a Communication Up + * notification (see Section 10). + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, + 0, asoc->c.sinit_num_ostreams, + asoc->c.sinit_max_instreams, + GFP_ATOMIC); + + if (!ev) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + return SCTP_DISPOSITION_CONSUME; +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Generate and sendout a heartbeat packet. */ +sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_transport *transport = (struct sctp_transport *) arg; + sctp_chunk_t *reply; + sctp_sender_hb_info_t hbinfo; + size_t paylen = 0; + + hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; + hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); + hbinfo.daddr = transport->ipaddr; + hbinfo.sent_at = jiffies; + + /* Send a heartbeat to our peer. */ + paylen = sizeof(sctp_sender_hb_info_t); + reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen); + if (!reply) + return SCTP_DISPOSITION_NOMEM; + + /* Set rto_pending indicating that an RTT measurement + * is started with this heartbeat chunk. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_RTO_PENDING, + SCTP_TRANSPORT(transport)); + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + return SCTP_DISPOSITION_CONSUME; +} + +/* Generate a HEARTBEAT packet on the given transport. */ +sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_transport *transport = (struct sctp_transport *) arg; + + if (asoc->overall_error_count >= asoc->overall_error_threshold) { + /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* Section 3.3.5. + * The Sender-specific Heartbeat Info field should normally include + * information about the sender's current time when this HEARTBEAT + * chunk is sent and the destination transport address to which this + * HEARTBEAT is sent (see Section 8.3). + */ + + if (transport->hb_allowed) { + if (SCTP_DISPOSITION_NOMEM == + sctp_sf_heartbeat(ep, asoc, type, arg, + commands)) + return SCTP_DISPOSITION_NOMEM; + /* Set transport error counter and association error counter + * when sending heartbeat. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET, + SCTP_TRANSPORT(transport)); + } + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMER_UPDATE, + SCTP_TRANSPORT(transport)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Process an heartbeat request. + * + * Section: 8.3 Path Heartbeat + * The receiver of the HEARTBEAT should immediately respond with a + * HEARTBEAT ACK that contains the Heartbeat Information field copied + * from the received HEARTBEAT chunk. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * When receiving an SCTP packet, the endpoint MUST ensure that the + * value in the Verification Tag field of the received SCTP packet + * matches its own Tag. If the received Verification Tag value does not + * match the receiver's own tag value, the receiver shall silently + * discard the packet and shall not process it any further except for + * those cases listed in Section 8.5.1 below. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *reply; + size_t paylen = 0; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* 8.3 The receiver of the HEARTBEAT should immediately + * respond with a HEARTBEAT ACK that contains the Heartbeat + * Information field copied from the received HEARTBEAT chunk. + */ + chunk->subh.hb_hdr = (sctp_heartbeathdr_t *) chunk->skb->data; + paylen = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); + skb_pull(chunk->skb, paylen); + + reply = sctp_make_heartbeat_ack(asoc, chunk, + chunk->subh.hb_hdr, paylen); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process the returning HEARTBEAT ACK. + * + * Section: 8.3 Path Heartbeat + * Upon the receipt of the HEARTBEAT ACK, the sender of the HEARTBEAT + * should clear the error counter of the destination transport + * address to which the HEARTBEAT was sent, and mark the destination + * transport address as active if it is not so marked. The endpoint may + * optionally report to the upper layer when an inactive destination + * address is marked as active due to the reception of the latest + * HEARTBEAT ACK. The receiver of the HEARTBEAT ACK must also + * clear the association overall error count as well (as defined + * in section 8.1). + * + * The receiver of the HEARTBEAT ACK should also perform an RTT + * measurement for that destination transport address using the time + * value carried in the HEARTBEAT ACK chunk. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + union sctp_addr from_addr; + struct sctp_transport *link; + sctp_sender_hb_info_t *hbinfo; + unsigned long max_interval; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; + from_addr = hbinfo->daddr; + link = sctp_assoc_lookup_paddr(asoc, &from_addr); + + /* This should never happen, but lets log it if so. */ + if (!link) { + printk(KERN_WARNING + "%s: Could not find address %d.%d.%d.%d\n", + __FUNCTION__, NIPQUAD(from_addr.v4.sin_addr)); + return SCTP_DISPOSITION_DISCARD; + } + + max_interval = link->hb_interval + link->rto; + + /* Check if the timestamp looks valid. */ + if (time_after(hbinfo->sent_at, jiffies) || + time_after(jiffies, hbinfo->sent_at + max_interval)) { + SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp" + "received for transport: %p\n", + __FUNCTION__, link); + return SCTP_DISPOSITION_DISCARD; + } + + /* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of + * the HEARTBEAT should clear the error counter of the + * destination transport address to which the HEARTBEAT was + * sent and mark the destination transport address as active if + * it is not so marked. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_ON, SCTP_TRANSPORT(link)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* Helper function to send out an abort for the restart + * condition. + */ +static int sctp_sf_send_restart_abort(union sctp_addr *ssa, + sctp_chunk_t *init, + sctp_cmd_seq_t *commands) +{ + int len; + struct sctp_packet *pkt; + sctp_addr_param_t *addrparm; + sctp_errhdr_t *errhdr; + struct sctp_endpoint *ep; + char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)]; + + /* Build the error on the stack. We are way to malloc crazy + * throughout the code today. + */ + errhdr = (sctp_errhdr_t *)buffer; + addrparm = (sctp_addr_param_t *)errhdr->variable; + + /* Copy into a parm format. */ + len = sockaddr2sctp_addr(ssa, addrparm); + len += sizeof(sctp_errhdr_t); + + errhdr->cause = SCTP_ERROR_RESTART; + errhdr->length = htons(len); + + /* Assign to the control socket. */ + ep = sctp_sk((sctp_get_ctl_sock()))->ep; + + /* Association is NULL since this may be a restart attack and we + * want to send back the attacker's vtag. + */ + pkt = sctp_abort_pkt_new(ep, NULL, init, errhdr, len); + + if (!pkt) + goto out; + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(pkt)); + + SCTP_INC_STATS(SctpOutCtrlChunks); + + /* Discard the rest of the inbound packet. */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); + +out: + /* Even if there is no memory, treat as a failure so + * the packet will get dropped. + */ + return 0; +} + +/* A restart is occurring, check to make sure no new addresses + * are being added as we may be under a takeover attack. + */ +static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc, + const struct sctp_association *asoc, + sctp_chunk_t *init, + sctp_cmd_seq_t *commands) +{ + struct sctp_transport *new_addr, *addr; + struct list_head *pos, *pos2; + int found; + + /* Implementor's Guide - Sectin 5.2.2 + * ... + * Before responding the endpoint MUST check to see if the + * unexpected INIT adds new addresses to the association. If new + * addresses are added to the association, the endpoint MUST respond + * with an ABORT.. + */ + + /* Search through all current addresses and make sure + * we aren't adding any new ones. + */ + new_addr = 0; + found = 0; + + list_for_each(pos, &new_asoc->peer.transport_addr_list) { + new_addr = list_entry(pos, struct sctp_transport, transports); + found = 0; + list_for_each(pos2, &asoc->peer.transport_addr_list) { + addr = list_entry(pos2, struct sctp_transport, + transports); + if (sctp_cmp_addr_exact(&new_addr->ipaddr, + &addr->ipaddr)) { + found = 1; + break; + } + } + if (!found) + break; + } + + /* If a new address was added, ABORT the sender. */ + if (!found && new_addr) { + sctp_sf_send_restart_abort(&new_addr->ipaddr, init, commands); + } + + /* Return success if all addresses were found. */ + return found; +} + +/* Populate the verification/tie tags based on overlapping INIT + * scenario. + * + * Note: Do not use in CLOSED or SHUTDOWN-ACK-SENT state. + */ +static void sctp_tietags_populate(struct sctp_association *new_asoc, + const struct sctp_association *asoc) +{ + switch (asoc->state) { + + /* 5.2.1 INIT received in COOKIE-WAIT or COOKIE-ECHOED State */ + + case SCTP_STATE_COOKIE_WAIT: + new_asoc->c.my_vtag = asoc->c.my_vtag; + new_asoc->c.my_ttag = asoc->c.my_vtag; + new_asoc->c.peer_ttag = 0; + break; + + case SCTP_STATE_COOKIE_ECHOED: + new_asoc->c.my_vtag = asoc->c.my_vtag; + new_asoc->c.my_ttag = asoc->c.my_vtag; + new_asoc->c.peer_ttag = asoc->c.peer_vtag; + break; + + /* 5.2.2 Unexpected INIT in States Other than CLOSED, COOKIE-ECHOED, + * COOKIE-WAIT and SHUTDOWN-ACK-SENT + */ + default: + new_asoc->c.my_ttag = asoc->c.my_vtag; + new_asoc->c.peer_ttag = asoc->c.peer_vtag; + break; + }; + + /* Other parameters for the endpoint SHOULD be copied from the + * existing parameters of the association (e.g. number of + * outbound streams) into the INIT ACK and cookie. + */ + new_asoc->rwnd = asoc->rwnd; + new_asoc->c.sinit_num_ostreams = asoc->c.sinit_num_ostreams; + new_asoc->c.sinit_max_instreams = asoc->c.sinit_max_instreams; + new_asoc->c.initial_tsn = asoc->c.initial_tsn; +} + +/* + * Compare vtag/tietag values to determine unexpected COOKIE-ECHO + * handling action. + * + * RFC 2960 5.2.4 Handle a COOKIE ECHO when a TCB exists. + * + * Returns value representing action to be taken. These action values + * correspond to Action/Description values in RFC 2960, Table 2. + */ +static char sctp_tietags_compare(struct sctp_association *new_asoc, + const struct sctp_association *asoc) +{ + /* In this case, the peer may have restarted. */ + if ((asoc->c.my_vtag != new_asoc->c.my_vtag) && + (asoc->c.peer_vtag != new_asoc->c.peer_vtag) && + (asoc->c.my_vtag == new_asoc->c.my_ttag) && + (asoc->c.peer_vtag == new_asoc->c.peer_ttag)) + return 'A'; + + /* Collision case B. */ + if ((asoc->c.my_vtag == new_asoc->c.my_vtag) && + ((asoc->c.peer_vtag != new_asoc->c.peer_vtag) || + (0 == asoc->c.peer_vtag))) { + return 'B'; + } + + /* Collision case D. */ + if ((asoc->c.my_vtag == new_asoc->c.my_vtag) && + (asoc->c.peer_vtag == new_asoc->c.peer_vtag)) + return 'D'; + + /* Collision case C. */ + if ((asoc->c.my_vtag != new_asoc->c.my_vtag) && + (asoc->c.peer_vtag == new_asoc->c.peer_vtag) && + (0 == new_asoc->c.my_ttag) && + (0 == new_asoc->c.peer_ttag)) + return 'C'; + + /* No match to any of the special cases; discard this packet. */ + return 'E'; +} + +/* Common helper routine for both duplicate and simulataneous INIT + * chunk handling. + */ +static sctp_disposition_t sctp_sf_do_unexpected_init( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, sctp_cmd_seq_t *commands) +{ + sctp_disposition_t retval; + sctp_chunk_t *chunk = arg; + sctp_chunk_t *repl; + struct sctp_association *new_asoc; + sctp_chunk_t *err_chunk; + struct sctp_packet *packet; + sctp_unrecognized_param_t *unk_param; + int len; + + /* 6.10 Bundling + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* Grab the INIT header. */ + chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; + + /* Tag the variable length parameters. */ + chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); + + /* Verify the INIT chunk before processing it. */ + err_chunk = NULL; + if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, + (sctp_init_chunk_t *)chunk->chunk_hdr, chunk, + &err_chunk)) { + /* This chunk contains fatal error. It is to be discarded. + * Send an ABORT, with causes if there is any. + */ + if (err_chunk) { + packet = sctp_abort_pkt_new(ep, asoc, arg, + (__u8 *)(err_chunk->chunk_hdr) + + sizeof(sctp_chunkhdr_t), + ntohs(err_chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t)); + + if (packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + SCTP_INC_STATS(SctpOutCtrlChunks); + retval = SCTP_DISPOSITION_CONSUME; + } else { + retval = SCTP_DISPOSITION_NOMEM; + } + goto cleanup; + } else { + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, + commands); + } + } + + /* + * Other parameters for the endpoint SHOULD be copied from the + * existing parameters of the association (e.g. number of + * outbound streams) into the INIT ACK and cookie. + * FIXME: We are copying parameters from the endpoint not the + * association. + */ + new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC); + if (!new_asoc) + goto nomem; + + /* In the outbound INIT ACK the endpoint MUST copy its current + * Verification Tag and Peers Verification tag into a reserved + * place (local tie-tag and per tie-tag) within the state cookie. + */ + if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), + (sctp_init_chunk_t *)chunk->chunk_hdr, + GFP_ATOMIC)) { + retval = SCTP_DISPOSITION_NOMEM; + goto nomem_init; + } + + /* Make sure no new addresses are being added during the + * restart. Do not do this check for COOKIE-WAIT state, + * since there are no peer addresses to check against. + * Upon return an ABORT will have been sent if needed. + */ + if (asoc->state != SCTP_STATE_COOKIE_WAIT) { + if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, + commands)) { + retval = SCTP_DISPOSITION_CONSUME; + goto cleanup_asoc; + } + } + + sctp_tietags_populate(new_asoc, asoc); + + /* B) "Z" shall respond immediately with an INIT ACK chunk. */ + + /* If there are errors need to be reported for unknown parameters, + * make sure to reserve enough room in the INIT ACK for them. + */ + len = 0; + if (err_chunk) { + len = ntohs(err_chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t); + } + + if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0) + goto nomem; + + repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len); + if (!repl) + goto nomem; + + /* If there are errors need to be reported for unknown parameters, + * include them in the outgoing INIT ACK as "Unrecognized parameter" + * parameter. + */ + if (err_chunk) { + /* Get the "Unrecognized parameter" parameter(s) out of the + * ERROR chunk generated by sctp_verify_init(). Since the + * error cause code for "unknown parameter" and the + * "Unrecognized parameter" type is the same, we can + * construct the parameters in INIT ACK by copying the + * ERROR causes over. + */ + unk_param = (sctp_unrecognized_param_t *) + ((__u8 *)(err_chunk->chunk_hdr) + + sizeof(sctp_chunkhdr_t)); + /* Replace the cause code with the "Unrecognized parameter" + * parameter type. + */ + sctp_addto_chunk(repl, len, unk_param); + sctp_free_chunk(err_chunk); + } + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* + * Note: After sending out INIT ACK with the State Cookie parameter, + * "Z" MUST NOT allocate any resources for this new association. + * Otherwise, "Z" will be vulnerable to resource attacks. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + retval = SCTP_DISPOSITION_CONSUME; + +cleanup: + if (err_chunk) + sctp_free_chunk(err_chunk); + return retval; +nomem: + retval = SCTP_DISPOSITION_NOMEM; + goto cleanup; +nomem_init: +cleanup_asoc: + sctp_association_free(new_asoc); + goto cleanup; +} + +/* + * Handle simultanous INIT. + * This means we started an INIT and then we got an INIT request from + * our peer. + * + * Section: 5.2.1 INIT received in COOKIE-WAIT or COOKIE-ECHOED State (Item B) + * This usually indicates an initialization collision, i.e., each + * endpoint is attempting, at about the same time, to establish an + * association with the other endpoint. + * + * Upon receipt of an INIT in the COOKIE-WAIT or COOKIE-ECHOED state, an + * endpoint MUST respond with an INIT ACK using the same parameters it + * sent in its original INIT chunk (including its Verification Tag, + * unchanged). These original parameters are combined with those from the + * newly received INIT chunk. The endpoint shall also generate a State + * Cookie with the INIT ACK. The endpoint uses the parameters sent in its + * INIT to calculate the State Cookie. + * + * After that, the endpoint MUST NOT change its state, the T1-init + * timer shall be left running and the corresponding TCB MUST NOT be + * destroyed. The normal procedures for handling State Cookies when + * a TCB exists will resolve the duplicate INITs to a single association. + * + * For an endpoint that is in the COOKIE-ECHOED state it MUST populate + * its Tie-Tags with the Tag information of itself and its peer (see + * section 5.2.2 for a description of the Tie-Tags). + * + * Verification Tag: Not explicit, but an INIT can not have a valid + * verification tag, so we skip the check. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_1_siminit(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Call helper to do the real work for both simulataneous and + * duplicate INIT chunk handling. + */ + return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands); +} + +/* + * Handle duplicated INIT messages. These are usually delayed + * restransmissions. + * + * Section: 5.2.2 Unexpected INIT in States Other than CLOSED, + * COOKIE-ECHOED and COOKIE-WAIT + * + * Unless otherwise stated, upon reception of an unexpected INIT for + * this association, the endpoint shall generate an INIT ACK with a + * State Cookie. In the outbound INIT ACK the endpoint MUST copy its + * current Verification Tag and peer's Verification Tag into a reserved + * place within the state cookie. We shall refer to these locations as + * the Peer's-Tie-Tag and the Local-Tie-Tag. The outbound SCTP packet + * containing this INIT ACK MUST carry a Verification Tag value equal to + * the Initiation Tag found in the unexpected INIT. And the INIT ACK + * MUST contain a new Initiation Tag (randomly generated see Section + * 5.3.1). Other parameters for the endpoint SHOULD be copied from the + * existing parameters of the association (e.g. number of outbound + * streams) into the INIT ACK and cookie. + * + * After sending out the INIT ACK, the endpoint shall take no further + * actions, i.e., the existing association, including its current state, + * and the corresponding TCB MUST NOT be changed. + * + * Note: Only when a TCB exists and the association is not in a COOKIE- + * WAIT state are the Tie-Tags populated. For a normal association INIT + * (i.e. the endpoint is in a COOKIE-WAIT state), the Tie-Tags MUST be + * set to 0 (indicating that no previous TCB existed). The INIT ACK and + * State Cookie are populated as specified in section 5.2.1. + * + * Verification Tag: Not specified, but an INIT has no way of knowing + * what the verification tag could be, so we ignore it. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Call helper to do the real work for both simulataneous and + * duplicate INIT chunk handling. + */ + return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands); +} + + + +/* Unexpected COOKIE-ECHO handler for peer restart (Table 2, action 'A') + * + * Section 5.2.4 + * A) In this case, the peer may have restarted. + */ +static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + struct sctp_association *new_asoc) +{ + sctp_init_chunk_t *peer_init; + struct sctp_ulpevent *ev; + sctp_chunk_t *repl; + + /* new_asoc is a brand-new association, so these are not yet + * side effects--it is safe to run them here. + */ + peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; + + if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), peer_init, + GFP_ATOMIC)) + goto nomem; + + /* Make sure no new addresses are being added during the + * restart. Though this is a pretty complicated attack + * since you'd have to get inside the cookie. + */ + if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) { + return SCTP_DISPOSITION_CONSUME; + } + + /* For now, fail any unsent/unacked data. Consider the optional + * choice of resending of this data. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL()); + + /* Update the content of current association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* Report association restart to upper layer. */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_RESTART, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem_ev; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + return SCTP_DISPOSITION_CONSUME; + +nomem_ev: + sctp_free_chunk(repl); +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Unexpected COOKIE-ECHO handler for setup collision (Table 2, action 'B') + * + * Section 5.2.4 + * B) In this case, both sides may be attempting to start an association + * at about the same time but the peer endpoint started its INIT + * after responding to the local endpoint's INIT + */ +/* This case represents an initialization collision. */ +static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + struct sctp_association *new_asoc) +{ + sctp_init_chunk_t *peer_init; + struct sctp_ulpevent *ev; + sctp_chunk_t *repl; + + /* new_asoc is a brand-new association, so these are not yet + * side effects--it is safe to run them here. + */ + peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; + if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), peer_init, + GFP_ATOMIC)) + goto nomem; + + /* Update the content of current association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + SCTP_INC_STATS(SctpCurrEstab); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * D) IMPLEMENTATION NOTE: An implementation may choose to + * send the Communication Up notification to the SCTP user + * upon reception of a valid COOKIE ECHO chunk. + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem_ev; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + return SCTP_DISPOSITION_CONSUME; + +nomem_ev: + sctp_free_chunk(repl); +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Unexpected COOKIE-ECHO handler for setup collision (Table 2, action 'C') + * + * Section 5.2.4 + * C) In this case, the local endpoint's cookie has arrived late. + * Before it arrived, the local endpoint sent an INIT and received an + * INIT-ACK and finally sent a COOKIE ECHO with the peer's same tag + * but a new tag of its own. + */ +/* This case represents an initialization collision. */ +static sctp_disposition_t sctp_sf_do_dupcook_c(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + struct sctp_association *new_asoc) +{ + /* The cookie should be silently discarded. + * The endpoint SHOULD NOT change states and should leave + * any timers running. + */ + return SCTP_DISPOSITION_DISCARD; +} + +/* Unexpected COOKIE-ECHO handler lost chunk (Table 2, action 'D') + * + * Section 5.2.4 + * + * D) When both local and remote tags match the endpoint should always + * enter the ESTABLISHED state, if it has not already done so. + */ +/* This case represents an initialization collision. */ +static sctp_disposition_t sctp_sf_do_dupcook_d(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + struct sctp_association *new_asoc) +{ + struct sctp_ulpevent *ev = NULL; + sctp_chunk_t *repl; + + /* Clarification from Implementor's Guide: + * D) When both local and remote tags match the endpoint should + * enter the ESTABLISHED state, if it is in the COOKIE-ECHOED state. + * It should stop any cookie timer that may be running and send + * a COOKIE ACK. + */ + + /* Don't accidentally move back into established state. */ + if (asoc->state < SCTP_STATE_ESTABLISHED) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + SCTP_INC_STATS(SctpCurrEstab); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, + SCTP_NULL()); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * D) IMPLEMENTATION NOTE: An implementation may choose + * to send the Communication Up notification to the + * SCTP user upon reception of a valid COOKIE + * ECHO chunk. + */ + ev = sctp_ulpevent_make_assoc_change(new_asoc, 0, + SCTP_COMM_UP, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem; + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(ev)); + } + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + if (ev) + sctp_ulpevent_free(ev); + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Handle a duplicate COOKIE-ECHO. This usually means a cookie-carrying + * chunk was retransmitted and then delayed in the network. + * + * Section: 5.2.4 Handle a COOKIE ECHO when a TCB exists + * + * Verification Tag: None. Do cookie validation. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_disposition_t retval; + sctp_chunk_t *chunk = arg; + struct sctp_association *new_asoc; + int error = 0; + char action; + sctp_chunk_t *err_chk_p; + + /* "Decode" the chunk. We have no optional parameters so we + * are in good shape. + */ + chunk->subh.cookie_hdr = (sctp_signed_cookie_t *)chunk->skb->data; + skb_pull(chunk->skb, ntohs(chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t)); + + /* In RFC 2960 5.2.4 3, if both Verification Tags in the State Cookie + * of a duplicate COOKIE ECHO match the Verification Tags of the + * current association, consider the State Cookie valid even if + * the lifespan is exceeded. + */ + new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error, + &err_chk_p); + + /* FIXME: + * If the re-build failed, what is the proper error path + * from here? + * + * [We should abort the association. --piggy] + */ + if (!new_asoc) { + /* FIXME: Several errors are possible. A bad cookie should + * be silently discarded, but think about logging it too. + */ + switch (error) { + case -SCTP_IERROR_NOMEM: + goto nomem; + + case -SCTP_IERROR_STALE_COOKIE: + sctp_send_stale_cookie_err(ep, asoc, chunk, commands, + err_chk_p); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + case -SCTP_IERROR_BAD_SIG: + default: + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + }; + } + + /* Compare the tie_tag in cookie with the verification tag of + * current association. + */ + action = sctp_tietags_compare(new_asoc, asoc); + + switch (action) { + case 'A': /* Association restart. */ + retval = sctp_sf_do_dupcook_a(ep, asoc, chunk, commands, + new_asoc); + break; + + case 'B': /* Collision case B. */ + retval = sctp_sf_do_dupcook_b(ep, asoc, chunk, commands, + new_asoc); + break; + + case 'C': /* Collision case C. */ + retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands, + new_asoc); + break; + + case 'D': /* Collision case D. */ + retval = sctp_sf_do_dupcook_d(ep, asoc, chunk, commands, + new_asoc); + break; + + default: /* Discard packet for all others. */ + retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands); + break; + }; + + /* Delete the tempory new association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return retval; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process an ABORT. (SHUTDOWN-PENDING state) + * + * See sctp_sf_do_9_1_abort(). + */ +sctp_disposition_t sctp_sf_shutdown_pending_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Stop the T5-shutdown guard timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + return sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands); +} + +/* + * Process an ABORT. (SHUTDOWN-SENT state) + * + * See sctp_sf_do_9_1_abort(). + */ +sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Stop the T2-shutdown timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + /* Stop the T5-shutdown guard timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + return sctp_sf_do_9_1_abort(ep, asoc, type, arg, commands); +} + +/* + * Process an ABORT. (SHUTDOWN-ACK-SENT state) + * + * See sctp_sf_do_9_1_abort(). + */ +sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* The same T2 timer, so we should be able to use + * common function with the SHUTDOWN-SENT state. + */ + return sctp_sf_shutdown_sent_abort(ep, asoc, type, arg, commands); +} + +/* + * Handle an Error received in COOKIE_ECHOED state. + * + * Only handle the error type of stale COOKIE Error, the other errors will + * be ignored. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_errhdr_t *err; + + err = (sctp_errhdr_t *)(chunk->skb->data); + + /* If we have gotten too many failures, give up. */ + if (1 + asoc->counters[SCTP_COUNTER_INIT_ERROR] > + asoc->max_init_attempts) { + /* INIT_FAILED will issue an ulpevent. */ + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(err->cause)); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* Process the error here */ + switch (err->cause) { + case SCTP_ERROR_STALE_COOKIE: + return sctp_sf_do_5_2_6_stale(ep, asoc, type, arg, commands); + default: + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } +} + +/* + * Handle a Stale COOKIE Error + * + * Section: 5.2.6 Handle Stale COOKIE Error + * If the association is in the COOKIE-ECHOED state, the endpoint may elect + * one of the following three alternatives. + * ... + * 3) Send a new INIT chunk to the endpoint, adding a Cookie + * Preservative parameter requesting an extension to the lifetime of + * the State Cookie. When calculating the time extension, an + * implementation SHOULD use the RTT information measured based on the + * previous COOKIE ECHO / ERROR exchange, and should add no more + * than 1 second beyond the measured RTT, due to long State Cookie + * lifetimes making the endpoint more subject to a replay attack. + * + * Verification Tag: Not explicit, but safe to ignore. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + time_t stale; + sctp_cookie_preserve_param_t bht; + sctp_errhdr_t *err; + struct list_head *pos; + struct sctp_transport *t; + sctp_chunk_t *reply; + sctp_bind_addr_t *bp; + int attempts; + + attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1; + + if (attempts >= asoc->max_init_attempts) { + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_STALE_COOKIE)); + return SCTP_DISPOSITION_DELETE_TCB; + } + + err = (sctp_errhdr_t *)(chunk->skb->data); + + /* When calculating the time extension, an implementation + * SHOULD use the RTT information measured based on the + * previous COOKIE ECHO / ERROR exchange, and should add no + * more than 1 second beyond the measured RTT, due to long + * State Cookie lifetimes making the endpoint more subject to + * a replay attack. + * Measure of Staleness's unit is usec. (1/1000000 sec) + * Suggested Cookie Life-span Increment's unit is msec. + * (1/1000 sec) + * In general, if you use the suggested cookie life, the value + * found in the field of measure of staleness should be doubled + * to give ample time to retransmit the new cookie and thus + * yield a higher probability of success on the reattempt. + */ + stale = ntohl(*(suseconds_t *)((u8 *)err + sizeof(sctp_errhdr_t))); + stale = stale << 1 / 1000; + + bht.param_hdr.type = SCTP_PARAM_COOKIE_PRESERVATIVE; + bht.param_hdr.length = htons(sizeof(bht)); + bht.lifespan_increment = htonl(stale); + + /* Build that new INIT chunk. */ + bp = (sctp_bind_addr_t *) &asoc->base.bind_addr; + reply = sctp_make_init(asoc, bp, GFP_ATOMIC, sizeof(bht)); + if (!reply) + goto nomem; + + sctp_addto_chunk(reply, sizeof(bht), &bht); + + /* Cast away the const modifier, as we want to just + * rerun it through as a sideffect. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC, + SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR)); + + /* If we've sent any data bundled with COOKIE-ECHO we need to + * resend. + */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(t)); + } + + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_COOKIE_WAIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process an ABORT. + * + * Section: 9.1 + * After checking the Verification Tag, the receiving endpoint shall + * remove the association from its record, and shall report the + * termination to its upper layer. + * + * Verification Tag: 8.5.1 Exceptions in Verification Tag Rules + * B) Rules for packet carrying ABORT: + * + * - The endpoint shall always fill in the Verification Tag field of the + * outbound packet with the destination endpoint's tag value if it + * is known. + * + * - If the ABORT is sent in response to an OOTB packet, the endpoint + * MUST follow the procedure described in Section 8.4. + * + * - The receiver MUST accept the packet if the Verification Tag + * matches either its own tag, OR the tag of its peer. Otherwise, the + * receiver MUST silently discard the packet and take no further + * action. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + __u16 error = SCTP_ERROR_NO_ERROR; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + if (chunk && (ntohs(chunk->chunk_hdr->length) >= + (sizeof(struct sctp_chunkhdr) + + sizeof(struct sctp_errhdr)))) + error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + + /* ASSOC_FAILED will DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + + /* BUG? This does not look complete... */ + return SCTP_DISPOSITION_ABORT; +} + +/* + * Process an ABORT. (COOKIE-WAIT state) + * + * See sctp_sf_do_9_1_abort() above. + */ +sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + __u16 error = SCTP_ERROR_NO_ERROR; + + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + SCTP_INC_STATS(SctpAborteds); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + + if (chunk && (ntohs(chunk->chunk_hdr->length) >= + (sizeof(struct sctp_chunkhdr) + + sizeof(struct sctp_errhdr)))) + error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + + /* CMD_INIT_FAILED will DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(error)); + + return SCTP_DISPOSITION_ABORT; +} + +/* + * Process an ABORT. (COOKIE-ECHOED state) + * + * See sctp_sf_do_9_1_abort() above. + */ +sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* There is a single T1 timer, so we should be able to use + * common function with the COOKIE-WAIT state. + */ + return sctp_sf_cookie_wait_abort(ep, asoc, type, arg, commands); +} + +/* + * sctp_sf_do_9_2_shut + * + * Section: 9.2 + * Upon the reception of the SHUTDOWN, the peer endpoint shall + * - enter the SHUTDOWN-RECEIVED state, + * + * - stop accepting new data from its SCTP user + * + * - verify, by checking the Cumulative TSN Ack field of the chunk, + * that all its outstanding DATA chunks have been received by the + * SHUTDOWN sender. + * + * Once an endpoint as reached the SHUTDOWN-RECEIVED state it MUST NOT + * send a SHUTDOWN in response to a ULP request. And should discard + * subsequent SHUTDOWN chunks. + * + * If there are still outstanding DATA chunks left, the SHUTDOWN + * receiver shall continue to follow normal data transmission + * procedures defined in Section 6 until all outstanding DATA chunks + * are acknowledged; however, the SHUTDOWN receiver MUST NOT accept + * new data from its SCTP user. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_shutdownhdr_t *sdh; + sctp_disposition_t disposition; + + /* Convert the elaborate header. */ + sdh = (sctp_shutdownhdr_t *)chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_shutdownhdr_t)); + chunk->subh.shutdown_hdr = sdh; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Upon the reception of the SHUTDOWN, the peer endpoint shall + * - enter the SHUTDOWN-RECEIVED state, + * - stop accepting new data from its SCTP user + * + * [This is implicit in the new state.] + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_RECEIVED)); + disposition = SCTP_DISPOSITION_CONSUME; + + if (sctp_outq_is_empty(&asoc->outqueue)) { + disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type, + arg, commands); + } + + /* - verify, by checking the Cumulative TSN Ack field of the + * chunk, that all its outstanding DATA chunks have been + * received by the SHUTDOWN sender. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_CTSN, + SCTP_U32(chunk->subh.shutdown_hdr->cum_tsn_ack)); + return disposition; +} + +/* RFC 2960 9.2 + * If an endpoint is in SHUTDOWN-ACK-SENT state and receives an INIT chunk + * (e.g., if the SHUTDOWN COMPLETE was lost) with source and destination + * transport addresses (either in the IP addresses or in the INIT chunk) + * that belong to this association, it should discard the INIT chunk and + * retransmit the SHUTDOWN ACK chunk. + */ +sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = (sctp_chunk_t *) arg; + sctp_chunk_t *reply; + + reply = sctp_make_shutdown_ack(asoc, chunk); + if (NULL == reply) + goto nomem; + + /* Set the transport for the SHUTDOWN ACK chunk and the timeout for + * the T2-SHUTDOWN timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* and restart the T2-shutdown timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * sctp_sf_do_ecn_cwr + * + * Section: Appendix A: Explicit Congestion Notification + * + * CWR: + * + * RFC 2481 details a specific bit for a sender to send in the header of + * its next outbound TCP segment to indicate to its peer that it has + * reduced its congestion window. This is termed the CWR bit. For + * SCTP the same indication is made by including the CWR chunk. + * This chunk contains one data element, i.e. the TSN number that + * was sent in the ECNE chunk. This element represents the lowest + * TSN number in the datagram that was originally marked with the + * CE bit. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_cwrhdr_t *cwr; + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + cwr = (sctp_cwrhdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_cwrhdr_t)); + + cwr->lowest_tsn = ntohl(cwr->lowest_tsn); + + /* Does this CWR ack the last sent congestion notification? */ + if (TSN_lte(asoc->last_ecne_tsn, cwr->lowest_tsn)) { + /* Stop sending ECNE. */ + sctp_add_cmd_sf(commands, + SCTP_CMD_ECN_CWR, + SCTP_U32(cwr->lowest_tsn)); + } + return SCTP_DISPOSITION_CONSUME; +} + +/* + * sctp_sf_do_ecne + * + * Section: Appendix A: Explicit Congestion Notification + * + * ECN-Echo + * + * RFC 2481 details a specific bit for a receiver to send back in its + * TCP acknowledgements to notify the sender of the Congestion + * Experienced (CE) bit having arrived from the network. For SCTP this + * same indication is made by including the ECNE chunk. This chunk + * contains one data element, i.e. the lowest TSN associated with the IP + * datagram marked with the CE bit..... + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_ecnehdr_t *ecne; + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + ecne = (sctp_ecnehdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t)); + + /* If this is a newer ECNE than the last CWR packet we sent out */ + sctp_add_cmd_sf(commands, SCTP_CMD_ECN_ECNE, + SCTP_U32(ntohl(ecne->lowest_tsn))); + + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Section: 6.2 Acknowledgement on Reception of DATA Chunks + * + * The SCTP endpoint MUST always acknowledge the reception of each valid + * DATA chunk. + * + * The guidelines on delayed acknowledgement algorithm specified in + * Section 4.2 of [RFC2581] SHOULD be followed. Specifically, an + * acknowledgement SHOULD be generated for at least every second packet + * (not every second DATA chunk) received, and SHOULD be generated within + * 200 ms of the arrival of any unacknowledged DATA chunk. In some + * situations it may be beneficial for an SCTP transmitter to be more + * conservative than the algorithms detailed in this document allow. + * However, an SCTP transmitter MUST NOT be more aggressive than the + * following algorithms allow. + * + * A SCTP receiver MUST NOT generate more than one SACK for every + * incoming packet, other than to update the offered window as the + * receiving application consumes new data. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_datahdr_t *data_hdr; + sctp_chunk_t *err; + size_t datalen; + sctp_verb_t deliver; + int tmp; + __u32 tsn; + + /* RFC 2960 8.5 Verification Tag + * + * When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, + SCTP_NULL()); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } + + data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); + + tsn = ntohl(data_hdr->tsn); + + SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn); + SCTP_DEBUG_PRINTK("eat_data: skb->head %p.\n", chunk->skb->head); + + /* ASSERT: Now skb->data is really the user data. */ + + /* Process ECN based congestion. + * + * Since the chunk structure is reused for all chunks within + * a packet, we use ecn_ce_done to track if we've already + * done CE processing for this packet. + * + * We need to do ECN processing even if we plan to discard the + * chunk later. + */ + + if (!chunk->ecn_ce_done) { + chunk->ecn_ce_done = 1; + if (INET_ECN_is_ce(chunk->skb->nh.iph->tos) && + asoc->peer.ecn_capable) { + /* Do real work as sideffect. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE, + SCTP_U32(tsn)); + } + } + + tmp = sctp_tsnmap_check(&asoc->peer.tsn_map, tsn); + if (tmp < 0) { + /* The TSN is too high--silently discard the chunk and + * count on it getting retransmitted later. + */ + goto discard_noforce; + } else if (tmp > 0) { + /* This is a duplicate. Record it. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_DUP, SCTP_U32(tsn)); + goto discard_force; + } + + /* This is a new TSN. */ + + /* Discard if there is no room in the receive window. + * Actually, allow a little bit of overflow (up to a MTU). + */ + datalen = ntohs(chunk->chunk_hdr->length); + datalen -= sizeof(sctp_data_chunk_t); + + deliver = SCTP_CMD_CHUNK_ULP; + + /* Think about partial delivery. */ + if ((datalen >= asoc->rwnd) && (!asoc->ulpq.pd_mode)) { + + /* Even if we don't accept this chunk there is + * memory pressure. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_PART_DELIVER, SCTP_NULL()); + } + + /* Spill over rwnd a little bit. Note: While allowed, this spill over + * seems a bit troublesome in that frag_point varies based on + * PMTU. In cases, such as loopback, this might be a rather + * large spill over. + */ + if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) { + + /* If this is the next TSN, consider reneging to make + * room. Note: Playing nice with a confused sender. A + * malicious sender can still eat up all our buffer + * space and in the future we may want to detect and + * do more drastic reneging. + */ + if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) && + (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) { + SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn); + deliver = SCTP_CMD_RENEGE; + } else { + SCTP_DEBUG_PRINTK("Discard tsn: %u len: %Zd, " + "rwnd: %d\n", tsn, datalen, + asoc->rwnd); + goto discard_force; + } + } + + /* + * Section 3.3.10.9 No User Data (9) + * + * Cause of error + * --------------- + * No User Data: This error cause is returned to the originator of a + * DATA chunk if a received DATA chunk has no user data. + */ + if (unlikely(0 == datalen)) { + err = sctp_make_abort_no_data(asoc, chunk, tsn); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_DATA)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_CONSUME; + } + + /* If definately accepting the DATA chunk, record its TSN, otherwise + * wait for renege processing. + */ + if (SCTP_CMD_CHUNK_ULP == deliver) + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); + + /* Note: Some chunks may get overcounted (if we drop) or overcounted + * if we renege and the chunk arrives again. + */ + if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) + SCTP_INC_STATS(SctpInUnorderChunks); + else + SCTP_INC_STATS(SctpInOrderChunks); + + /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number + * + * If an endpoint receive a DATA chunk with an invalid stream + * identifier, it shall acknowledge the reception of the DATA chunk + * following the normal procedure, immediately send an ERROR chunk + * with cause set to "Invalid Stream Identifier" (See Section 3.3.10) + * and discard the DATA chunk. + */ + if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) { + err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM, + &data_hdr->stream, + sizeof(data_hdr->stream)); + if (err) + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + goto discard_noforce; + } + + /* Send the data up to the user. Note: Schedule the + * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK + * chunk needs the updated rwnd. + */ + sctp_add_cmd_sf(commands, deliver, SCTP_CHUNK(chunk)); + + if (asoc->autoclose) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + } + + /* If this is the last chunk in a packet, we need to count it + * toward sack generation. Note that we need to SACK every + * OTHER packet containing data chunks, EVEN IF WE DISCARD + * THEM. We elect to NOT generate SACK's if the chunk fails + * the verification tag test. + * + * RFC 2960 6.2 Acknowledgement on Reception of DATA Chunks + * + * The SCTP endpoint MUST always acknowledge the reception of + * each valid DATA chunk. + * + * The guidelines on delayed acknowledgement algorithm + * specified in Section 4.2 of [RFC2581] SHOULD be followed. + * Specifically, an acknowledgement SHOULD be generated for at + * least every second packet (not every second DATA chunk) + * received, and SHOULD be generated within 200 ms of the + * arrival of any unacknowledged DATA chunk. In some + * situations it may be beneficial for an SCTP transmitter to + * be more conservative than the algorithms detailed in this + * document allow. However, an SCTP transmitter MUST NOT be + * more aggressive than the following algorithms allow. + */ + if (chunk->end_of_packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE()); + + /* Start the SACK timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + } + + return SCTP_DISPOSITION_CONSUME; + +discard_force: + /* RFC 2960 6.2 Acknowledgement on Reception of DATA Chunks + * + * When a packet arrives with duplicate DATA chunk(s) and with + * no new DATA chunk(s), the endpoint MUST immediately send a + * SACK with no delay. If a packet arrives with duplicate + * DATA chunk(s) bundled with new DATA chunks, the endpoint + * MAY immediately send a SACK. Normally receipt of duplicate + * DATA chunks will occur when the original SACK chunk was lost + * and the peer's RTO has expired. The duplicate TSN number(s) + * SHOULD be reported in the SACK as duplicate. + */ + /* In our case, we split the MAY SACK advice up whether or not + * the last chunk is a duplicate.' + */ + if (chunk->end_of_packet) + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); + return SCTP_DISPOSITION_DISCARD; + +discard_noforce: + if (chunk->end_of_packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE()); + + /* Start the SACK timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + } + return SCTP_DISPOSITION_DISCARD; +} + +/* + * sctp_sf_eat_data_fast_4_4 + * + * Section: 4 (4) + * (4) In SHUTDOWN-SENT state the endpoint MUST acknowledge any received + * DATA chunks without delay. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_datahdr_t *data_hdr; + sctp_chunk_t *err; + size_t datalen; + int tmp; + __u32 tsn; + + /* RFC 2960 8.5 Verification Tag + * + * When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, + SCTP_NULL()); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } + + data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); + + tsn = ntohl(data_hdr->tsn); + + SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn); + + /* ASSERT: Now skb->data is really the user data. */ + + /* Process ECN based congestion. + * + * Since the chunk structure is reused for all chunks within + * a packet, we use ecn_ce_done to track if we've already + * done CE processing for this packet. + * + * We need to do ECN processing even if we plan to discard the + * chunk later. + */ + if (!chunk->ecn_ce_done) { + chunk->ecn_ce_done = 1; + if (INET_ECN_is_ce(chunk->skb->nh.iph->tos) && + asoc->peer.ecn_capable) { + /* Do real work as sideffect. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE, + SCTP_U32(tsn)); + } + } + + tmp = sctp_tsnmap_check(&asoc->peer.tsn_map, tsn); + if (tmp < 0) { + /* The TSN is too high--silently discard the chunk and + * count on it getting retransmitted later. + */ + goto gen_shutdown; + } else if (tmp > 0) { + /* This is a duplicate. Record it. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_DUP, SCTP_U32(tsn)); + goto gen_shutdown; + } + + /* This is a new TSN. */ + + datalen = ntohs(chunk->chunk_hdr->length); + datalen -= sizeof(sctp_data_chunk_t); + + /* + * Section 3.3.10.9 No User Data (9) + * + * Cause of error + * --------------- + * No User Data: This error cause is returned to the originator of a + * DATA chunk if a received DATA chunk has no user data. + */ + if (unlikely(0 == datalen)) { + err = sctp_make_abort_no_data(asoc, chunk, tsn); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_DATA)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_CONSUME; + } + + /* We are accepting this DATA chunk. */ + + /* Record the fact that we have received this TSN. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); + + if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) + SCTP_INC_STATS(SctpInUnorderChunks); + else + SCTP_INC_STATS(SctpInOrderChunks); + + /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number + * + * If an endpoint receive a DATA chunk with an invalid stream + * identifier, it shall acknowledge the reception of the DATA chunk + * following the normal procedure, immediately send an ERROR chunk + * with cause set to "Invalid Stream Identifier" (See Section 3.3.10) + * and discard the DATA chunk. + */ + if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) { + err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM, + &data_hdr->stream, + sizeof(data_hdr->stream)); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + } + + /* Go a head and force a SACK, since we are shutting down. */ +gen_shutdown: + /* Implementor's Guide. + * + * While in SHUTDOWN-SENT state, the SHUTDOWN sender MUST immediately + * respond to each received packet containing one or more DATA chunk(s) + * with a SACK, a SHUTDOWN chunk, and restart the T2-shutdown timer + */ + if (chunk->end_of_packet) { + /* We must delay the chunk creation since the cumulative + * TSN has not been updated yet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SHUTDOWN, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + } + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Section: 6.2 Processing a Received SACK + * D) Any time a SACK arrives, the endpoint performs the following: + * + * i) If Cumulative TSN Ack is less than the Cumulative TSN Ack Point, + * then drop the SACK. Since Cumulative TSN Ack is monotonically + * increasing, a SACK whose Cumulative TSN Ack is less than the + * Cumulative TSN Ack Point indicates an out-of-order SACK. + * + * ii) Set rwnd equal to the newly received a_rwnd minus the number + * of bytes still outstanding after processing the Cumulative TSN Ack + * and the Gap Ack Blocks. + * + * iii) If the SACK is missing a TSN that was previously + * acknowledged via a Gap Ack Block (e.g., the data receiver + * reneged on the data), then mark the corresponding DATA chunk + * as available for retransmit: Mark it as missing for fast + * retransmit as described in Section 7.2.4 and if no retransmit + * timer is running for the destination address to which the DATA + * chunk was originally transmitted, then T3-rtx is started for + * that destination address. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_sackhdr_t *sackh; + __u32 ctsn; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Pull the SACK chunk from the data buffer */ + sackh = sctp_sm_pull_sack(chunk); + chunk->subh.sack_hdr = sackh; + ctsn = ntohl(sackh->cum_tsn_ack); + + /* i) If Cumulative TSN Ack is less than the Cumulative TSN + * Ack Point, then drop the SACK. Since Cumulative TSN + * Ack is monotonically increasing, a SACK whose + * Cumulative TSN Ack is less than the Cumulative TSN Ack + * Point indicates an out-of-order SACK. + */ + if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { + SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); + SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; + } + + /* Return this SACK for further processing. */ + sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, SCTP_SACKH(sackh)); + + /* Note: We do the rest of the work on the PROCESS_SACK + * sideeffect. + */ + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Generate an ABORT in response to a packet. + * + * Section: 8.4 Handle "Out of the blue" Packets + * + * 8) The receiver should respond to the sender of the OOTB packet + * with an ABORT. When sending the ABORT, the receiver of the + * OOTB packet MUST fill in the Verification Tag field of the + * outbound packet with the value found in the Verification Tag + * field of the OOTB packet and set the T-bit in the Chunk Flags + * to indicate that no TCB was found. After sending this ABORT, + * the receiver of the OOTB packet shall discard the OOTB packet + * and take no further action. + * + * Verification Tag: + * + * The return value is the disposition of the chunk. +*/ +sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_packet *packet = NULL; + sctp_chunk_t *chunk = arg; + sctp_chunk_t *abort; + + packet = sctp_ootb_pkt_new(asoc, chunk); + + if (packet) { + /* Make an ABORT. The T bit will be set if the asoc + * is NULL. + */ + abort = sctp_make_abort(asoc, chunk, 0); + if (!abort) { + sctp_ootb_pkt_free(packet); + return SCTP_DISPOSITION_NOMEM; + } + + /* Set the skb to the belonging sock for accounting. */ + abort->skb->sk = ep->base.sk; + + sctp_packet_append_chunk(packet, abort); + + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + + SCTP_INC_STATS(SctpOutCtrlChunks); + + return SCTP_DISPOSITION_CONSUME; + } + + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Received an ERROR chunk from peer. Generate SCTP_REMOTE_ERROR + * event as ULP notification for each cause included in the chunk. + * + * API 5.3.1.3 - SCTP_REMOTE_ERROR + * + * The return value is the disposition of the chunk. +*/ +sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + struct sctp_ulpevent *ev; + + while (chunk->chunk_end > chunk->skb->data) { + ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0, + GFP_ATOMIC); + if (!ev) + goto nomem; + + if (!sctp_add_cmd(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(ev))) { + sctp_ulpevent_free(ev); + goto nomem; + } + } + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process an inbound SHUTDOWN ACK. + * + * From Section 9.2: + * Upon the receipt of the SHUTDOWN ACK, the SHUTDOWN sender shall + * stop the T2-shutdown timer, send a SHUTDOWN COMPLETE chunk to its + * peer, and remove all record of the association. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *reply; + struct sctp_ulpevent *ev; + + /* 10.2 H) SHUTDOWN COMPLETE notification + * + * When SCTP completes the shutdown procedures (section 9.2) this + * notification is passed to the upper layer. + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP, + 0, 0, 0, GFP_ATOMIC); + if (!ev) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + /* Upon the receipt of the SHUTDOWN ACK, the SHUTDOWN sender shall + * stop the T2-shutdown timer, + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + /* ...send a SHUTDOWN COMPLETE chunk to its peer, */ + reply = sctp_make_shutdown_complete(asoc, chunk); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + SCTP_INC_STATS(SctpShutdowns); + SCTP_DEC_STATS(SctpCurrEstab); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + /* ...and remove all record of the association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * RFC 2960, 8.4 - Handle "Out of the blue" Packets + * 5) If the packet contains a SHUTDOWN ACK chunk, the receiver should + * respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE. + * When sending the SHUTDOWN COMPLETE, the receiver of the OOTB + * packet must fill in the Verification Tag field of the outbound + * packet with the Verification Tag received in the SHUTDOWN ACK and + * set the T-bit in the Chunk Flags to indicate that no TCB was + * found. Otherwise, + * + * 8) The receiver should respond to the sender of the OOTB packet with + * an ABORT. When sending the ABORT, the receiver of the OOTB packet + * MUST fill in the Verification Tag field of the outbound packet + * with the value found in the Verification Tag field of the OOTB + * packet and set the T-bit in the Chunk Flags to indicate that no + * TCB was found. After sending this ABORT, the receiver of the OOTB + * packet shall discard the OOTB packet and take no further action. + */ +sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + struct sk_buff *skb = chunk->skb; + sctp_chunkhdr_t *ch; + __u8 *ch_end; + int ootb_shut_ack = 0; + + SCTP_INC_STATS(SctpOutOfBlues); + + ch = (sctp_chunkhdr_t *) chunk->chunk_hdr; + do { + ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); + + if (SCTP_CID_SHUTDOWN_ACK == ch->type) + ootb_shut_ack = 1; + + ch = (sctp_chunkhdr_t *) ch_end; + } while (ch_end < skb->tail); + + if (ootb_shut_ack) + sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); + else + sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); +} + +/* + * Handle an "Out of the blue" SHUTDOWN ACK. + * + * Section: 8.4 5) + * 5) If the packet contains a SHUTDOWN ACK chunk, the receiver should + * respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE. + * When sending the SHUTDOWN COMPLETE, the receiver of the OOTB packet + * must fill in the Verification Tag field of the outbound packet with + * the Verification Tag received in the SHUTDOWN ACK and set the + * T-bit in the Chunk Flags to indicate that no TCB was found. + * + * Inputs + * (endpoint, asoc, type, arg, commands) + * + * Outputs + * (sctp_disposition_t) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_packet *packet = NULL; + sctp_chunk_t *chunk = arg; + sctp_chunk_t *shut; + + packet = sctp_ootb_pkt_new(asoc, chunk); + + if (packet) { + /* Make an SHUTDOWN_COMPLETE. + * The T bit will be set if the asoc is NULL. + */ + shut = sctp_make_shutdown_complete(asoc, chunk); + if (!shut) { + sctp_ootb_pkt_free(packet); + return SCTP_DISPOSITION_NOMEM; + } + + /* Set the skb to the belonging sock for accounting. */ + shut->skb->sk = ep->base.sk; + + sctp_packet_append_chunk(packet, shut); + + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + + SCTP_INC_STATS(SctpOutCtrlChunks); + + return SCTP_DISPOSITION_CONSUME; + } + + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Handle SHUTDOWN ACK in COOKIE_ECHOED or COOKIE_WAIT state. + * + * Verification Tag: 8.5.1 E) Rules for packet carrying a SHUTDOWN ACK + * If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state the + * procedures in section 8.4 SHOULD be followed, in other words it + * should be treated as an Out Of The Blue packet. + * [This means that we do NOT check the Verification Tag on these + * chunks. --piggy ] + * + */ +sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Although we do have an association in this case, it corresponds + * to a restarted association. So the packet is treated as an OOTB + * packet and the state function that handles OOTB SHUTDOWN_ACK is + * called with a NULL association. + */ + return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands); +} + +/* + * Process an unknown chunk. + * + * Section: 3.2. Also, 2.1 in the implementor's guide. + * + * Chunk Types are encoded such that the highest-order two bits specify + * the action that must be taken if the processing endpoint does not + * recognize the Chunk Type. + * + * 00 - Stop processing this SCTP packet and discard it, do not process + * any further chunks within it. + * + * 01 - Stop processing this SCTP packet and discard it, do not process + * any further chunks within it, and report the unrecognized + * chunk in an 'Unrecognized Chunk Type'. + * + * 10 - Skip this chunk and continue processing. + * + * 11 - Skip this chunk and continue processing, but report in an ERROR + * Chunk using the 'Unrecognized Chunk Type' cause of error. + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *unk_chunk = arg; + sctp_chunk_t *err_chunk; + sctp_chunkhdr_t *hdr; + + SCTP_DEBUG_PRINTK("Processing the unknown chunk id %d.\n", type.chunk); + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet. + */ + if (ntohl(unk_chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + switch (type.chunk & SCTP_CID_ACTION_MASK) { + case SCTP_CID_ACTION_DISCARD: + /* Discard the packet. */ + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + break; + case SCTP_CID_ACTION_DISCARD_ERR: + /* Discard the packet. */ + sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Generate an ERROR chunk as response. */ + hdr = unk_chunk->chunk_hdr; + err_chunk = sctp_make_op_error(asoc, unk_chunk, + SCTP_ERROR_UNKNOWN_CHUNK, hdr, + WORD_ROUND(ntohs(hdr->length))); + if (err_chunk) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err_chunk)); + } + return SCTP_DISPOSITION_CONSUME; + break; + case SCTP_CID_ACTION_SKIP: + /* Skip the chunk. */ + return SCTP_DISPOSITION_DISCARD; + break; + case SCTP_CID_ACTION_SKIP_ERR: + /* Generate an ERROR chunk as response. */ + hdr = unk_chunk->chunk_hdr; + err_chunk = sctp_make_op_error(asoc, unk_chunk, + SCTP_ERROR_UNKNOWN_CHUNK, hdr, + WORD_ROUND(ntohs(hdr->length))); + if (err_chunk) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err_chunk)); + } + /* Skip the chunk. */ + return SCTP_DISPOSITION_CONSUME; + break; + default: + break; + } + + return SCTP_DISPOSITION_DISCARD; +} + +/* + * Discard the chunk. + * + * Section: 0.2, 5.2.3, 5.2.5, 5.2.6, 6.0, 8.4.6, 8.5.1c, 9.2 + * [Too numerous to mention...] + * Verification Tag: No verification needed. + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk); + return SCTP_DISPOSITION_DISCARD; +} + +/* + * Discard the whole packet. + * + * Section: 8.4 2) + * + * 2) If the OOTB packet contains an ABORT chunk, the receiver MUST + * silently discard the OOTB packet and take no further action. + * Otherwise, + * + * Verification Tag: No verification necessary + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_pdiscard(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); + + return SCTP_DISPOSITION_CONSUME; +} + +#if 0 +/* + * We did something stupid but got lucky. Namely, we sent a HEARTBEAT + * before the association was all the way up and we did NOT get an + * ABORT. + * + * Log the fact and then process normally. + * + * Section: Not specified + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t lucky(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (chunk->sctp_hdr->vtag != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} +#endif /* 0 */ + +#if 0 +/* + * The other end is doing something very stupid. We'll ignore them + * after logging their idiocy. :-) + * + * Section: Not specified + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t other_stupid(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (chunk->sctp_hdr->vtag != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} +#endif /* 0 */ + +/* + * The other end is violating protocol. + * + * Section: Not specified + * Verification Tag: Not specified + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * We simply tag the chunk as a violation. The state machine will log + * the violation and continue. + */ +sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return SCTP_DISPOSITION_VIOLATION; +} + +/*************************************************************************** + * These are the state functions for handling primitive (Section 10) events. + ***************************************************************************/ +/* + * sctp_sf_do_prm_asoc + * + * Section: 10.1 ULP-to-SCTP + * B) Associate + * + * Format: ASSOCIATE(local SCTP instance name, destination transport addr, + * outbound stream count) + * -> association id [,destination transport addr list] [,outbound stream + * count] + * + * This primitive allows the upper layer to initiate an association to a + * specific peer endpoint. + * + * The peer endpoint shall be specified by one of the transport addresses + * which defines the endpoint (see Section 1.4). If the local SCTP + * instance has not been initialized, the ASSOCIATE is considered an + * error. + * [This is not relevant for the kernel implementation since we do all + * initialization at boot time. It we hadn't initialized we wouldn't + * get anywhere near this code.] + * + * An association id, which is a local handle to the SCTP association, + * will be returned on successful establishment of the association. If + * SCTP is not able to open an SCTP association with the peer endpoint, + * an error is returned. + * [In the kernel implementation, the struct sctp_association needs to + * be created BEFORE causing this primitive to run.] + * + * Other association parameters may be returned, including the + * complete destination transport addresses of the peer as well as the + * outbound stream count of the local endpoint. One of the transport + * address from the returned destination addresses will be selected by + * the local endpoint as default primary path for sending SCTP packets + * to this peer. The returned "destination transport addr list" can + * be used by the ULP to change the default primary path or to force + * sending a packet to a specific transport address. [All of this + * stuff happens when the INIT ACK arrives. This is a NON-BLOCKING + * function.] + * + * Mandatory attributes: + * + * o local SCTP instance name - obtained from the INITIALIZE operation. + * [This is the argument asoc.] + * o destination transport addr - specified as one of the transport + * addresses of the peer endpoint with which the association is to be + * established. + * [This is asoc->peer.active_path.] + * o outbound stream count - the number of outbound streams the ULP + * would like to open towards this peer endpoint. + * [BUG: This is not currently implemented.] + * Optional attributes: + * + * None. + * + * The return value is a disposition. + */ +sctp_disposition_t sctp_sf_do_prm_asoc(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *repl; + + /* The comment below says that we enter COOKIE-WAIT AFTER + * sending the INIT, but that doesn't actually work in our + * implementation... + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_COOKIE_WAIT)); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * A) "A" first sends an INIT chunk to "Z". In the INIT, "A" + * must provide its Verification Tag (Tag_A) in the Initiate + * Tag field. Tag_A SHOULD be a random number in the range of + * 1 to 4294967295 (see 5.3.1 for Tag value selection). ... + */ + + repl = sctp_make_init(asoc, &asoc->base.bind_addr, GFP_ATOMIC, 0); + if (!repl) + goto nomem; + + /* Cast away the const modifier, as we want to just + * rerun it through as a sideffect. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, + SCTP_ASOC((struct sctp_association *) asoc)); + + /* After sending the INIT, "A" starts the T1-init timer and + * enters the COOKIE-WAIT state. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process the SEND primitive. + * + * Section: 10.1 ULP-to-SCTP + * E) Send + * + * Format: SEND(association id, buffer address, byte count [,context] + * [,stream id] [,life time] [,destination transport address] + * [,unorder flag] [,no-bundle flag] [,payload protocol-id] ) + * -> result + * + * This is the main method to send user data via SCTP. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * o buffer address - the location where the user message to be + * transmitted is stored; + * + * o byte count - The size of the user data in number of bytes; + * + * Optional attributes: + * + * o context - an optional 32 bit integer that will be carried in the + * sending failure notification to the ULP if the transportation of + * this User Message fails. + * + * o stream id - to indicate which stream to send the data on. If not + * specified, stream 0 will be used. + * + * o life time - specifies the life time of the user data. The user data + * will not be sent by SCTP after the life time expires. This + * parameter can be used to avoid efforts to transmit stale + * user messages. SCTP notifies the ULP if the data cannot be + * initiated to transport (i.e. sent to the destination via SCTP's + * send primitive) within the life time variable. However, the + * user data will be transmitted if SCTP has attempted to transmit a + * chunk before the life time expired. + * + * o destination transport address - specified as one of the destination + * transport addresses of the peer endpoint to which this packet + * should be sent. Whenever possible, SCTP should use this destination + * transport address for sending the packets, instead of the current + * primary path. + * + * o unorder flag - this flag, if present, indicates that the user + * would like the data delivered in an unordered fashion to the peer + * (i.e., the U flag is set to 1 on all DATA chunks carrying this + * message). + * + * o no-bundle flag - instructs SCTP not to bundle this user data with + * other outbound DATA chunks. SCTP MAY still bundle even when + * this flag is present, when faced with network congestion. + * + * o payload protocol-id - A 32 bit unsigned integer that is to be + * passed to the peer indicating the type of payload protocol data + * being transmitted. This value is passed as opaque data by SCTP. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk)); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Process the SHUTDOWN primitive. + * + * Section: 10.1: + * C) Shutdown + * + * Format: SHUTDOWN(association id) + * -> result + * + * Gracefully closes an association. Any locally queued user data + * will be delivered to the peer. The association will be terminated only + * after the peer acknowledges all the SCTP packets sent. A success code + * will be returned on successful termination of the association. If + * attempting to terminate the association results in a failure, an error + * code shall be returned. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * Optional attributes: + * + * None. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + int disposition; + + /* From 9.2 Shutdown of an Association + * Upon receipt of the SHUTDOWN primitive from its upper + * layer, the endpoint enters SHUTDOWN-PENDING state and + * remains there until all outstanding data has been + * acknowledged by its peer. The endpoint accepts no new data + * from its upper layer, but retransmits data to the far end + * if necessary to fill gaps. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_PENDING)); + + /* sctpimpguide-05 Section 2.12.2 + * The sender of the SHUTDOWN MAY also start an overall guard timer + * 'T5-shutdown-guard' to bound the overall time for shutdown sequence. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + disposition = SCTP_DISPOSITION_CONSUME; + if (sctp_outq_is_empty(&asoc->outqueue)) { + disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type, + arg, commands); + } + return disposition; +} + +/* + * Process the ABORT primitive. + * + * Section: 10.1: + * C) Abort + * + * Format: Abort(association id [, cause code]) + * -> result + * + * Ungracefully closes an association. Any locally queued user data + * will be discarded and an ABORT chunk is sent to the peer. A success code + * will be returned on successful abortion of the association. If + * attempting to abort the association results in a failure, an error + * code shall be returned. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * Optional attributes: + * + * o cause code - reason of the abort to be passed to the peer + * + * None. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_1_prm_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* From 9.1 Abort of an Association + * Upon receipt of the ABORT primitive from its upper + * layer, the endpoint enters CLOSED state and + * discard all outstanding data has been + * acknowledged by its peer. The endpoint accepts no new data + * from its upper layer, but retransmits data to the far end + * if necessary to fill gaps. + */ + struct msghdr *msg = arg; + sctp_chunk_t *abort; + sctp_disposition_t retval; + + retval = SCTP_DISPOSITION_CONSUME; + + /* Generate ABORT chunk to send the peer. */ + abort = sctp_make_abort_user(asoc, NULL, msg); + if (!abort) + retval = SCTP_DISPOSITION_NOMEM; + else + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); + + /* Even if we can't send the ABORT due to low memory delete the + * TCB. This is a departure from our typical NOMEM handling. + */ + + /* Delete the established association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_USER_ABORT)); + + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + + return retval; +} + +/* We tried an illegal operation on an association which is closed. */ +sctp_disposition_t sctp_sf_error_closed(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-EINVAL)); + return SCTP_DISPOSITION_CONSUME; +} + +/* We tried an illegal operation on an association which is shutting + * down. + */ +sctp_disposition_t sctp_sf_error_shutdown(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, + SCTP_ERROR(-ESHUTDOWN)); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * sctp_cookie_wait_prm_shutdown + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explicitly address this issue, but is the route through the + * state table when someone issues a shutdown while in COOKIE_WAIT state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + SCTP_INC_STATS(SctpShutdowns); + + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; +} + +/* + * sctp_cookie_echoed_prm_shutdown + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explcitly address this issue, but is the route through the + * state table when someone issues a shutdown while in COOKIE_ECHOED state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, sctp_cmd_seq_t *commands) +{ + /* There is a single T1 timer, so we should be able to use + * common function with the COOKIE-WAIT state. + */ + return sctp_sf_cookie_wait_prm_shutdown(ep, asoc, type, arg, commands); +} + +/* + * sctp_sf_cookie_wait_prm_abort + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explicitly address this issue, but is the route through the + * state table when someone issues an abort while in COOKIE_WAIT state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_wait_prm_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct msghdr *msg = arg; + sctp_chunk_t *abort; + sctp_disposition_t retval; + + /* Stop T1-init timer */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + retval = SCTP_DISPOSITION_CONSUME; + + /* Generate ABORT chunk to send the peer */ + abort = sctp_make_abort_user(asoc, NULL, msg); + if (!abort) + retval = SCTP_DISPOSITION_NOMEM; + else + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + SCTP_INC_STATS(SctpAborteds); + + /* Even if we can't send the ABORT due to low memory delete the + * TCB. This is a departure from our typical NOMEM handling. + */ + + /* Delete the established association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_USER_ABORT)); + + return retval; +} + +/* + * sctp_sf_cookie_echoed_prm_abort + * + * Section: 4 Note: 3 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explcitly address this issue, but is the route through the + * state table when someone issues an abort while in COOKIE_ECHOED state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* There is a single T1 timer, so we should be able to use + * common function with the COOKIE-WAIT state. + */ + return sctp_sf_cookie_wait_prm_abort(ep, asoc, type, arg, commands); +} + +/* + * sctp_sf_shutdown_pending_prm_abort + * + * Inputs + * (endpoint, asoc) + * + * The RFC does not explicitly address this issue, but is the route through the + * state table when someone issues an abort while in SHUTDOWN-PENDING state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Stop the T5-shutdown guard timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands); +} + +/* + * sctp_sf_shutdown_sent_prm_abort + * + * Inputs + * (endpoint, asoc) + * + * The RFC does not explicitly address this issue, but is the route through the + * state table when someone issues an abort while in SHUTDOWN-SENT state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Stop the T2-shutdown timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + /* Stop the T5-shutdown guard timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + + return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands); +} + +/* + * sctp_sf_cookie_echoed_prm_abort + * + * Inputs + * (endpoint, asoc) + * + * The RFC does not explcitly address this issue, but is the route through the + * state table when someone issues an abort while in COOKIE_ECHOED state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* The same T2 timer, so we should be able to use + * common function with the SHUTDOWN-SENT state. + */ + return sctp_sf_shutdown_sent_prm_abort(ep, asoc, type, arg, commands); +} + +/* + * Process the REQUESTHEARTBEAT primitive + * + * 10.1 ULP-to-SCTP + * J) Request Heartbeat + * + * Format: REQUESTHEARTBEAT(association id, destination transport address) + * + * -> result + * + * Instructs the local endpoint to perform a HeartBeat on the specified + * destination transport address of the given association. The returned + * result should indicate whether the transmission of the HEARTBEAT + * chunk to the destination address is successful. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * o destination transport address - the transport address of the + * association on which a heartbeat should be issued. + */ +sctp_disposition_t sctp_sf_do_prm_requestheartbeat( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return sctp_sf_heartbeat(ep, asoc, type, (struct sctp_transport *)arg, + commands); +} + +/* + * Ignore the primitive event + * + * The return value is the disposition of the primitive. + */ +sctp_disposition_t sctp_sf_ignore_primitive( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("Primitive type %d is ignored.\n", type.primitive); + return SCTP_DISPOSITION_DISCARD; +} + +/*************************************************************************** + * These are the state functions for the OTHER events. + ***************************************************************************/ + +/* + * Start the shutdown negotiation. + * + * From Section 9.2: + * Once all its outstanding data has been acknowledged, the endpoint + * shall send a SHUTDOWN chunk to its peer including in the Cumulative + * TSN Ack field the last sequential TSN it has received from the peer. + * It shall then start the T2-shutdown timer and enter the SHUTDOWN-SENT + * state. If the timer expires, the endpoint must re-send the SHUTDOWN + * with the updated last sequential TSN received from its peer. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_start_shutdown( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *reply; + + /* Once all its outstanding data has been acknowledged, the + * endpoint shall send a SHUTDOWN chunk to its peer including + * in the Cumulative TSN Ack field the last sequential TSN it + * has received from the peer. + */ + reply = sctp_make_shutdown(asoc); + if (!reply) + goto nomem; + + /* Set the transport for the SHUTDOWN chunk and the timeout for the + * T2-shutdown timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* It shall then start the T2-shutdown timer */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + if (asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + + /* and enter the SHUTDOWN-SENT state. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_SENT)); + + /* sctp-implguide 2.10 Issues with Heartbeating and failover + * + * HEARTBEAT ... is discontinued after sending either SHUTDOWN + * or SHUTDOWN-ACK. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL()); + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Generate a SHUTDOWN ACK now that everything is SACK'd. + * + * From Section 9.2: + * + * If it has no more outstanding DATA chunks, the SHUTDOWN receiver + * shall send a SHUTDOWN ACK and start a T2-shutdown timer of its own, + * entering the SHUTDOWN-ACK-SENT state. If the timer expires, the + * endpoint must re-send the SHUTDOWN ACK. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = (sctp_chunk_t *) arg; + sctp_chunk_t *reply; + + /* If it has no more outstanding DATA chunks, the SHUTDOWN receiver + * shall send a SHUTDOWN ACK ... + */ + reply = sctp_make_shutdown_ack(asoc, chunk); + if (!reply) + goto nomem; + + /* Set the transport for the SHUTDOWN ACK chunk and the timeout for + * the T2-shutdown timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* and start/restart a T2-shutdown timer of its own, */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + if (asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + + /* Enter the SHUTDOWN-ACK-SENT state. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_ACK_SENT)); + + /* sctp-implguide 2.10 Issues with Heartbeating and failover + * + * HEARTBEAT ... is discontinued after sending either SHUTDOWN + * or SHUTDOWN-ACK. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL()); + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Ignore the event defined as other + * + * The return value is the disposition of the event. + */ +sctp_disposition_t sctp_sf_ignore_other(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("The event other type %d is ignored\n", type.other); + return SCTP_DISPOSITION_DISCARD; +} + +/************************************************************ + * These are the state functions for handling timeout events. + ************************************************************/ + +/* + * RTX Timeout + * + * Section: 6.3.3 Handle T3-rtx Expiration + * + * Whenever the retransmission timer T3-rtx expires for a destination + * address, do the following: + * [See below] + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_transport *transport = arg; + + if (asoc->overall_error_count >= asoc->overall_error_threshold) { + /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* E1) For the destination address for which the timer + * expires, adjust its ssthresh with rules defined in Section + * 7.2.3 and set the cwnd <- MTU. + */ + + /* E2) For the destination address for which the timer + * expires, set RTO <- RTO * 2 ("back off the timer"). The + * maximum value discussed in rule C7 above (RTO.max) may be + * used to provide an upper bound to this doubling operation. + */ + + /* E3) Determine how many of the earliest (i.e., lowest TSN) + * outstanding DATA chunks for the address for which the + * T3-rtx has expired will fit into a single packet, subject + * to the MTU constraint for the path corresponding to the + * destination transport address to which the retransmission + * is being sent (this may be different from the address for + * which the timer expires [see Section 6.4]). Call this + * value K. Bundle and retransmit those K DATA chunks in a + * single packet to the destination endpoint. + * + * Note: Any DATA chunks that were sent to the address for + * which the T3-rtx timer expired but did not fit in one MTU + * (rule E3 above), should be marked for retransmission and + * sent as soon as cwnd allows (normally when a SACK arrives). + */ + + /* NB: Rules E4 and F1 are implicit in R1. */ + sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(transport)); + + /* Do some failure management (Section 8.2). */ + sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Generate delayed SACK on timeout + * + * Section: 6.2 Acknowledgement on Reception of DATA Chunks + * + * The guidelines on delayed acknowledgement algorithm specified in + * Section 4.2 of [RFC2581] SHOULD be followed. Specifically, an + * acknowledgement SHOULD be generated for at least every second packet + * (not every second DATA chunk) received, and SHOULD be generated + * within 200 ms of the arrival of any unacknowledged DATA chunk. In + * some situations it may be beneficial for an SCTP transmitter to be + * more conservative than the algorithms detailed in this document + * allow. However, an SCTP transmitter MUST NOT be more aggressive than + * the following algorithms allow. + */ +sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * sctp_sf_t1_timer_expire + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * RFC 2960 Section 4 Notes + * 2) If the T1-init timer expires, the endpoint MUST retransmit INIT + * and re-start the T1-init timer without changing state. This MUST + * be repeated up to 'Max.Init.Retransmits' times. After that, the + * endpoint MUST abort the initialization process and report the + * error to SCTP user. + * + * 3) If the T1-cookie timer expires, the endpoint MUST retransmit + * COOKIE ECHO and re-start the T1-cookie timer without changing + * state. This MUST be repeated up to 'Max.Init.Retransmits' times. + * After that, the endpoint MUST abort the initialization process and + * report the error to SCTP user. + * + * Outputs + * (timers, events) + * + */ +sctp_disposition_t sctp_sf_t1_timer_expire(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *repl; + sctp_bind_addr_t *bp; + sctp_event_timeout_t timer = (sctp_event_timeout_t) arg; + int timeout; + int attempts; + + timeout = asoc->timeouts[timer]; + attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1; + repl = NULL; + + SCTP_DEBUG_PRINTK("Timer T1 expired.\n"); + + if ((timeout < asoc->max_init_timeo) && + (attempts < asoc->max_init_attempts)) { + switch (timer) { + case SCTP_EVENT_TIMEOUT_T1_INIT: + bp = (sctp_bind_addr_t *) &asoc->base.bind_addr; + repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0); + break; + + case SCTP_EVENT_TIMEOUT_T1_COOKIE: + repl = sctp_make_cookie_echo(asoc, NULL); + break; + + default: + BUG(); + break; + }; + + if (!repl) + goto nomem; + + /* Issue a sideeffect to do the needed accounting. */ + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_RESTART, + SCTP_TO(timer)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + } else { + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + return SCTP_DISPOSITION_DELETE_TCB; + } + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* RFC2960 9.2 If the timer expires, the endpoint must re-send the SHUTDOWN + * with the updated last sequential TSN received from its peer. + * + * An endpoint should limit the number of retransmissions of the + * SHUTDOWN chunk to the protocol parameter 'Association.Max.Retrans'. + * If this threshold is exceeded the endpoint should destroy the TCB and + * MUST report the peer endpoint unreachable to the upper layer (and + * thus the association enters the CLOSED state). The reception of any + * packet from its peer (i.e. as the peer sends all of its queued DATA + * chunks) should clear the endpoint's retransmission count and restart + * the T2-Shutdown timer, giving its peer ample opportunity to transmit + * all of its queued DATA chunks that have not yet been sent. + */ +sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *reply = NULL; + + SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); + if (asoc->overall_error_count >= asoc->overall_error_threshold) { + /* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_DELETE_TCB; + } + + switch (asoc->state) { + case SCTP_STATE_SHUTDOWN_SENT: + reply = sctp_make_shutdown(asoc); + break; + + case SCTP_STATE_SHUTDOWN_ACK_SENT: + reply = sctp_make_shutdown_ack(asoc, NULL); + break; + + default: + BUG(); + break; + }; + + if (!reply) + goto nomem; + + /* Do some failure management (Section 8.2). */ + sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, + SCTP_TRANSPORT(asoc->shutdown_last_sent_to)); + + /* Set the transport for the SHUTDOWN/ACK chunk and the timeout for + * the T2-shutdown timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* Restart the T2-shutdown timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* sctpimpguide-05 Section 2.12.2 + * The sender of the SHUTDOWN MAY also start an overall guard timer + * 'T5-shutdown-guard' to bound the overall time for shutdown sequence. + * At the expiration of this timer the sender SHOULD abort the association + * by sending an ABORT chunk. + */ +sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *reply = NULL; + + SCTP_DEBUG_PRINTK("Timer T5 expired.\n"); + + reply = sctp_make_abort(asoc, NULL, 0); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + + return SCTP_DISPOSITION_DELETE_TCB; +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Handle expiration of AUTOCLOSE timer. When the autoclose timer expires, + * the association is automatically closed by starting the shutdown process. + * The work that needs to be done is same as when SHUTDOWN is initiated by + * the user. So this routine looks same as sctp_sf_do_9_2_prm_shutdown(). + */ +sctp_disposition_t sctp_sf_autoclose_timer_expire( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + int disposition; + + /* From 9.2 Shutdown of an Association + * Upon receipt of the SHUTDOWN primitive from its upper + * layer, the endpoint enters SHUTDOWN-PENDING state and + * remains there until all outstanding data has been + * acknowledged by its peer. The endpoint accepts no new data + * from its upper layer, but retransmits data to the far end + * if necessary to fill gaps. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_PENDING)); + + /* sctpimpguide-05 Section 2.12.2 + * The sender of the SHUTDOWN MAY also start an overall guard timer + * 'T5-shutdown-guard' to bound the overall time for shutdown sequence. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); + disposition = SCTP_DISPOSITION_CONSUME; + if (sctp_outq_is_empty(&asoc->outqueue)) { + disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type, + arg, commands); + } + return disposition; +} + +/***************************************************************************** + * These are sa state functions which could apply to all types of events. + ****************************************************************************/ + +/* + * This table entry is not implemented. + * + * Inputs + * (endpoint, asoc, chunk) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_not_impl(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return SCTP_DISPOSITION_NOT_IMPL; +} + +/* + * This table entry represents a bug. + * + * Inputs + * (endpoint, asoc, chunk) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_bug(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return SCTP_DISPOSITION_BUG; +} + +/* + * This table entry represents the firing of a timer in the wrong state. + * Since timer deletion cannot be guaranteed a timer 'may' end up firing + * when the association is in the wrong state. This event should + * be ignored, so as to prevent any rearming of the timer. + * + * Inputs + * (endpoint, asoc, chunk) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_timer_ignore(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("Timer %d ignored.\n", type.chunk); + return SCTP_DISPOSITION_CONSUME; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* Pull the SACK chunk based on the SACK header. */ +sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk) +{ + sctp_sackhdr_t *sack; + __u16 num_blocks; + __u16 num_dup_tsns; + + /* FIXME: Protect ourselves from reading too far into + * the skb from a bogus sender. + */ + sack = (sctp_sackhdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_sackhdr_t)); + + num_blocks = ntohs(sack->num_gap_ack_blocks); + num_dup_tsns = ntohs(sack->num_dup_tsns); + + skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32)); + return sack; +} + +/* Create an ABORT packet to be sent as a response, with the specified + * error causes. + */ +struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + sctp_chunk_t *chunk, + const void *payload, + size_t paylen) +{ + struct sctp_packet *packet; + sctp_chunk_t *abort; + + packet = sctp_ootb_pkt_new(asoc, chunk); + + if (packet) { + /* Make an ABORT. + * The T bit will be set if the asoc is NULL. + */ + abort = sctp_make_abort(asoc, chunk, paylen); + if (!abort) { + sctp_ootb_pkt_free(packet); + return NULL; + } + /* Add specified error causes, i.e., payload, to the + * end of the chunk. + */ + sctp_addto_chunk(abort, paylen, payload); + + /* Set the skb to the belonging sock for accounting. */ + abort->skb->sk = ep->base.sk; + + sctp_packet_append_chunk(packet, abort); + + } + + return packet; +} + +/* Allocate a packet for responding in the OOTB conditions. */ +struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, + const sctp_chunk_t *chunk) +{ + struct sctp_packet *packet; + struct sctp_transport *transport; + __u16 sport; + __u16 dport; + __u32 vtag; + + /* Get the source and destination port from the inbound packet. */ + sport = ntohs(chunk->sctp_hdr->dest); + dport = ntohs(chunk->sctp_hdr->source); + + /* The V-tag is going to be the same as the inbound packet if no + * association exists, otherwise, use the peer's vtag. + */ + if (asoc) { + vtag = asoc->peer.i.init_tag; + } else { + /* Special case the INIT as there is no vtag yet. */ + if (SCTP_CID_INIT == chunk->chunk_hdr->type) { + sctp_init_chunk_t *init; + init = (sctp_init_chunk_t *)chunk->chunk_hdr; + vtag = ntohl(init->init_hdr.init_tag); + } else { + vtag = ntohl(chunk->sctp_hdr->vtag); + } + } + + /* Make a transport for the bucket, Eliza... */ + transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC); + + if (!transport) + goto nomem; + + /* Allocate a new packet for sending the response. */ + packet = t_new(struct sctp_packet, GFP_ATOMIC); + if (!packet) + goto nomem_packet; + + /* Cache a route for the transport with the chunk's destination as + * the source address. + */ + sctp_transport_route(transport, (union sctp_addr *)&chunk->dest, + sctp_sk(sctp_get_ctl_sock())); + + packet = sctp_packet_init(packet, transport, sport, dport); + packet = sctp_packet_config(packet, vtag, 0, NULL); + + return packet; + +nomem_packet: + sctp_transport_free(transport); +nomem: + return NULL; +} + +/* Free the packet allocated earlier for responding in the OOTB condition. */ +void sctp_ootb_pkt_free(struct sctp_packet *packet) +{ + sctp_transport_free(packet->transport); + sctp_packet_free(packet); +} + +/* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */ +void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + sctp_chunk_t *err_chunk) +{ + struct sctp_packet *packet; + + if (err_chunk) { + packet = sctp_ootb_pkt_new(asoc, chunk); + if (packet) { + /* Set the skb to the belonging sock for accounting. */ + err_chunk->skb->sk = ep->base.sk; + sctp_packet_append_chunk(packet, err_chunk); + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + SCTP_INC_STATS(SctpOutCtrlChunks); + } else + sctp_free_chunk (err_chunk); + } +} diff -urN linux-2.4.22-bk23/net/sctp/sm_statetable.c linux-2.4.22-bk24/net/sctp/sm_statetable.c --- linux-2.4.22-bk23/net/sctp/sm_statetable.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/sm_statetable.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,889 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * + * This file is part of the SCTP kernel reference Implementation + * + * These are the state tables for the SCTP state machine. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Hui Huang + * Daisy Chang + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include + +static sctp_sm_table_entry_t bug = { + .fn = sctp_sf_bug, + .name = "sctp_sf_bug" +}; + +#define DO_LOOKUP(_max, _type, _table) \ + if ((event_subtype._type > (_max))) { \ + printk(KERN_WARNING \ + "sctp table %p possible attack:" \ + " event %d exceeds max %d\n", \ + _table, event_subtype._type, _max); \ + return &bug; \ + } \ + return &_table[event_subtype._type][(int)state]; + +sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, + sctp_state_t state, + sctp_subtype_t event_subtype) +{ + switch (event_type) { + case SCTP_EVENT_T_CHUNK: + return sctp_chunk_event_lookup(event_subtype.chunk, state); + break; + case SCTP_EVENT_T_TIMEOUT: + DO_LOOKUP(SCTP_EVENT_TIMEOUT_MAX, timeout, + timeout_event_table); + break; + + case SCTP_EVENT_T_OTHER: + DO_LOOKUP(SCTP_EVENT_OTHER_MAX, other, other_event_table); + break; + + case SCTP_EVENT_T_PRIMITIVE: + DO_LOOKUP(SCTP_EVENT_PRIMITIVE_MAX, primitive, + primitive_event_table); + break; + + default: + /* Yikes! We got an illegal event type. */ + return &bug; + }; +} + +#define TYPE_SCTP_DATA { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_eat_data_6_2, .name = "sctp_sf_eat_data_6_2"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_eat_data_6_2, .name = "sctp_sf_eat_data_6_2"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_eat_data_fast_4_4, .name = "sctp_sf_eat_data_fast_4_4"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_DATA */ + +#define TYPE_SCTP_INIT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_do_5_1B_init, .name = "sctp_sf_do_5_1B_init"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_do_5_2_1_siminit, .name = "sctp_sf_do_5_2_1_siminit"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_5_2_1_siminit, .name = "sctp_sf_do_5_2_1_siminit"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_5_2_2_dupinit, .name = "sctp_sf_do_5_2_2_dupinit"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_do_9_2_reshutack, .name = "sctp_sf_do_9_2_reshutack"}, \ +} /* TYPE_SCTP_INIT */ + +#define TYPE_SCTP_INIT_ACK { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_do_5_1C_ack, .name = "sctp_sf_do_5_1C_ack"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_INIT_ACK */ + +#define TYPE_SCTP_SACK { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_eat_sack_6_2, .name = "sctp_sf_eat_sack_6_2"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_SACK */ + +#define TYPE_SCTP_HEARTBEAT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + /* This should not happen, but we are nice. */ \ + {.fn = sctp_sf_beat_8_3, .name = "sctp_sf_beat_8_3"}, \ +} /* TYPE_SCTP_HEARTBEAT */ + +#define TYPE_SCTP_HEARTBEAT_ACK { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_HEARTBEAT_ACK */ + +#define TYPE_SCTP_ABORT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_pdiscard, .name = "sctp_sf_pdiscard"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_cookie_wait_abort, .name = "sctp_sf_cookie_wait_abort"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_cookie_echoed_abort, \ + .name = "sctp_sf_cookie_echoed_abort"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_9_1_abort, .name = "sctp_sf_do_9_1_abort"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_shutdown_pending_abort, \ + .name = "sctp_sf_shutdown_pending_abort"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_shutdown_sent_abort, \ + .name = "sctp_sf_shutdown_sent_abort"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_9_1_abort, .name = "sctp_sf_do_9_1_abort"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_shutdown_ack_sent_abort, \ + .name = "sctp_sf_shutdown_ack_sent_abort"}, \ +} /* TYPE_SCTP_ABORT */ + +#define TYPE_SCTP_SHUTDOWN { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_9_2_shutdown, .name = "sctp_sf_do_9_2_shutdown"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_9_2_shutdown_ack, \ + .name = "sctp_sf_do_9_2_shutdown_ack"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_SHUTDOWN */ + +#define TYPE_SCTP_SHUTDOWN_ACK { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_8_5_1_E_sa, .name = "sctp_sf_do_8_5_1_E_sa"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_9_2_final, .name = "sctp_sf_do_9_2_final"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_do_9_2_final, .name = "sctp_sf_do_9_2_final"}, \ +} /* TYPE_SCTP_SHUTDOWN_ACK */ + +#define TYPE_SCTP_ERROR { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_cookie_echoed_err, .name = "sctp_sf_cookie_echoed_err"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_ERROR */ + +#define TYPE_SCTP_COOKIE_ECHO { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_do_5_1D_ce, .name = "sctp_sf_do_5_1D_ce"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_do_5_2_4_dupcook, .name = "sctp_sf_do_5_2_4_dupcook"}, \ +} /* TYPE_SCTP_COOKIE_ECHO */ + +#define TYPE_SCTP_COOKIE_ACK { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_5_1E_ca, .name = "sctp_sf_do_5_1E_ca"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ +} /* TYPE_SCTP_COOKIE_ACK */ + +#define TYPE_SCTP_ECN_ECNE { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_ecne, .name = "sctp_sf_do_ecne"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ +} /* TYPE_SCTP_ECN_ECNE */ + +#define TYPE_SCTP_ECN_CWR { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_ecn_cwr, .name = "sctp_sf_do_ecn_cwr"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_ecn_cwr, .name = "sctp_sf_do_ecn_cwr"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_ecn_cwr, .name = "sctp_sf_do_ecn_cwr"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ +} /* TYPE_SCTP_ECN_CWR */ + +#define TYPE_SCTP_SHUTDOWN_COMPLETE { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_do_4_C, .name = "sctp_sf_do_4_C"}, \ +} /* TYPE_SCTP_SHUTDOWN_COMPLETE */ + +/* The primary index for this table is the chunk type. + * The secondary index for this table is the state. + * + * For base protocol (RFC 2960). + */ +sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { + TYPE_SCTP_DATA, + TYPE_SCTP_INIT, + TYPE_SCTP_INIT_ACK, + TYPE_SCTP_SACK, + TYPE_SCTP_HEARTBEAT, + TYPE_SCTP_HEARTBEAT_ACK, + TYPE_SCTP_ABORT, + TYPE_SCTP_SHUTDOWN, + TYPE_SCTP_SHUTDOWN_ACK, + TYPE_SCTP_ERROR, + TYPE_SCTP_COOKIE_ECHO, + TYPE_SCTP_COOKIE_ACK, + TYPE_SCTP_ECN_ECNE, + TYPE_SCTP_ECN_CWR, + TYPE_SCTP_SHUTDOWN_COMPLETE, +}; /* state_fn_t chunk_event_table[][] */ + +static sctp_sm_table_entry_t +chunk_event_table_asconf[SCTP_STATE_NUM_STATES] = { + /* SCTP_STATE_EMPTY */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_CLOSED */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_COOKIE_WAIT */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_COOKIE_ECHOED */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_ESTABLISHED */ + {.fn = sctp_sf_discard_chunk, + .name = "sctp_sf_discard_chunk (will be sctp_addip_do_asconf)"}, + /* SCTP_STATE_SHUTDOWN_PENDING */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_SHUTDOWN_SENT */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_SHUTDOWN_RECEIVED */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, +}; /* chunk asconf */ + +static sctp_sm_table_entry_t +chunk_event_table_asconf_ack[SCTP_STATE_NUM_STATES] = { + /* SCTP_STATE_EMPTY */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_CLOSED */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_COOKIE_WAIT */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_COOKIE_ECHOED */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_ESTABLISHED */ + {.fn = sctp_sf_discard_chunk, + .name = "sctp_sf_discard_chunk (will be sctp_addip_do_asconf_ack)"}, + /* SCTP_STATE_SHUTDOWN_PENDING */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_SHUTDOWN_SENT */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_SHUTDOWN_RECEIVED */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ + {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, +}; /* chunk asconf_ack */ + +static sctp_sm_table_entry_t +chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { + /* SCTP_STATE_EMPTY */ + {.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, + /* SCTP_STATE_CLOSED */ + {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, + /* SCTP_STATE_COOKIE_WAIT */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, + /* SCTP_STATE_COOKIE_ECHOED */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, + /* SCTP_STATE_ESTABLISHED */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, + /* SCTP_STATE_SHUTDOWN_PENDING */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, + /* SCTP_STATE_SHUTDOWN_SENT */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, + /* SCTP_STATE_SHUTDOWN_RECEIVED */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ + {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, +}; /* chunk unknown */ + + +#define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_do_prm_asoc, .name = "sctp_sf_do_prm_asoc"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ +} /* TYPE_SCTP_PRIMITIVE_ASSOCIATE */ + +#define TYPE_SCTP_PRIMITIVE_SHUTDOWN { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_cookie_wait_prm_shutdown, \ + .name = "sctp_sf_cookie_wait_prm_shutdown"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_cookie_echoed_prm_shutdown, \ + .name = "sctp_sf_cookie_echoed_prm_shutdown"},\ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_9_2_prm_shutdown, \ + .name = "sctp_sf_do_9_2_prm_shutdown"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_ignore_primitive, .name = "sctp_sf_ignore_primitive"}, \ +} /* TYPE_SCTP_PRIMITIVE_SHUTDOWN */ + +#define TYPE_SCTP_PRIMITIVE_ABORT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_cookie_wait_prm_abort, \ + .name = "sctp_sf_cookie_wait_prm_abort"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_cookie_echoed_prm_abort, \ + .name = "sctp_sf_cookie_echoed_prm_abort"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_9_1_prm_abort, \ + .name = "sctp_sf_do_9_1_prm_abort"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_shutdown_pending_prm_abort, \ + .name = "sctp_sf_shutdown_pending_prm_abort"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_shutdown_sent_prm_abort, \ + .name = "sctp_sf_shutdown_sent_prm_abort"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_9_1_prm_abort, \ + .name = "sctp_sf_do_9_1_prm_abort"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_shutdown_ack_sent_prm_abort, \ + .name = "sctp_sf_shutdown_ack_sent_prm_abort"}, \ +} /* TYPE_SCTP_PRIMITIVE_ABORT */ + +#define TYPE_SCTP_PRIMITIVE_SEND { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_error_closed, .name = "sctp_sf_error_closed"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_do_prm_send, .name = "sctp_sf_do_prm_send"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_prm_send, .name = "sctp_sf_do_prm_send"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_prm_send, .name = "sctp_sf_do_prm_send"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \ +} /* TYPE_SCTP_PRIMITIVE_SEND */ + +#define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_do_prm_requestheartbeat, \ + .name = "sctp_sf_do_prm_requestheartbeat"}, \ +} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */ + + +/* The primary index for this table is the primitive type. + * The secondary index for this table is the state. + */ +sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = { + TYPE_SCTP_PRIMITIVE_ASSOCIATE, + TYPE_SCTP_PRIMITIVE_SHUTDOWN, + TYPE_SCTP_PRIMITIVE_ABORT, + TYPE_SCTP_PRIMITIVE_SEND, + TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT, +}; + +#define TYPE_SCTP_OTHER_NO_PENDING_TSN { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_9_2_start_shutdown, \ + .name = "sctp_do_9_2_start_shutdown"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_9_2_shutdown_ack, \ + .name = "sctp_sf_do_9_2_shutdown_ack"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ +} + +sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = { + TYPE_SCTP_OTHER_NO_PENDING_TSN, +}; + +#define TYPE_SCTP_EVENT_TIMEOUT_NONE { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_t1_timer_expire, .name = "sctp_sf_t1_timer_expire"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_T1_INIT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_t1_timer_expire, .name = "sctp_sf_t1_timer_expire"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_t2_timer_expire, .name = "sctp_sf_t2_timer_expire"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_t2_timer_expire, .name = "sctp_sf_t2_timer_expire"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_T3_RTX { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_do_6_3_3_rtx, .name = "sctp_sf_do_6_3_3_rtx"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_t5_timer_expire, .name = "sctp_sf_t5_timer_expire"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_SACK { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_do_6_2_sack, .name = "sctp_sf_do_6_2_sack"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_do_6_2_sack, .name = "sctp_sf_do_6_2_sack"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_do_6_2_sack, .name = "sctp_sf_do_6_2_sack"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +#define TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_autoclose_timer_expire, \ + .name = "sctp_sf_autoclose_timer_expire"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ +} + +sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = { + TYPE_SCTP_EVENT_TIMEOUT_NONE, + TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE, + TYPE_SCTP_EVENT_TIMEOUT_T1_INIT, + TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN, + TYPE_SCTP_EVENT_TIMEOUT_T3_RTX, + TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD, + TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT, + TYPE_SCTP_EVENT_TIMEOUT_SACK, + TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE, +}; + +sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, sctp_state_t state) +{ + if (state > SCTP_STATE_MAX) + return &bug; + + if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) { + return &chunk_event_table[cid][state]; + } + + switch (cid) { + case SCTP_CID_ASCONF: + return &chunk_event_table_asconf[state]; + + case SCTP_CID_ASCONF_ACK: + return &chunk_event_table_asconf_ack[state]; + default: + return &chunk_event_table_unknown[state]; + } +} diff -urN linux-2.4.22-bk23/net/sctp/socket.c linux-2.4.22-bk24/net/sctp/socket.c --- linux-2.4.22-bk23/net/sctp/socket.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/socket.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,3639 @@ +/* Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * Copyright (c) 2001-2003 Intel Corp. + * Copyright (c) 2001-2002 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions interface with the sockets layer to implement the + * SCTP Extensions for the Sockets API. + * + * Note that the descriptions from the specification are USER level + * functions--this file is the functions which populate the struct proto + * for SCTP which is the BOTTOM of the sockets interface. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Narasimha Budihal + * Karl Knutson + * Jon Grimm + * Xingang Guo + * Daisy Chang + * Sridhar Samudrala + * Inaky Perez-Gonzalez + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include /* for sa_family_t */ +#include +#include + +/* WARNING: Please do not remove the SCTP_STATIC attribute to + * any of the functions below as they are used to export functions + * used by a project regression testsuite. + */ + +/* Forward declarations for internal helper functions. */ +static int sctp_writeable(struct sock *sk); +static inline int sctp_wspace(struct sctp_association *asoc); +static inline void sctp_set_owner_w(sctp_chunk_t *chunk); +static void sctp_wfree(struct sk_buff *skb); +static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, + int msg_len); +static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); +static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); +static int sctp_wait_for_accept(struct sock *sk, long timeo); +static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int); +static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); +static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); +static int sctp_do_bind(struct sock *, union sctp_addr *, int); +static int sctp_autobind(struct sock *sk); +static void sctp_sock_migrate(struct sock *, struct sock *, + struct sctp_association *, sctp_socket_type_t); +static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG; + +/* Look up the association by its id. If this is not a UDP-style + * socket, the ID field is always ignored. + */ +struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id) +{ + struct sctp_association *asoc = NULL; + + /* If this is not a UDP-style socket, assoc id should be + * ignored. + */ + if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) { + if (!list_empty(&sctp_sk(sk)->ep->asocs)) + asoc = list_entry(sctp_sk(sk)->ep->asocs.next, + struct sctp_association, asocs); + return asoc; + } + + /* First, verify that this is a kernel address. */ + if (sctp_is_valid_kaddr((unsigned long) id)) { + struct sctp_association *temp; + + /* Verify that this _is_ an sctp_association + * data structure and if so, that the socket matches. + */ + temp = (struct sctp_association *)id; + if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) && + (temp->base.sk == sk)) + asoc = temp; + } + + return asoc; +} + +/* API 3.1.2 bind() - UDP Style Syntax + * The syntax of bind() is, + * + * ret = bind(int sd, struct sockaddr *addr, int addrlen); + * + * sd - the socket descriptor returned by socket(). + * addr - the address structure (struct sockaddr_in or struct + * sockaddr_in6 [RFC 2553]), + * addrlen - the size of the address structure. + * + * The caller should use struct sockaddr_storage described in RFC 2553 + * to represent addr for portability reason. + */ +int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + int retval = 0; + + sctp_lock_sock(sk); + + SCTP_DEBUG_PRINTK("sctp_bind(sk: %p, uaddr: %p, addr_len: %d)\n", + sk, uaddr, addr_len); + + /* Disallow binding twice. */ + if (!sctp_sk(sk)->ep->base.bind_addr.port) + retval = sctp_do_bind(sk, (union sctp_addr *)uaddr, + addr_len); + else + retval = -EINVAL; + + sctp_release_sock(sk); + + return retval; +} + +static long sctp_get_port_local(struct sock *, union sctp_addr *); + +/* Verify this is a valid sockaddr. */ +static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt, + union sctp_addr *addr, int len) +{ + struct sctp_af *af; + + /* Check minimum size. */ + if (len < sizeof (struct sockaddr)) + return NULL; + + /* Does this PF support this AF? */ + if (!opt->pf->af_supported(addr->sa.sa_family)) + return NULL; + + /* If we get this far, af is valid. */ + af = sctp_get_af_specific(addr->sa.sa_family); + + if (len < af->sockaddr_len) + return NULL; + + return af; +} + +/* Bind a local address either to an endpoint or to an association. */ +SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) +{ + struct sctp_opt *sp = sctp_sk(sk); + struct sctp_endpoint *ep = sp->ep; + sctp_bind_addr_t *bp = &ep->base.bind_addr; + struct sctp_af *af; + unsigned short snum; + int ret = 0; + + SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d)\n", + sk, addr, len); + + /* Common sockaddr verification. */ + af = sctp_sockaddr_af(sp, addr, len); + if (!af) + return -EINVAL; + + /* PF specific bind() address verification. */ + if (!sp->pf->bind_verify(sp, addr)) + return -EADDRNOTAVAIL; + + snum= ntohs(addr->v4.sin_port); + + SCTP_DEBUG_PRINTK("sctp_do_bind: port: %d, new port: %d\n", + bp->port, snum); + + /* We must either be unbound, or bind to the same port. */ + if (bp->port && (snum != bp->port)) { + SCTP_DEBUG_PRINTK("sctp_do_bind:" + " New port %d does not match existing port " + "%d.\n", snum, bp->port); + return -EINVAL; + } + + if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) + return -EACCES; + + /* Make sure we are allowed to bind here. + * The function sctp_get_port_local() does duplicate address + * detection. + */ + if ((ret = sctp_get_port_local(sk, addr))) { + if (ret == (long) sk) { + /* This endpoint has a conflicting address. */ + return -EINVAL; + } else { + return -EADDRINUSE; + } + } + + /* Refresh ephemeral port. */ + if (!snum) + snum = (sk)->num; + + /* Add the address to the bind address list. */ + sctp_local_bh_disable(); + sctp_write_lock(&ep->base.addr_lock); + + /* Use GFP_ATOMIC since BHs are disabled. */ + addr->v4.sin_port = ntohs(addr->v4.sin_port); + ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC); + addr->v4.sin_port = htons(addr->v4.sin_port); + if (!ret && !bp->port) + bp->port = snum; + sctp_write_unlock(&ep->base.addr_lock); + sctp_local_bh_enable(); + + /* Copy back into socket for getsockname() use. */ + if (!ret) { + (sk)->sport = htons((sk)->num); + af->to_sk_saddr(addr, sk); + } + + return ret; +} + +/* API 8.1 sctp_bindx() + * + * The syntax of sctp_bindx() is, + * + * ret = sctp_bindx(int sd, + * struct sockaddr_storage *addrs, + * int addrcnt, + * int flags); + * + * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses. + * If the sd is an IPv6 socket, the addresses passed can either be IPv4 + * or IPv6 addresses. + * + * A single address may be specified as INADDR_ANY or IPV6_ADDR_ANY, see + * section 3.1.2 for this usage. + * + * addrs is a pointer to an array of one or more socket addresses. Each + * address is contained in a struct sockaddr_storage, so each address is + * fixed length. The caller specifies the number of addresses in the + * array with addrcnt. + * + * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns -1, + * and sets errno to the appropriate error code. [ Editor's note: need + * to fill in all error code? ] + * + * For SCTP, the port given in each socket address must be the same, or + * sctp_bindx() will fail, setting errno to EINVAL . + * + * The flags parameter is formed from the bitwise OR of zero or + * more of the following currently defined flags: + * + * SCTP_BINDX_ADD_ADDR + * SCTP_BINDX_REM_ADDR + * + * SCTP_BIND_ADD_ADDR directs SCTP to add the given addresses to the + * association, and SCTP_BIND_REM_ADDR directs SCTP to remove the given + * addresses from the association. The two flags are mutually exclusive; + * if both are given, sctp_bindx() will fail with EINVAL. A caller may not + * remove all addresses from an association; sctp_bindx() will reject such + * an attempt with EINVAL. + * + * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate + * additional addresses with an endpoint after calling bind(). Or use + * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening + * socket is associated with so that no new association accepted will be + * associated with those addresses. + * + * SCTP_BIND_ADD_ADDR is defined as 0, so that it becomes the default + * behavior for sctp_bindx() when no flags are given. + * + * Adding and removing addresses from a connected association is optional + * functionality. Implementations that do not support this functionality + * should return EOPNOTSUPP. + * + * NOTE: This could be integrated into sctp_setsockopt_bindx(), + * but keeping it this way makes it easier if sometime sys_bindx is + * added. + */ + +/* Unprotected by locks. Call only with socket lock sk->lock held! See + * sctp_bindx() for a lock-protected call. + */ + +static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs, + int addrcnt, int flags) +{ + int retval = 0; + + SCTP_DEBUG_PRINTK("__sctp_bindx(sk: %p, addrs: %p, addrcnt: %d, " + "flags: %s)\n", sk, addrs, addrcnt, + (BINDX_ADD_ADDR == flags) ? "ADD" : + ((BINDX_REM_ADDR == flags) ? "REM" : "BOGUS")); + + switch (flags) { + case BINDX_ADD_ADDR: + retval = sctp_bindx_add(sk, addrs, addrcnt); + break; + + case BINDX_REM_ADDR: + retval = sctp_bindx_rem(sk, addrs, addrcnt); + break; + + default: + retval = -EINVAL; + break; + }; + + return retval; +} + +/* BINDX with locks. + * + * NOTE: Currently unused at all ... + */ +int sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt, + int flags) +{ + int retval; + + sctp_lock_sock(sk); + retval = __sctp_bindx(sk, addrs, addrcnt, flags); + sctp_release_sock(sk); + + return retval; +} + +/* Add a list of addresses as bind addresses to local endpoint or + * association. + * + * Basically run through each address specified in the addrs/addrcnt + * array/length pair, determine if it is IPv6 or IPv4 and call + * sctp_do_bind() on it. + * + * If any of them fails, then the operation will be reversed and the + * ones that were added will be removed. + * + * Only __sctp_bindx() is supposed to call this function. + */ +int sctp_bindx_add(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) +{ + int cnt; + int retval = 0; + int addr_len; + + SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n", + sk, addrs, addrcnt); + + for (cnt = 0; cnt < addrcnt; cnt++) { + /* The list may contain either IPv4 or IPv6 address; + * determine the address length for walking thru the list. + */ + switch (((struct sockaddr *)&addrs[cnt])->sa_family) { + case AF_INET: + addr_len = sizeof(struct sockaddr_in); + break; + + case AF_INET6: + addr_len = sizeof(struct sockaddr_in6); + break; + + default: + retval = -EINVAL; + goto err_bindx_add; + }; + + retval = sctp_do_bind(sk, (union sctp_addr *)&addrs[cnt], + addr_len); + +err_bindx_add: + if (retval < 0) { + /* Failed. Cleanup the ones that has been added */ + if (cnt > 0) + sctp_bindx_rem(sk, addrs, cnt); + return retval; + } + } + + /* Notify the peer(s), assuming we have (an) association(s). + * FIXME: for UDP, we have a 1-1-many mapping amongst sk, ep and asoc, + * so we don't have to do much work on locating associations. + * + * However, when the separation of ep and asoc kicks in, especially + * for TCP style connection, it becomes n-1-n mapping. We will need + * to do more fine work. Until then, hold my peace. + * --xguo + * + * Really, I don't think that will be a problem. The bind() + * call on a socket will either know the endpoint + * (e.g. TCP-style listen()ing socket, or UDP-style socket), + * or exactly one association. The former case is EXACTLY + * what we have now. In the former case we know the + * association already. --piggy + * + * This code will be working on either a UDP style or a TCP style + * socket, or say either an endpoint or an association. The socket + * type verification code need to be added later before calling the + * ADDIP code. + * --daisy + */ + +#if CONFIG_IP_SCTP_ADDIP + /* Add these addresses to all associations on this endpoint. */ + if (retval >= 0) { + struct list_head *pos; + struct sctp_endpoint *ep; + struct sctp_association *asoc; + ep = sctp_sk(sk)->ep; + + list_for_each(pos, &ep->asocs) { + asoc = list_entry(pos, struct sctp_association, asocs); + + sctp_addip_addr_config(asoc, + SCTP_PARAM_ADD_IP, + addrs, addrcnt); + } + } +#endif + + return retval; +} + +/* Remove a list of addresses from bind addresses list. Do not remove the + * last address. + * + * Basically run through each address specified in the addrs/addrcnt + * array/length pair, determine if it is IPv6 or IPv4 and call + * sctp_del_bind() on it. + * + * If any of them fails, then the operation will be reversed and the + * ones that were removed will be added back. + * + * At least one address has to be left; if only one address is + * available, the operation will return -EBUSY. + * + * Only __sctp_bindx() is supposed to call this function. + */ +int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) +{ + struct sctp_opt *sp = sctp_sk(sk); + struct sctp_endpoint *ep = sp->ep; + int cnt; + sctp_bind_addr_t *bp = &ep->base.bind_addr; + int retval = 0; + union sctp_addr saveaddr; + + SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n", + sk, addrs, addrcnt); + + for (cnt = 0; cnt < addrcnt; cnt++) { + /* If there is only one bind address, there is nothing more + * to be removed (we need at least one address here). + */ + if (list_empty(&bp->address_list)) { + retval = -EBUSY; + goto err_bindx_rem; + } + + /* The list may contain either IPv4 or IPv6 address; + * determine the address length for walking thru the list. + */ + switch (((struct sockaddr *)&addrs[cnt])->sa_family) { + case AF_INET: + saveaddr = *((union sctp_addr *) + &addrs[cnt]); + saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port); + /* Verify the port. */ + if (saveaddr.v4.sin_port != bp->port) { + retval = -EINVAL; + goto err_bindx_rem; + } + break; + + case AF_INET6: + saveaddr = *((union sctp_addr *) + &addrs[cnt]); + saveaddr.v6.sin6_port = + ntohs(saveaddr.v6.sin6_port); + /* verify the port */ + if (saveaddr.v6.sin6_port != bp->port) { + retval = -EINVAL; + goto err_bindx_rem; + } + break; + + default: + retval = -EINVAL; + goto err_bindx_rem; + }; + + /* FIXME - There is probably a need to check if sk->saddr and + * sk->rcv_addr are currently set to one of the addresses to + * be removed. This is something which needs to be looked into + * when we are fixing the outstanding issues with multi-homing + * socket routing and failover schemes. Refer to comments in + * sctp_do_bind(). -daisy + */ + sctp_local_bh_disable(); + sctp_write_lock(&ep->base.addr_lock); + + retval = sctp_del_bind_addr(bp, &saveaddr); + + sctp_write_unlock(&ep->base.addr_lock); + sctp_local_bh_enable(); + +err_bindx_rem: + if (retval < 0) { + /* Failed. Add the ones that has been removed back */ + if (cnt > 0) + sctp_bindx_add(sk, addrs, cnt); + return retval; + } + } + + /* + * This code will be working on either a UDP style or a TCP style + * socket, * or say either an endpoint or an association. The socket + * type verification code need to be added later before calling the + * ADDIP code. + * --daisy + */ +#if CONFIG_IP_SCTP_ADDIP + /* Remove these addresses from all associations on this endpoint. */ + if (retval >= 0) { + struct list_head *pos; + struct sctp_endpoint *ep; + struct sctp_association *asoc; + + ep = sctp_sk(sk)->ep; + list_for_each(pos, &ep->asocs) { + asoc = list_entry(pos, struct sctp_association, asocs); + sctp_addip_addr_config(asoc, SCTP_PARAM_DEL_IP, + addrs, addrcnt); + } + } +#endif + return retval; +} + +/* Helper for tunneling sys_bindx() requests through sctp_setsockopt() + * + * Basically do nothing but copying the addresses from user to kernel + * land and invoking sctp_bindx on the sk. This is used for tunneling + * the sctp_bindx() [sys_bindx()] request through sctp_setsockopt() + * from userspace. + * + * Note I don't use move_addr_to_kernel(): the reason is we would be + * iterating over an array of struct sockaddr_storage passing always + * what we know is a good size (sizeof (struct sock...)), so it is + * pointless. Instead check the whole area for read access and copy + * it. + * + * We don't use copy_from_user() for optimization: we first do the + * sanity checks (buffer size -fast- and access check-healthy + * pointer); if all of those succeed, then we can alloc the memory + * (expensive operation) needed to copy the data to kernel. Then we do + * the copying without checking the user space area + * (__copy_from_user()). + * + * On exit there is no need to do sockfd_put(), sys_setsockopt() does + * it. + * + * sk The sk of the socket + * addrs The pointer to the addresses in user land + * addrssize Size of the addrs buffer + * op Operation to perform (add or remove, see the flags of + * sctp_bindx) + * + * Returns 0 if ok, <0 errno code on error. + */ +SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, + struct sockaddr_storage *addrs, + int addrssize, int op) +{ + struct sockaddr_storage *kaddrs; + int err; + size_t addrcnt; + + SCTP_DEBUG_PRINTK("sctp_do_setsocktopt_bindx: sk %p addrs %p" + " addrssize %d opt %d\n", sk, addrs, addrssize, op); + + /* Do we have an integer number of structs sockaddr_storage? */ + if (unlikely(addrssize <= 0 || + addrssize % sizeof(struct sockaddr_storage) != 0)) + return -EINVAL; + + /* Check the user passed a healthy pointer. */ + if (unlikely(!access_ok(VERIFY_READ, addrs, addrssize))) + return -EFAULT; + + /* Alloc space for the address array in kernel memory. */ + kaddrs = (struct sockaddr_storage *) kmalloc(addrssize, GFP_KERNEL); + if (unlikely(!kaddrs)) + return -ENOMEM; + + if (copy_from_user(kaddrs, addrs, addrssize)) { + kfree(kaddrs); + return -EFAULT; + } + + addrcnt = addrssize / sizeof(struct sockaddr_storage); + err = __sctp_bindx(sk, kaddrs, addrcnt, op); /* Do the work. */ + kfree(kaddrs); + + return err; +} + +/* API 3.1.4 close() - UDP Style Syntax + * Applications use close() to perform graceful shutdown (as described in + * Section 10.1 of [SCTP]) on ALL the associations currently represented + * by a UDP-style socket. + * + * The syntax is + * + * ret = close(int sd); + * + * sd - the socket descriptor of the associations to be closed. + * + * To gracefully shutdown a specific association represented by the + * UDP-style socket, an application should use the sendmsg() call, + * passing no user data, but including the appropriate flag in the + * ancillary data (see Section xxxx). + * + * If sd in the close() call is a branched-off socket representing only + * one association, the shutdown is performed on that association only. + */ +SCTP_STATIC void sctp_close(struct sock *sk, long timeout) +{ + struct sctp_endpoint *ep; + struct sctp_association *asoc; + struct list_head *pos, *temp; + + SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p...)\n", sk); + + sctp_lock_sock(sk); + sk->shutdown = SHUTDOWN_MASK; + + ep = sctp_sk(sk)->ep; + + /* Walk all associations on a socket, not on an endpoint. */ + list_for_each_safe(pos, temp, &ep->asocs) { + asoc = list_entry(pos, struct sctp_association, asocs); + + /* A closed association can still be in the list if it + * belongs to a TCP-style listening socket that is not + * yet accepted. + */ + if ((SCTP_SOCKET_TCP == sctp_sk(sk)->type) && + (SCTP_STATE_CLOSED == asoc->state)) { + sctp_unhash_established(asoc); + sctp_association_free(asoc); + } else + sctp_primitive_SHUTDOWN(asoc, NULL); + } + + /* Clean up any skbs sitting on the receive queue. */ + skb_queue_purge(&sk->receive_queue); + skb_queue_purge(&sctp_sk(sk)->pd_lobby); + + /* This will run the backlog queue. */ + sctp_release_sock(sk); + + /* Supposedly, no process has access to the socket, but + * the net layers still may. + */ + sctp_local_bh_disable(); + sctp_bh_lock_sock(sk); + + /* Hold the sock, since inet_sock_release() will put sock_put() + * and we have just a little more cleanup. + */ + sock_hold(sk); + inet_sock_release(sk); + + sctp_bh_unlock_sock(sk); + sctp_local_bh_enable(); + + sock_put(sk); + + SCTP_DBG_OBJCNT_DEC(sock); +} + +/* Handle EPIPE error. */ +static int sctp_error(struct sock *sk, int flags, int err) +{ + if (err == -EPIPE) + err = sock_error(sk) ? : -EPIPE; + if (err == -EPIPE && !(flags & MSG_NOSIGNAL)) + send_sig(SIGPIPE, current, 0); + return err; +} + +/* API 3.1.3 sendmsg() - UDP Style Syntax + * + * An application uses sendmsg() and recvmsg() calls to transmit data to + * and receive data from its peer. + * + * ssize_t sendmsg(int socket, const struct msghdr *message, + * int flags); + * + * socket - the socket descriptor of the endpoint. + * message - pointer to the msghdr structure which contains a single + * user message and possibly some ancillary data. + * + * See Section 5 for complete description of the data + * structures. + * + * flags - flags sent or received with the user message, see Section + * 5 for complete description of the flags. + * + * Note: This function could use a rewrite especially when explicit + * connect support comes in. + */ +/* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */ + +SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *); + +SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int msg_len) +{ + struct sctp_opt *sp; + struct sctp_endpoint *ep; + struct sctp_association *new_asoc=NULL, *asoc=NULL; + struct sctp_transport *transport; + sctp_chunk_t *chunk = NULL; + union sctp_addr to; + struct sockaddr *msg_name = NULL; + struct sctp_sndrcvinfo default_sinfo = { 0 }; + struct sctp_sndrcvinfo *sinfo; + struct sctp_initmsg *sinit; + sctp_assoc_t associd = NULL; + sctp_cmsgs_t cmsgs = { 0 }; + int err; + sctp_scope_t scope; + long timeo; + __u16 sinfo_flags = 0; + struct sk_buff_head chunks; + int msg_flags = msg->msg_flags; + + SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n", + sk, msg, msg_len); + + err = 0; + sp = sctp_sk(sk); + ep = sp->ep; + + SCTP_DEBUG_PRINTK("Using endpoint: %s.\n", ep->debug_name); + + if ((SCTP_SOCKET_TCP == sp->type) && + (SCTP_SS_ESTABLISHED != sk->state)) { + err = -EPIPE; + goto out_nounlock; + } + + /* Parse out the SCTP CMSGs. */ + err = sctp_msghdr_parse(msg, &cmsgs); + + if (err) { + SCTP_DEBUG_PRINTK("msghdr parse err = %x\n", err); + goto out_nounlock; + } + + /* Fetch the destination address for this packet. This + * address only selects the association--it is not necessarily + * the address we will send to. + * For a peeled-off socket, msg_name is ignored. + */ + if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) { + int msg_namelen = msg->msg_namelen; + + err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name, + msg_namelen); + if (err) + return err; + + if (msg_namelen > sizeof(to)) + msg_namelen = sizeof(to); + memcpy(&to, msg->msg_name, msg_namelen); + SCTP_DEBUG_PRINTK("Just memcpy'd. msg_name is " + "0x%x:%u.\n", + to.v4.sin_addr.s_addr, to.v4.sin_port); + + to.v4.sin_port = ntohs(to.v4.sin_port); + msg_name = msg->msg_name; + } + + sinfo = cmsgs.info; + sinit = cmsgs.init; + + /* Did the user specify SNDRCVINFO? */ + if (sinfo) { + sinfo_flags = sinfo->sinfo_flags; + associd = sinfo->sinfo_assoc_id; + } + + SCTP_DEBUG_PRINTK("msg_len: %Zd, sinfo_flags: 0x%x\n", + msg_len, sinfo_flags); + + /* If MSG_EOF is set, no data can be sent. Disallow sending zero + * length messages when MSG_EOF|MSG_ABORT is not set. + * If MSG_ABORT is set, the message length could be non zero with + * the msg_iov set to the user abort reason. + */ + if (((sinfo_flags & MSG_EOF) && (msg_len > 0)) || + (!(sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len == 0))) { + err = -EINVAL; + goto out_nounlock; + } + + sctp_lock_sock(sk); + + transport = NULL; + + SCTP_DEBUG_PRINTK("About to look up association.\n"); + + /* If a msg_name has been specified, assume this is to be used. */ + if (msg_name) { + /* Look for a matching association on the endpoint. */ + asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); + if (!asoc) { + /* If we could not find a matching association on the + * endpoint, make sure that there is no peeled-off + * association on another socket. + */ + if (sctp_endpoint_is_peeled_off(ep, &to)) { + err = -EADDRNOTAVAIL; + goto out_unlock; + } + } + } else { + asoc = sctp_id2assoc(sk, associd); + if (!asoc) { + err = -EINVAL; + goto out_unlock; + } + } + + if (asoc) { + SCTP_DEBUG_PRINTK("Just looked up association: " + "%s. \n", asoc->debug_name); + + /* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED + * socket that has an association in CLOSED state. This can + * happen when an accepted socket has an association that is + * already CLOSED. + */ + if ((SCTP_STATE_CLOSED == asoc->state) && + (SCTP_SOCKET_TCP == sp->type)) { + err = -EPIPE; + goto out_unlock; + } + + if (sinfo_flags & MSG_EOF) { + SCTP_DEBUG_PRINTK("Shutting down association: %p\n", + asoc); + sctp_primitive_SHUTDOWN(asoc, NULL); + err = 0; + goto out_unlock; + } + if (sinfo_flags & MSG_ABORT) { + SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); + sctp_primitive_ABORT(asoc, msg); + err = 0; + goto out_unlock; + } + } + + /* Do we need to create the association? */ + if (!asoc) { + SCTP_DEBUG_PRINTK("There is no association yet.\n"); + + /* Check for invalid stream against the stream counts, + * either the default or the user specified stream counts. + */ + if (sinfo) { + if (!sinit || (sinit && !sinit->sinit_num_ostreams)) { + /* Check against the defaults. */ + if (sinfo->sinfo_stream >= + sp->initmsg.sinit_num_ostreams) { + err = -EINVAL; + goto out_unlock; + } + } else { + /* Check against the requested. */ + if (sinfo->sinfo_stream >= + sinit->sinit_num_ostreams) { + err = -EINVAL; + goto out_unlock; + } + } + } + + /* + * API 3.1.2 bind() - UDP Style Syntax + * If a bind() or sctp_bindx() is not called prior to a + * sendmsg() call that initiates a new association, the + * system picks an ephemeral port and will choose an address + * set equivalent to binding with a wildcard address. + */ + if (!ep->base.bind_addr.port) { + if (sctp_autobind(sk)) { + err = -EAGAIN; + goto out_unlock; + } + } + + scope = sctp_scope(&to); + new_asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL); + if (!new_asoc) { + err = -ENOMEM; + goto out_unlock; + } + asoc = new_asoc; + + /* If the SCTP_INIT ancillary data is specified, set all + * the association init values accordingly. + */ + if (sinit) { + if (sinit->sinit_num_ostreams) { + asoc->c.sinit_num_ostreams = + sinit->sinit_num_ostreams; + } + if (sinit->sinit_max_instreams) { + asoc->c.sinit_max_instreams = + sinit->sinit_max_instreams; + } + if (sinit->sinit_max_attempts) { + asoc->max_init_attempts + = sinit->sinit_max_attempts; + } + if (sinit->sinit_max_init_timeo) { + asoc->max_init_timeo + = sinit->sinit_max_init_timeo * HZ; + } + } + + /* Prime the peer's transport structures. */ + transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL); + if (!transport) { + err = -ENOMEM; + goto out_free; + } + err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL); + if (err < 0) { + err = -ENOMEM; + goto out_free; + } + } + + /* ASSERT: we have a valid association at this point. */ + SCTP_DEBUG_PRINTK("We have a valid association.\n"); + + if (!sinfo) { + /* If the user didn't specify SNDRCVINFO, make up one with + * some defaults. + */ + default_sinfo.sinfo_stream = asoc->defaults.stream; + default_sinfo.sinfo_flags = asoc->defaults.flags; + default_sinfo.sinfo_ppid = asoc->defaults.ppid; + default_sinfo.sinfo_context = asoc->defaults.context; + default_sinfo.sinfo_timetolive = asoc->defaults.timetolive; + default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc); + sinfo = &default_sinfo; + } + + /* API 7.1.7, the sndbuf size per association bounds the + * maximum size of data that can be sent in a single send call. + */ + if (msg_len > sk->sndbuf) { + err = -EMSGSIZE; + goto out_free; + } + + /* If fragmentation is disabled and the message length exceeds the + * association fragmentation point, return EMSGSIZE. The I-D + * does not specify what this error is, but this looks like + * a great fit. + */ + if (sctp_sk(sk)->disable_fragments && (msg_len > asoc->frag_point)) { + err = -EMSGSIZE; + goto out_free; + } + + if (sinfo) { + /* Check for invalid stream. */ + if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) { + err = -EINVAL; + goto out_free; + } + } + + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + if (!sctp_wspace(asoc)) { + err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); + if (err) + goto out_free; + } + + /* Break the message into multiple chunks of maximum size. */ + skb_queue_head_init(&chunks); + err = sctp_datachunks_from_user(asoc, sinfo, msg, msg_len, &chunks); + if (err) + goto out_free; + + /* Auto-connect, if we aren't connected already. */ + if (SCTP_STATE_CLOSED == asoc->state) { + err = sctp_primitive_ASSOCIATE(asoc, NULL); + if (err < 0) + goto out_free; + SCTP_DEBUG_PRINTK("We associated primitively.\n"); + } + + /* Now send the (possibly) fragmented message. */ + while ((chunk = (sctp_chunk_t *)__skb_dequeue(&chunks))) { + + /* Do accounting for the write space. */ + sctp_set_owner_w(chunk); + + /* This flag, in the UDP model, requests the SCTP stack to + * override the primary destination address with the + * address found with the sendto/sendmsg call. + */ + if (sinfo_flags & MSG_ADDR_OVER) { + if (!msg->msg_name) { + err = -EINVAL; + goto out_free; + } + chunk->transport = sctp_assoc_lookup_paddr(asoc, &to); + if (!chunk->transport) { + err = -EINVAL; + goto out_free; + } + } + + /* Send it to the lower layers. */ + sctp_primitive_SEND(asoc, chunk); + SCTP_DEBUG_PRINTK("We sent primitively.\n"); + } + + if (!err) { + err = msg_len; + goto out_unlock; + } + /* If we are already past ASSOCIATE, the lower + * layers are responsible for association cleanup. + */ + goto out_free_chunk; + +out_free: + if (new_asoc) + sctp_association_free(asoc); + +out_free_chunk: + if (chunk) + sctp_free_chunk(chunk); + +out_unlock: + sctp_release_sock(sk); + +out_nounlock: + return sctp_error(sk, msg_flags, err); + +#if 0 +do_sock_err: + if (msg_len) + err = msg_len; + else + err = sock_error(sk); + goto out; + +do_interrupted: + if (msg_len) + err = msg_len; + goto out; +#endif /* 0 */ +} + +/* This is an extended version of skb_pull() that removes the data from the + * start of a skb even when data is spread across the list of skb's in the + * frag_list. len specifies the total amount of data that needs to be removed. + * when 'len' bytes could be removed from the skb, it returns 0. + * If 'len' exceeds the total skb length, it returns the no. of bytes that + * could not be removed. + */ +static int sctp_skb_pull(struct sk_buff *skb, int len) +{ + struct sk_buff *list; + int skb_len = skb_headlen(skb); + int rlen; + + if (len <= skb_len) { + __skb_pull(skb, len); + return 0; + } + len -= skb_len; + __skb_pull(skb, skb_len); + + for (list = skb_shinfo(skb)->frag_list; list; list = list->next) { + rlen = sctp_skb_pull(list, len); + skb->len -= (len-rlen); + skb->data_len -= (len-rlen); + + if (!rlen) + return 0; + + len = rlen; + } + + return len; +} + +/* API 3.1.3 recvmsg() - UDP Style Syntax + * + * ssize_t recvmsg(int socket, struct msghdr *message, + * int flags); + * + * socket - the socket descriptor of the endpoint. + * message - pointer to the msghdr structure which contains a single + * user message and possibly some ancillary data. + * + * See Section 5 for complete description of the data + * structures. + * + * flags - flags sent or received with the user message, see Section + * 5 for complete description of the flags. + */ +static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *); + +SCTP_STATIC int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len, + int noblock, int flags, int *addr_len) +{ + struct sctp_ulpevent *event = NULL; + struct sctp_opt *sp = sctp_sk(sk); + struct sk_buff *skb; + int copied; + int err = 0; + int skb_len; + + SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %d, %s: %d, %s: " + "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg, + "len", len, "knoblauch", noblock, + "flags", flags, "addr_len", addr_len); + + sctp_lock_sock(sk); + + if ((SCTP_SOCKET_TCP == sp->type) && + (SCTP_SS_ESTABLISHED != sk->state)) { + err = -ENOTCONN; + goto out; + } + + skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out; + + /* Get the total length of the skb including any skb's in the + * frag_list. + */ + skb_len = skb->len; + + copied = skb_len; + if (copied > len) + copied = len; + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + + event = sctp_skb2event(skb); + + if (err) + goto out_free; + + sock_recv_timestamp(msg, sk, skb); + if (sctp_ulpevent_is_notification(event)) { + msg->msg_flags |= MSG_NOTIFICATION; + sp->pf->event_msgname(event, msg->msg_name, addr_len); + } else { + sp->pf->skb_msgname(skb, msg->msg_name, addr_len); + } + + /* Check if we allow SCTP_SNDRCVINFO. */ + if (sp->subscribe.sctp_data_io_event) + sctp_ulpevent_read_sndrcvinfo(event, msg); +#if 0 + /* FIXME: we should be calling IP/IPv6 layers. */ + if (sk->protinfo.af_inet.cmsg_flags) + ip_cmsg_recv(msg, skb); +#endif + + err = copied; + + /* If skb's length exceeds the user's buffer, update the skb and + * push it back to the receive_queue so that the next call to + * recvmsg() will return the remaining data. Don't set MSG_EOR. + */ + if (skb_len > copied) { + msg->msg_flags &= ~MSG_EOR; + if (flags & MSG_PEEK) + goto out_free; + sctp_skb_pull(skb, copied); + skb_queue_head(&sk->receive_queue, skb); + + /* When only partial message is copied to the user, increase + * rwnd by that amount. If all the data in the skb is read, + * rwnd is updated when the skb's destructor is called via + * sctp_ulpevent_free(). + */ + sctp_assoc_rwnd_increase(event->event_asoc, copied); + goto out; + } else if ((event->msg_flags & MSG_NOTIFICATION) || + (event->msg_flags & MSG_EOR)) + msg->msg_flags |= MSG_EOR; + else + msg->msg_flags &= ~MSG_EOR; + +out_free: + sctp_ulpevent_free(event); /* Free the skb. */ +out: + sctp_release_sock(sk); + return err; +} + +static int sctp_setsockopt_disable_fragments(struct sock *sk, + char *optval, int optlen) +{ + int val; + + if (optlen < sizeof(int)) + return -EINVAL; + + if (get_user(val, (int *)optval)) + return -EFAULT; + + sctp_sk(sk)->disable_fragments = (val == 0) ? 0 : 1; + + return 0; +} + +static int sctp_setsockopt_events(struct sock *sk, char *optval, + int optlen) +{ + if (optlen != sizeof(struct sctp_event_subscribe)) + return -EINVAL; + if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen)) + return -EFAULT; + return 0; +} + +static int sctp_setsockopt_autoclose(struct sock *sk, char *optval, + int optlen) +{ + struct sctp_opt *sp = sctp_sk(sk); + + /* Applicable to UDP-style socket only */ + if (SCTP_SOCKET_TCP == sp->type) + return -EOPNOTSUPP; + if (optlen != sizeof(int)) + return -EINVAL; + if (copy_from_user(&sp->autoclose, optval, optlen)) + return -EFAULT; + + sp->ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ; + return 0; +} + +static int sctp_setsockopt_peer_addr_params(struct sock *sk, + char *optval, int optlen) +{ + struct sctp_paddrparams params; + struct sctp_association *asoc; + union sctp_addr *addr; + struct sctp_transport *trans; + int error; + + if (optlen != sizeof(struct sctp_paddrparams)) + return -EINVAL; + if (copy_from_user(¶ms, optval, optlen)) + return -EFAULT; + + asoc = sctp_id2assoc(sk, params.spp_assoc_id); + if (!asoc) + return -EINVAL; + + addr = (union sctp_addr *) &(params.spp_address); + + trans = sctp_assoc_lookup_paddr(asoc, addr); + if (!trans) + return -ENOENT; + + /* Applications can enable or disable heartbeats for any peer address + * of an association, modify an address's heartbeat interval, force a + * heartbeat to be sent immediately, and adjust the address's maximum + * number of retransmissions sent before an address is considered + * unreachable. + * + * The value of the heartbeat interval, in milliseconds. A value of + * UINT32_MAX (4294967295), when modifying the parameter, specifies + * that a heartbeat should be sent immediately to the peer address, + * and the current interval should remain unchanged. + */ + if (0xffffffff == params.spp_hbinterval) { + error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans); + if (error) + return error; + } else { + /* The value of the heartbeat interval, in milliseconds. A value of 0, + * when modifying the parameter, specifies that the heartbeat on this + * address should be disabled. + */ + if (params.spp_hbinterval) { + trans->hb_allowed = 1; + trans->hb_interval = params.spp_hbinterval * HZ / 1000; + } else + trans->hb_allowed = 0; + } + + /* spp_pathmaxrxt contains the maximum number of retransmissions + * before this address shall be considered unreachable. + */ + trans->error_threshold = params.spp_pathmaxrxt; + + return 0; +} + +static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen) +{ + if (optlen != sizeof(struct sctp_initmsg)) + return -EINVAL; + if (copy_from_user(&sctp_sk(sk)->initmsg, optval, optlen)) + return -EFAULT; + return 0; +} + +/* + * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM) + * + * Applications that wish to use the sendto() system call may wish to + * specify a default set of parameters that would normally be supplied + * through the inclusion of ancillary data. This socket option allows + * such an application to set the default sctp_sndrcvinfo structure. + * The application that wishes to use this socket option simply passes + * in to this call the sctp_sndrcvinfo structure defined in Section + * 5.2.2) The input parameters accepted by this call include + * sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context, + * sinfo_timetolive. The user must provide the sinfo_assoc_id field in + * to this call if the caller is using the UDP model. + */ +static int sctp_setsockopt_default_send_param(struct sock *sk, + char *optval, int optlen) +{ + struct sctp_sndrcvinfo info; + struct sctp_association *asoc; + + if (optlen != sizeof(struct sctp_sndrcvinfo)) + return -EINVAL; + if (copy_from_user(&info, optval, optlen)) + return -EFAULT; + + asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); + if (!asoc) + return -EINVAL; + + asoc->defaults.stream = info.sinfo_stream; + asoc->defaults.flags = info.sinfo_flags; + asoc->defaults.ppid = info.sinfo_ppid; + asoc->defaults.context = info.sinfo_context; + asoc->defaults.timetolive = info.sinfo_timetolive; + return 0; +} + +/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. + */ +static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen) +{ + struct sctp_setpeerprim prim; + struct sctp_association *asoc; + union sctp_addr *addr; + struct sctp_transport *trans; + + if (optlen != sizeof(struct sctp_setpeerprim)) + return -EINVAL; + + if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim))) + return -EFAULT; + + asoc = sctp_id2assoc(sk, prim.sspp_assoc_id); + if (!asoc) + return -EINVAL; + + /* Find the requested address. */ + addr = (union sctp_addr *) &(prim.sspp_addr); + + trans = sctp_assoc_lookup_paddr(asoc, addr); + if (!trans) + return -ENOENT; + + sctp_assoc_set_primary(asoc, trans); + + return 0; +} + +/* + * + * 7.1.5 SCTP_NODELAY + * + * Turn on/off any Nagle-like algorithm. This means that packets are + * generally sent as soon as possible and no unnecessary delays are + * introduced, at the cost of more packets in the network. Expects an + * integer boolean flag. + */ +static int sctp_setsockopt_nodelay(struct sock *sk, char *optval, + int optlen) +{ + __u8 val; + + if (optlen < sizeof(__u8)) + return -EINVAL; + if (get_user(val, (__u8 *)optval)) + return -EFAULT; + + sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1; + return 0; +} + +/* API 6.2 setsockopt(), getsockopt() + * + * Applications use setsockopt() and getsockopt() to set or retrieve + * socket options. Socket options are used to change the default + * behavior of sockets calls. They are described in Section 7. + * + * The syntax is: + * + * ret = getsockopt(int sd, int level, int optname, void *optval, + * int *optlen); + * ret = setsockopt(int sd, int level, int optname, const void *optval, + * int optlen); + * + * sd - the socket descript. + * level - set to IPPROTO_SCTP for all SCTP options. + * optname - the option name. + * optval - the buffer to store the value of the option. + * optlen - the size of the buffer. + */ +SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, + char *optval, int optlen) +{ + int retval = 0; + char *tmp; + + SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n", + sk, optname); + + /* I can hardly begin to describe how wrong this is. This is + * so broken as to be worse than useless. The API draft + * REALLY is NOT helpful here... I am not convinced that the + * semantics of setsockopt() with a level OTHER THAN SOL_SCTP + * are at all well-founded. + */ + if (level != SOL_SCTP) { + struct sctp_af *af = sctp_sk(sk)->pf->af; + retval = af->setsockopt(sk, level, optname, optval, optlen); + goto out_nounlock; + } + + sctp_lock_sock(sk); + + switch (optname) { + case SCTP_SOCKOPT_DEBUG_NAME: + /* BUG! we don't ever seem to free this memory. --jgrimm */ + if (NULL == (tmp = kmalloc(optlen + 1, GFP_KERNEL))) { + retval = -ENOMEM; + goto out_unlock; + } + + if (copy_from_user(tmp, optval, optlen)) { + retval = -EFAULT; + goto out_unlock; + } + tmp[optlen] = '\000'; + sctp_sk(sk)->ep->debug_name = tmp; + break; + + case SCTP_SOCKOPT_BINDX_ADD: + /* 'optlen' is the size of the addresses buffer. */ + retval = sctp_setsockopt_bindx(sk, (struct sockaddr_storage *) + optval, optlen, BINDX_ADD_ADDR); + break; + + case SCTP_SOCKOPT_BINDX_REM: + /* 'optlen' is the size of the addresses buffer. */ + retval = sctp_setsockopt_bindx(sk, (struct sockaddr_storage *) + optval, optlen, BINDX_REM_ADDR); + break; + + case SCTP_DISABLE_FRAGMENTS: + retval = sctp_setsockopt_disable_fragments(sk, optval, optlen); + break; + + case SCTP_SET_EVENTS: + retval = sctp_setsockopt_events(sk, optval, optlen); + break; + + case SCTP_AUTOCLOSE: + retval = sctp_setsockopt_autoclose(sk, optval, optlen); + break; + + case SCTP_SET_PEER_ADDR_PARAMS: + retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen); + break; + + case SCTP_INITMSG: + retval = sctp_setsockopt_initmsg(sk, optval, optlen); + break; + + case SCTP_SET_DEFAULT_SEND_PARAM: + retval = sctp_setsockopt_default_send_param(sk, optval, + optlen); + break; + + case SCTP_SET_PEER_PRIMARY_ADDR: + retval = sctp_setsockopt_peer_prim(sk, optval, optlen); + break; + + case SCTP_NODELAY: + retval = sctp_setsockopt_nodelay(sk, optval, optlen); + break; + + default: + retval = -ENOPROTOOPT; + break; + }; + +out_unlock: + sctp_release_sock(sk); + +out_nounlock: + return retval; +} + +/* API 3.1.6 connect() - UDP Style Syntax + * + * An application may use the connect() call in the UDP model to initiate an + * association without sending data. + * + * The syntax is: + * + * ret = connect(int sd, const struct sockaddr *nam, socklen_t len); + * + * sd: the socket descriptor to have a new association added to. + * + * nam: the address structure (either struct sockaddr_in or struct + * sockaddr_in6 defined in RFC2553 [7]). + * + * len: the size of the address. + */ +SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + struct sctp_opt *sp; + struct sctp_endpoint *ep; + struct sctp_association *asoc; + struct sctp_transport *transport; + union sctp_addr to; + struct sctp_af *af; + sctp_scope_t scope; + long timeo; + int err = 0; + + sctp_lock_sock(sk); + + SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d)\n", + __FUNCTION__, sk, uaddr, addr_len); + + sp = sctp_sk(sk); + ep = sp->ep; + + /* connect() cannot be done on a socket that is already in ESTABLISHED + * state - UDP-style peeled off socket or a TCP-style socket that + * is already connected. + * It cannot be done even on a TCP-style listening socket. + */ + if ((SCTP_SS_ESTABLISHED == sk->state) || + ((SCTP_SOCKET_TCP == sp->type) && + (SCTP_SS_LISTENING == sk->state))) { + err = -EISCONN; + goto out_unlock; + } + + err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len); + if (err) + goto out_unlock; + + if (addr_len > sizeof(to)) + addr_len = sizeof(to); + memcpy(&to, uaddr, addr_len); + to.v4.sin_port = ntohs(to.v4.sin_port); + + asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); + if (asoc) { + if (asoc->state >= SCTP_STATE_ESTABLISHED) + err = -EISCONN; + else + err = -EALREADY; + goto out_unlock; + } + + /* If we could not find a matching association on the endpoint, + * make sure that there is no peeled-off association matching the + * peer address even on another socket. + */ + if (sctp_endpoint_is_peeled_off(ep, &to)) { + err = -EADDRNOTAVAIL; + goto out_unlock; + } + + /* If a bind() or sctp_bindx() is not called prior to a connect() + * call, the system picks an ephemeral port and will choose an address + * set equivalent to binding with a wildcard address. + */ + if (!ep->base.bind_addr.port) { + if (sctp_autobind(sk)) { + err = -EAGAIN; + goto out_unlock; + } + } + + scope = sctp_scope(&to); + asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL); + if (!asoc) { + err = -ENOMEM; + goto out_unlock; + } + + /* Prime the peer's transport structures. */ + transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL); + if (!transport) { + sctp_association_free(asoc); + goto out_unlock; + } + err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL); + if (err < 0) { + sctp_association_free(asoc); + goto out_unlock; + } + + err = sctp_primitive_ASSOCIATE(asoc, NULL); + if (err < 0) { + sctp_association_free(asoc); + goto out_unlock; + } + + /* Initialize sk's dport and daddr for getpeername() */ + (sk)->dport = htons(asoc->peer.port); + af = sctp_get_af_specific(to.sa.sa_family); + af->to_sk_daddr(&to, sk); + + timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK); + err = sctp_wait_for_connect(asoc, &timeo); + +out_unlock: + sctp_release_sock(sk); + + return err; +} + +/* FIXME: Write comments. */ +SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags) +{ + return -EOPNOTSUPP; /* STUB */ +} + +/* 4.1.4 accept() - TCP Style Syntax + * + * Applications use accept() call to remove an established SCTP + * association from the accept queue of the endpoint. A new socket + * descriptor will be returned from accept() to represent the newly + * formed association. + */ +SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err) +{ + struct sctp_opt *sp; + struct sctp_endpoint *ep; + struct sock *newsk = NULL; + struct sctp_association *asoc; + long timeo; + int error = 0; + + sctp_lock_sock(sk); + + sp = sctp_sk(sk); + ep = sp->ep; + + if (SCTP_SOCKET_TCP != sp->type) { + error = -EOPNOTSUPP; + goto out; + } + + if (SCTP_SS_LISTENING != sk->state) { + error = -EINVAL; + goto out; + } + + timeo = sock_rcvtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK); + + error = sctp_wait_for_accept(sk, timeo); + if (error) + goto out; + + /* We treat the list of associations on the endpoint as the accept + * queue and pick the first association on the list. + */ + asoc = list_entry(ep->asocs.next, struct sctp_association, asocs); + + newsk = sp->pf->create_accept_sk(sk, asoc); + if (!newsk) { + error = -ENOMEM; + goto out; + } + + /* Populate the fields of the newsk from the oldsk and migrate the + * asoc to the newsk. + */ + sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP); + +out: + sctp_release_sock(sk); + *err = error; + return newsk; +} + +/* The SCTP ioctl handler. */ +SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +/* This is the function which gets called during socket creation to + * initialized the SCTP-specific portion of the sock. + * The sock structure should already be zero-filled memory. + */ +SCTP_STATIC int sctp_init_sock(struct sock *sk) +{ + struct sctp_endpoint *ep; + struct sctp_protocol *proto; + struct sctp_opt *sp; + + SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk); + + proto = sctp_get_protocol(); + + sp = sctp_sk(sk); + + /* Initialize the SCTP per socket area. */ + switch (sk->type) { + case SOCK_SEQPACKET: + sp->type = SCTP_SOCKET_UDP; + break; + case SOCK_STREAM: + sp->type = SCTP_SOCKET_TCP; + break; + default: + return -ESOCKTNOSUPPORT; + } + + /* FIXME: The next draft (04) of the SCTP Sockets Extensions + * should include a socket option for manipulating these + * message parameters (and a few others). + */ + sp->default_stream = 0; + sp->default_ppid = 0; + + /* Initialize default setup parameters. These parameters + * can be modified with the SCTP_INITMSG socket option or + * overridden by the SCTP_INIT CMSG. + */ + sp->initmsg.sinit_num_ostreams = proto->max_outstreams; + sp->initmsg.sinit_max_instreams = proto->max_instreams; + sp->initmsg.sinit_max_attempts = proto->max_retrans_init; + sp->initmsg.sinit_max_init_timeo = proto->rto_max / HZ; + + /* Initialize default RTO related parameters. These parameters can + * be modified for with the SCTP_RTOINFO socket option. + * FIXME: These are not used yet. + */ + sp->rtoinfo.srto_initial = proto->rto_initial; + sp->rtoinfo.srto_max = proto->rto_max; + sp->rtoinfo.srto_min = proto->rto_min; + + /* Initialize default event subscriptions. + * the struct sock is initialized to zero, so only + * enable the events needed. By default, UDP-style + * sockets enable io and association change notifications. + */ + if (SCTP_SOCKET_UDP == sp->type) { + sp->subscribe.sctp_data_io_event = 1; + sp->subscribe.sctp_association_event = 1; + } + + /* Default Peer Address Parameters. These defaults can + * be modified via SCTP_SET_PEER_ADDR_PARAMS + */ + sp->paddrparam.spp_hbinterval = proto->hb_interval / HZ; + sp->paddrparam.spp_pathmaxrxt = proto->max_retrans_path; + + /* If enabled no SCTP message fragmentation will be performed. + * Configure through SCTP_DISABLE_FRAGMENTS socket option. + */ + sp->disable_fragments = 0; + + /* Turn on/off any Nagle-like algorithm. */ + sp->nodelay = 1; + + /* Auto-close idle associations after the configured + * number of seconds. A value of 0 disables this + * feature. Configure through the SCTP_AUTOCLOSE socket option, + * for UDP-style sockets only. + */ + sp->autoclose = 0; + sp->pf = sctp_get_pf_specific(sk->family); + + /* Control variables for partial data delivery. */ + sp->pd_mode = 0; + skb_queue_head_init(&sp->pd_lobby); + + /* Create a per socket endpoint structure. Even if we + * change the data structure relationships, this may still + * be useful for storing pre-connect address information. + */ + ep = sctp_endpoint_new(sk, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + sp->ep = ep; + sp->hmac = NULL; + + SCTP_DBG_OBJCNT_INC(sock); + return 0; +} + +/* Cleanup any SCTP per socket resources. */ +SCTP_STATIC int sctp_destroy_sock(struct sock *sk) +{ + struct sctp_endpoint *ep; + + SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk); + + /* Release our hold on the endpoint. */ + ep = sctp_sk(sk)->ep; + sctp_endpoint_free(ep); + + return 0; +} + +/* API 4.1.7 shutdown() - TCP Style Syntax + * int shutdown(int socket, int how); + * + * sd - the socket descriptor of the association to be closed. + * how - Specifies the type of shutdown. The values are + * as follows: + * SHUT_RD + * Disables further receive operations. No SCTP + * protocol action is taken. + * SHUT_WR + * Disables further send operations, and initiates + * the SCTP shutdown sequence. + * SHUT_RDWR + * Disables further send and receive operations + * and initiates the SCTP shutdown sequence. + */ +SCTP_STATIC void sctp_shutdown(struct sock *sk, int how) +{ + struct sctp_endpoint *ep; + struct sctp_association *asoc; + + if (SCTP_SOCKET_TCP != sctp_sk(sk)->type) + return; + + if (how & SEND_SHUTDOWN) { + ep = sctp_sk(sk)->ep; + if (!list_empty(&ep->asocs)) { + asoc = list_entry(ep->asocs.next, + struct sctp_association, asocs); + sctp_primitive_SHUTDOWN(asoc, NULL); + } + } +} + +/* 7.2.1 Association Status (SCTP_STATUS) + + * Applications can retrieve current status information about an + * association, including association state, peer receiver window size, + * number of unacked data chunks, and number of data chunks pending + * receipt. This information is read-only. + */ +static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, + int *optlen) +{ + struct sctp_status status; + struct sctp_association *asoc = NULL; + struct sctp_transport *transport; + sctp_assoc_t associd; + int retval = 0; + + if (len != sizeof(status)) { + retval = -EINVAL; + goto out; + } + + if (copy_from_user(&status, optval, sizeof(status))) { + retval = -EFAULT; + goto out; + } + + associd = status.sstat_assoc_id; + asoc = sctp_id2assoc(sk, associd); + if (!asoc) { + retval = -EINVAL; + goto out; + } + + transport = asoc->peer.primary_path; + + status.sstat_assoc_id = sctp_assoc2id(asoc); + status.sstat_state = asoc->state; + status.sstat_rwnd = asoc->peer.rwnd; + status.sstat_unackdata = asoc->unack_data; + status.sstat_penddata = asoc->peer.tsn_map.pending_data; + status.sstat_instrms = asoc->c.sinit_max_instreams; + status.sstat_outstrms = asoc->c.sinit_num_ostreams; + status.sstat_fragmentation_point = asoc->frag_point; + status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc); + memcpy(&status.sstat_primary.spinfo_address, + &(transport->ipaddr), sizeof(union sctp_addr)); + status.sstat_primary.spinfo_state = transport->active; + status.sstat_primary.spinfo_cwnd = transport->cwnd; + status.sstat_primary.spinfo_srtt = transport->srtt; + status.sstat_primary.spinfo_rto = transport->rto; + status.sstat_primary.spinfo_mtu = transport->pmtu; + + if (put_user(len, optlen)) { + retval = -EFAULT; + goto out; + } + + SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n", + len, status.sstat_state, status.sstat_rwnd, + status.sstat_assoc_id); + + if (copy_to_user(optval, &status, len)) { + retval = -EFAULT; + goto out; + } + +out: + return (retval); +} + +static int sctp_getsockopt_disable_fragments(struct sock *sk, int len, + char *optval, int *optlen) +{ + int val; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + val = (sctp_sk(sk)->disable_fragments == 1); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + +static int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen) +{ + if (len != sizeof(struct sctp_event_subscribe)) + return -EINVAL; + if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len)) + return -EFAULT; + return 0; +} + +static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen) +{ + /* Applicable to UDP-style socket only */ + if (SCTP_SOCKET_TCP == sctp_sk(sk)->type) + return -EOPNOTSUPP; + if (len != sizeof(int)) + return -EINVAL; + if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len)) + return -EFAULT; + return 0; +} + +/* Helper routine to branch off an association to a new socket. */ +SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc, + struct socket **sockp) +{ + struct sock *sk = asoc->base.sk; + struct socket *sock; + int err = 0; + + /* An association cannot be branched off from an already peeled-off + * socket, nor is this supported for tcp style sockets. + */ + if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) + return -EINVAL; + + /* Create a new socket. */ + err = sock_create(sk->family, SOCK_SEQPACKET, IPPROTO_SCTP, &sock); + if (err < 0) + return err; + + /* Populate the fields of the newsk from the oldsk and migrate the + * asoc to the newsk. + */ + sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH); + *sockp = sock; + + return err; +} + +static int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen) +{ + sctp_peeloff_arg_t peeloff; + struct socket *newsock; + int retval = 0; + struct sctp_association *asoc; + + if (len != sizeof(sctp_peeloff_arg_t)) + return -EINVAL; + if (copy_from_user(&peeloff, optval, len)) + return -EFAULT; + + asoc = sctp_id2assoc(sk, peeloff.associd); + if (!asoc) { + retval = -EINVAL; + goto out; + } + + SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p\n", __FUNCTION__, sk, asoc); + + retval = sctp_do_peeloff(asoc, &newsock); + if (retval < 0) + goto out; + + /* Map the socket to an unused fd that can be returned to the user. */ + retval = sock_map_fd(newsock); + if (retval < 0) { + sock_release(newsock); + goto out; + } + + SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p newsk: %p sd: %d\n", + __FUNCTION__, sk, asoc, newsock->sk, retval); + + /* Return the fd mapped to the new socket. */ + peeloff.sd = retval; + if (copy_to_user(optval, &peeloff, len)) + retval = -EFAULT; + +out: + return retval; +} + +static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, + char *optval, int *optlen) +{ + struct sctp_paddrparams params; + struct sctp_association *asoc; + union sctp_addr *addr; + struct sctp_transport *trans; + + if (len != sizeof(struct sctp_paddrparams)) + return -EINVAL; + if (copy_from_user(¶ms, optval, *optlen)) + return -EFAULT; + + asoc = sctp_id2assoc(sk, params.spp_assoc_id); + if (!asoc) + return -EINVAL; + + addr = (union sctp_addr *) &(params.spp_address); + + trans = sctp_assoc_lookup_paddr(asoc, addr); + if (!trans) + return -ENOENT; + + /* The value of the heartbeat interval, in milliseconds. A value of 0, + * when modifying the parameter, specifies that the heartbeat on this + * address should be disabled. + */ + if (!trans->hb_allowed) + params.spp_hbinterval = 0; + else + params.spp_hbinterval = trans->hb_interval * 1000 / HZ; + + /* spp_pathmaxrxt contains the maximum number of retransmissions + * before this address shall be considered unreachable. + */ + params.spp_pathmaxrxt = trans->error_threshold; + + if (copy_to_user(optval, ¶ms, len)) + return -EFAULT; + *optlen = len; + + return 0; +} + +static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen) +{ + if (len != sizeof(struct sctp_initmsg)) + return -EINVAL; + if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len)) + return -EFAULT; + return 0; +} + +static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len, + char *optval, int *optlen) +{ + sctp_assoc_t id; + struct sctp_association *asoc; + struct list_head *pos; + int cnt = 0; + + if (len != sizeof(sctp_assoc_t)) + return -EINVAL; + + if (copy_from_user(&id, optval, sizeof(sctp_assoc_t))) + return -EFAULT; + + /* + * For UDP-style sockets, id specifies the association to query. + */ + asoc = sctp_id2assoc(sk, id); + if (!asoc) + return -EINVAL; + + list_for_each(pos, &asoc->peer.transport_addr_list) { + cnt ++; + } + if (copy_to_user(optval, &cnt, sizeof(int))) + return -EFAULT; + + return 0; +} + +static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, + char *optval, int *optlen) +{ + struct sctp_association *asoc; + struct list_head *pos; + int cnt = 0; + struct sctp_getaddrs getaddrs; + struct sctp_transport *from; + struct sockaddr_storage *to; + + if (len != sizeof(struct sctp_getaddrs)) + return -EINVAL; + + if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs))) + return -EFAULT; + + if (getaddrs.addr_num <= 0) return -EINVAL; + /* + * For UDP-style sockets, id specifies the association to query. + */ + asoc = sctp_id2assoc(sk, getaddrs.assoc_id); + if (!asoc) + return -EINVAL; + + to = getaddrs.addrs; + list_for_each(pos, &asoc->peer.transport_addr_list) { + from = list_entry(pos, struct sctp_transport, transports); + if (copy_to_user(to, &from->ipaddr, sizeof(from->ipaddr))) + return -EFAULT; + to ++; + cnt ++; + if (cnt >= getaddrs.addr_num) break; + } + getaddrs.addr_num = cnt; + if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs))) + return -EFAULT; + + return 0; +} + +static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, + char *optval, int *optlen) +{ + sctp_assoc_t id; + sctp_bind_addr_t *bp; + struct sctp_association *asoc; + struct list_head *pos; + int cnt = 0; + + if (len != sizeof(sctp_assoc_t)) + return -EINVAL; + + if (copy_from_user(&id, optval, sizeof(sctp_assoc_t))) + return -EFAULT; + + /* + * For UDP-style sockets, id specifies the association to query. + * If the id field is set to the value '0' then the locally bound + * addresses are returned without regard to any particular + * association. + */ + if (0 == id) { + bp = &sctp_sk(sk)->ep->base.bind_addr; + } else { + asoc = sctp_id2assoc(sk, id); + if (!asoc) + return -EINVAL; + bp = &asoc->base.bind_addr; + } + + list_for_each(pos, &bp->address_list) { + cnt ++; + } + if (copy_to_user(optval, &cnt, sizeof(int))) + return -EFAULT; + + return 0; +} + +static int sctp_getsockopt_local_addrs(struct sock *sk, int len, + char *optval, int *optlen) +{ + sctp_bind_addr_t *bp; + struct sctp_association *asoc; + struct list_head *pos; + int cnt = 0; + struct sctp_getaddrs getaddrs; + struct sockaddr_storage_list *from; + struct sockaddr_storage *to; + + if (len != sizeof(struct sctp_getaddrs)) + return -EINVAL; + + if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs))) + return -EFAULT; + + if (getaddrs.addr_num <= 0) return -EINVAL; + /* + * For UDP-style sockets, id specifies the association to query. + * If the id field is set to the value '0' then the locally bound + * addresses are returned without regard to any particular + * association. + */ + if (0 == getaddrs.assoc_id) { + bp = &sctp_sk(sk)->ep->base.bind_addr; + } else { + asoc = sctp_id2assoc(sk, getaddrs.assoc_id); + if (!asoc) + return -EINVAL; + bp = &asoc->base.bind_addr; + } + + to = getaddrs.addrs; + list_for_each(pos, &bp->address_list) { + from = list_entry(pos, + struct sockaddr_storage_list, + list); + if (copy_to_user(to, &from->a, sizeof(from->a))) + return -EFAULT; + to ++; + cnt ++; + if (cnt >= getaddrs.addr_num) break; + } + getaddrs.addr_num = cnt; + if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs))) + return -EFAULT; + + return 0; +} + +/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. + */ +static int sctp_getsockopt_peer_prim(struct sock *sk, int len, + char *optval, int *optlen) +{ + struct sctp_setpeerprim prim; + struct sctp_association *asoc; + + if (len != sizeof(struct sctp_setpeerprim)) + return -EINVAL; + + if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim))) + return -EFAULT; + + asoc = sctp_id2assoc(sk, prim.sspp_assoc_id); + if (!asoc) + return -EINVAL; + + if (!asoc->peer.primary_path) + return -ENOTCONN; + + memcpy(&prim.sspp_addr, &asoc->peer.primary_path->ipaddr, + sizeof(union sctp_addr)); + + if (copy_to_user(optval, &prim, sizeof(struct sctp_setpeerprim))) + return -EFAULT; + + return 0; +} + +/* + * + * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM) + * + * Applications that wish to use the sendto() system call may wish to + * specify a default set of parameters that would normally be supplied + * through the inclusion of ancillary data. This socket option allows + * such an application to set the default sctp_sndrcvinfo structure. + * The application that wishes to use this socket option simply passes + * in to this call the sctp_sndrcvinfo structure defined in Section + * 5.2.2) The input parameters accepted by this call include + * sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context, + * sinfo_timetolive. The user must provide the sinfo_assoc_id field in + * to this call if the caller is using the UDP model. + * + * For getsockopt, it get the default sctp_sndrcvinfo structure. + */ +static int sctp_getsockopt_default_send_param(struct sock *sk, + int len, char *optval, int *optlen) +{ + struct sctp_sndrcvinfo info; + struct sctp_association *asoc; + + if (len != sizeof(struct sctp_sndrcvinfo)) + return -EINVAL; + if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo))) + return -EFAULT; + + asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); + if (!asoc) + return -EINVAL; + + info.sinfo_stream = asoc->defaults.stream; + info.sinfo_flags = asoc->defaults.flags; + info.sinfo_ppid = asoc->defaults.ppid; + info.sinfo_context = asoc->defaults.context; + info.sinfo_timetolive = asoc->defaults.timetolive; + + if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo))) + return -EFAULT; + + return 0; +} + +/* + * + * 7.1.5 SCTP_NODELAY + * + * Turn on/off any Nagle-like algorithm. This means that packets are + * generally sent as soon as possible and no unnecessary delays are + * introduced, at the cost of more packets in the network. Expects an + * integer boolean flag. + */ + +static int sctp_getsockopt_nodelay(struct sock *sk, int len, + char *optval, int *optlen) +{ + __u8 val; + + if (len < sizeof(__u8)) + return -EINVAL; + + len = sizeof(__u8); + val = (sctp_sk(sk)->nodelay == 1); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + return 0; +} + +SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, + char *optval, int *optlen) +{ + int retval = 0; + int len; + + SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p, ...)\n", sk); + + /* I can hardly begin to describe how wrong this is. This is + * so broken as to be worse than useless. The API draft + * REALLY is NOT helpful here... I am not convinced that the + * semantics of getsockopt() with a level OTHER THAN SOL_SCTP + * are at all well-founded. + */ + if (level != SOL_SCTP) { + struct sctp_af *af = sctp_sk(sk)->pf->af; + + retval = af->getsockopt(sk, level, optname, optval, optlen); + return retval; + } + + if (get_user(len, optlen)) + return -EFAULT; + + sctp_lock_sock(sk); + + switch (optname) { + case SCTP_STATUS: + retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen); + break; + case SCTP_DISABLE_FRAGMENTS: + retval = sctp_getsockopt_disable_fragments(sk, len, optval, + optlen); + break; + case SCTP_SET_EVENTS: + retval = sctp_getsockopt_set_events(sk, len, optval, optlen); + break; + case SCTP_AUTOCLOSE: + retval = sctp_getsockopt_autoclose(sk, len, optval, optlen); + break; + case SCTP_SOCKOPT_PEELOFF: + retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); + break; + case SCTP_GET_PEER_ADDR_PARAMS: + retval = sctp_getsockopt_peer_addr_params(sk, len, optval, + optlen); + break; + case SCTP_INITMSG: + retval = sctp_getsockopt_initmsg(sk, len, optval, optlen); + break; + case SCTP_GET_PEER_ADDRS_NUM: + retval = sctp_getsockopt_peer_addrs_num(sk, len, optval, + optlen); + break; + case SCTP_GET_LOCAL_ADDRS_NUM: + retval = sctp_getsockopt_local_addrs_num(sk, len, optval, + optlen); + break; + case SCTP_GET_PEER_ADDRS: + retval = sctp_getsockopt_peer_addrs(sk, len, optval, + optlen); + break; + case SCTP_GET_LOCAL_ADDRS: + retval = sctp_getsockopt_local_addrs(sk, len, optval, + optlen); + break; + case SCTP_SET_DEFAULT_SEND_PARAM: + retval = sctp_getsockopt_default_send_param(sk, len, + optval, optlen); + break; + case SCTP_SET_PEER_PRIMARY_ADDR: + retval = sctp_getsockopt_peer_prim(sk, len, optval, optlen); + break; + case SCTP_NODELAY: + retval = sctp_getsockopt_nodelay(sk, len, optval, optlen); + break; + default: + retval = -ENOPROTOOPT; + break; + }; + + sctp_release_sock(sk); + return retval; +} + +static void sctp_hash(struct sock *sk) +{ + /* STUB */ +} + +static void sctp_unhash(struct sock *sk) +{ + /* STUB */ +} + +/* Check if port is acceptable. Possibly find first available port. + * + * The port hash table (contained in the 'global' SCTP protocol storage + * returned by struct sctp_protocol *sctp_get_protocol()). The hash + * table is an array of 4096 lists (sctp_bind_hashbucket_t). Each + * list (the list number is the port number hashed out, so as you + * would expect from a hash function, all the ports in a given list have + * such a number that hashes out to the same list number; you were + * expecting that, right?); so each list has a set of ports, with a + * link to the socket (struct sock) that uses it, the port number and + * a fastreuse flag (FIXME: NPI ipg). + */ +static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, + unsigned short snum); +static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) +{ + sctp_bind_hashbucket_t *head; /* hash list */ + sctp_bind_bucket_t *pp; /* hash list port iterator */ + struct sctp_protocol *sctp = sctp_get_protocol(); + unsigned short snum; + int ret; + + /* NOTE: Remember to put this back to net order. */ + addr->v4.sin_port = ntohs(addr->v4.sin_port); + snum = addr->v4.sin_port; + + SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum); + + sctp_local_bh_disable(); + + if (snum == 0) { + /* Search for an available port. + * + * 'sctp->port_rover' was the last port assigned, so + * we start to search from 'sctp->port_rover + + * 1'. What we do is first check if port 'rover' is + * already in the hash table; if not, we use that; if + * it is, we try next. + */ + int low = sysctl_local_port_range[0]; + int high = sysctl_local_port_range[1]; + int remaining = (high - low) + 1; + int rover; + int index; + + sctp_spin_lock(&sctp->port_alloc_lock); + rover = sctp->port_rover; + do { + rover++; + if ((rover < low) || (rover > high)) + rover = low; + index = sctp_phashfn(rover); + head = &sctp->port_hashtable[index]; + sctp_spin_lock(&head->lock); + for (pp = head->chain; pp; pp = pp->next) + if (pp->port == rover) + goto next; + break; + next: + sctp_spin_unlock(&head->lock); + } while (--remaining > 0); + sctp->port_rover = rover; + sctp_spin_unlock(&sctp->port_alloc_lock); + + /* Exhausted local port range during search? */ + ret = 1; + if (remaining <= 0) + goto fail; + + /* OK, here is the one we will use. HEAD (the port + * hash table list entry) is non-NULL and we hold it's + * mutex. + */ + snum = rover; + pp = NULL; + } else { + /* We are given an specific port number; we verify + * that it is not being used. If it is used, we will + * exahust the search in the hash list corresponding + * to the port number (snum) - we detect that with the + * port iterator, pp being NULL. + */ + head = &sctp->port_hashtable[sctp_phashfn(snum)]; + sctp_spin_lock(&head->lock); + for (pp = head->chain; pp; pp = pp->next) { + if (pp->port == snum) + break; + } + } + + + if (pp && pp->sk) { + /* We had a port hash table hit - there is an + * available port (pp != NULL) and it is being + * used by other socket (pp->sk != NULL); that other + * socket is going to be sk2. + */ + int sk_reuse = sk->reuse; + struct sock *sk2 = pp->sk; + + SCTP_DEBUG_PRINTK("sctp_get_port() found a " + "possible match\n"); + if (pp->fastreuse != 0 && sk->reuse != 0) + goto success; + + /* Run through the list of sockets bound to the port + * (pp->port) [via the pointers bind_next and + * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one, + * we get the endpoint they describe and run through + * the endpoint's list of IP (v4 or v6) addresses, + * comparing each of the addresses with the address of + * the socket sk. If we find a match, then that means + * that this port/socket (sk) combination are already + * in an endpoint. + */ + for ( ; sk2 != NULL; sk2 = sk2->bind_next) { + struct sctp_endpoint *ep2; + ep2 = sctp_sk(sk2)->ep; + + if (sk_reuse && sk2->reuse) + continue; + + if (sctp_bind_addr_match(&ep2->base.bind_addr, addr, + sctp_sk(sk))) + goto found; + } + + found: + /* If we found a conflict, fail. */ + if (sk2 != NULL) { + ret = (long) sk2; + goto fail_unlock; + } + SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n"); + } + + /* If there was a hash table miss, create a new port. */ + ret = 1; + + if (!pp && !(pp = sctp_bucket_create(head, snum))) + goto fail_unlock; + + /* In either case (hit or miss), make sure fastreuse is 1 only + * if sk->reuse is too (that is, if the caller requested + * SO_REUSEADDR on this socket -sk-). + */ + if (!pp->sk) + pp->fastreuse = sk->reuse ? 1 : 0; + else if (pp->fastreuse && sk->reuse == 0) + pp->fastreuse = 0; + + /* We are set, so fill up all the data in the hash table + * entry, tie the socket list information with the rest of the + * sockets FIXME: Blurry, NPI (ipg). + */ +success: + (sk)->num = snum; + if (sk->prev == NULL) { + if ((sk->bind_next = pp->sk) != NULL) + pp->sk->bind_pprev = &sk->bind_next; + pp->sk = sk; + sk->bind_pprev = &pp->sk; + sk->prev = (struct sock *) pp; + } + ret = 0; + +fail_unlock: + sctp_spin_unlock(&head->lock); + +fail: + sctp_local_bh_enable(); + + SCTP_DEBUG_PRINTK("sctp_get_port() ends, ret=%d\n", ret); + addr->v4.sin_port = htons(addr->v4.sin_port); + return ret; +} + +/* Assign a 'snum' port to the socket. If snum == 0, an ephemeral + * port is requested. + */ +static int sctp_get_port(struct sock *sk, unsigned short snum) +{ + long ret; + union sctp_addr addr; + struct sctp_af *af = sctp_sk(sk)->pf->af; + + /* Set up a dummy address struct from the sk. */ + af->from_sk(&addr, sk); + addr.v4.sin_port = htons(snum); + + /* Note: sk->num gets filled in if ephemeral port request. */ + ret = sctp_get_port_local(sk, &addr); + + return (ret ? 1 : 0); +} + +/* + * 3.1.3 listen() - UDP Style Syntax + * + * By default, new associations are not accepted for UDP style sockets. + * An application uses listen() to mark a socket as being able to + * accept new associations. + */ +SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) +{ + struct sctp_opt *sp = sctp_sk(sk); + struct sctp_endpoint *ep = sp->ep; + + /* Only UDP style sockets that are not peeled off are allowed to + * listen(). + */ + if (SCTP_SOCKET_UDP != sp->type) + return -EINVAL; + + if (sk->state == SCTP_SS_LISTENING) + return 0; + + /* + * If a bind() or sctp_bindx() is not called prior to a listen() + * call that allows new associations to be accepted, the system + * picks an ephemeral port and will choose an address set equivalent + * to binding with a wildcard address. + * + * This is not currently spelled out in the SCTP sockets + * extensions draft, but follows the practice as seen in TCP + * sockets. + */ + if (!ep->base.bind_addr.port) { + if (sctp_autobind(sk)) + return -EAGAIN; + } + sk->state = SCTP_SS_LISTENING; + sctp_hash_endpoint(ep); + return 0; +} + +/* + * 4.1.3 listen() - TCP Style Syntax + * + * Applications uses listen() to ready the SCTP endpoint for accepting + * inbound associations. + */ +SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog) +{ + struct sctp_opt *sp = sctp_sk(sk); + struct sctp_endpoint *ep = sp->ep; + + if (sk->state == SCTP_SS_LISTENING) + return 0; + + /* + * If a bind() or sctp_bindx() is not called prior to a listen() + * call that allows new associations to be accepted, the system + * picks an ephemeral port and will choose an address set equivalent + * to binding with a wildcard address. + * + * This is not currently spelled out in the SCTP sockets + * extensions draft, but follows the practice as seen in TCP + * sockets. + */ + if (!ep->base.bind_addr.port) { + if (sctp_autobind(sk)) + return -EAGAIN; + } + sk->state = SCTP_SS_LISTENING; + sk->max_ack_backlog = backlog; + sctp_hash_endpoint(ep); + return 0; +} + +/* + * Move a socket to LISTENING state. + */ +int sctp_inet_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + struct crypto_tfm *tfm=NULL; + int err = -EINVAL; + + if (unlikely(backlog < 0)) + goto out; + + sctp_lock_sock(sk); + + if (sock->state != SS_UNCONNECTED) + goto out; + + /* Allocate HMAC for generating cookie. */ + if (sctp_hmac_alg) { + tfm = sctp_crypto_alloc_tfm(sctp_hmac_alg, 0); + if (!tfm) { + err = -ENOSYS; + goto out; + } + } + + switch (sock->type) { + case SOCK_SEQPACKET: + err = sctp_seqpacket_listen(sk, backlog); + break; + case SOCK_STREAM: + err = sctp_stream_listen(sk, backlog); + break; + default: + break; + }; + if (err) + goto cleanup; + + /* Store away the transform reference. */ + sctp_sk(sk)->hmac = tfm; +out: + sctp_release_sock(sk); + return err; +cleanup: + if (tfm) + sctp_crypto_free_tfm(tfm); + goto out; +} + +/* + * This function is done by modeling the current datagram_poll() and the + * tcp_poll(). Note that, based on these implementations, we don't + * lock the socket in this function, even though it seems that, + * ideally, locking or some other mechanisms can be used to ensure + * the integrity of the counters (sndbuf and wmem_queued) used + * in this place. We assume that we don't need locks either until proven + * otherwise. + * + * Another thing to note is that we include the Async I/O support + * here, again, by modeling the current TCP/UDP code. We don't have + * a good way to test with it yet. + */ +unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait) +{ + struct sock *sk = sock->sk; + unsigned int mask; + + poll_wait(file, sk->sleep, wait); + mask = 0; + + /* Is there any exceptional events? */ + if (sk->err || !skb_queue_empty(&sk->error_queue)) + mask |= POLLERR; + if (sk->shutdown == SHUTDOWN_MASK) + mask |= POLLHUP; + + /* Is it readable? Reconsider this code with TCP-style support. */ + if (!skb_queue_empty(&sk->receive_queue) || + (sk->shutdown & RCV_SHUTDOWN)) + mask |= POLLIN | POLLRDNORM; + + /* + * FIXME: We need to set SCTP_SS_DISCONNECTING for TCP-style and + * peeled off sockets. Additionally, TCP-style needs to consider + * other establishment conditions. + */ + if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) { + /* The association is going away. */ + if (SCTP_SS_DISCONNECTING == sk->state) + mask |= POLLHUP; + /* The association is either gone or not ready. */ + if (SCTP_SS_CLOSED == sk->state) + return mask; + } + + /* Is it writable? */ + if (sctp_writeable(sk)) { + mask |= POLLOUT | POLLWRNORM; + } else { + set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags); + /* + * Since the socket is not locked, the buffer + * might be made available after the writeable check and + * before the bit is set. This could cause a lost I/O + * signal. tcp_poll() has a race breaker for this race + * condition. Based on their implementation, we put + * in the following code to cover it as well. + */ + if (sctp_writeable(sk)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsigned short snum) +{ + sctp_bind_bucket_t *pp; + + SCTP_DEBUG_PRINTK( "sctp_bucket_create() begins, snum=%d\n", snum); + pp = kmalloc(sizeof(sctp_bind_bucket_t), GFP_ATOMIC); + if (pp) { + pp->port = snum; + pp->fastreuse = 0; + pp->sk = NULL; + if ((pp->next = head->chain) != NULL) + pp->next->pprev = &pp->next; + head->chain = pp; + pp->pprev = &head->chain; + } + SCTP_DEBUG_PRINTK("sctp_bucket_create() ends, pp=%p\n", pp); + return pp; +} + +/* FIXME: Comments! */ +static __inline__ void __sctp_put_port(struct sock *sk) +{ + struct sctp_protocol *sctp_proto = sctp_get_protocol(); + sctp_bind_hashbucket_t *head = + &sctp_proto->port_hashtable[sctp_phashfn((sk)->num)]; + sctp_bind_bucket_t *pp; + + sctp_spin_lock(&head->lock); + pp = (sctp_bind_bucket_t *) sk->prev; + if (sk->bind_next) + sk->bind_next->bind_pprev = sk->bind_pprev; + *(sk->bind_pprev) = sk->bind_next; + sk->prev = NULL; + (sk)->num = 0; + if (pp->sk) { + if (pp->next) + pp->next->pprev = pp->pprev; + *(pp->pprev) = pp->next; + kfree(pp); + } + sctp_spin_unlock(&head->lock); +} + +void sctp_put_port(struct sock *sk) +{ + sctp_local_bh_disable(); + __sctp_put_port(sk); + sctp_local_bh_enable(); +} + +/* + * The system picks an ephemeral port and choose an address set equivalent + * to binding with a wildcard address. + * One of those addresses will be the primary address for the association. + * This automatically enables the multihoming capability of SCTP. + */ +static int sctp_autobind(struct sock *sk) +{ + union sctp_addr autoaddr; + struct sctp_af *af; + unsigned short port; + + /* Initialize a local sockaddr structure to INADDR_ANY. */ + af = sctp_sk(sk)->pf->af; + + port = htons((sk)->num); + af->inaddr_any(&autoaddr, port); + + return sctp_do_bind(sk, &autoaddr, af->sockaddr_len); +} + +/* Parse out IPPROTO_SCTP CMSG headers. Perform only minimal validation. + * + * From RFC 2292 + * 4.2 The cmsghdr Structure * + * + * When ancillary data is sent or received, any number of ancillary data + * objects can be specified by the msg_control and msg_controllen members of + * the msghdr structure, because each object is preceded by + * a cmsghdr structure defining the object's length (the cmsg_len member). + * Historically Berkeley-derived implementations have passed only one object + * at a time, but this API allows multiple objects to be + * passed in a single call to sendmsg() or recvmsg(). The following example + * shows two ancillary data objects in a control buffer. + * + * |<--------------------------- msg_controllen -------------------------->| + * | | + * + * |<----- ancillary data object ----->|<----- ancillary data object ----->| + * + * |<---------- CMSG_SPACE() --------->|<---------- CMSG_SPACE() --------->| + * | | | + * + * |<---------- cmsg_len ---------->| |<--------- cmsg_len ----------->| | + * + * |<--------- CMSG_LEN() --------->| |<-------- CMSG_LEN() ---------->| | + * | | | | | + * + * +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+ + * |cmsg_|cmsg_|cmsg_|XX| |XX|cmsg_|cmsg_|cmsg_|XX| |XX| + * + * |len |level|type |XX|cmsg_data[]|XX|len |level|type |XX|cmsg_data[]|XX| + * + * +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+ + * ^ + * | + * + * msg_control + * points here + */ +SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg, + sctp_cmsgs_t *cmsgs) +{ + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) { + /* Check for minimum length. The SCM code has this check. */ + if (cmsg->cmsg_len < sizeof(struct cmsghdr) || + (unsigned long)(((char*)cmsg - (char*)msg->msg_control) + + cmsg->cmsg_len) > msg->msg_controllen) { + return -EINVAL; + } + + /* Should we parse this header or ignore? */ + if (cmsg->cmsg_level != IPPROTO_SCTP) + continue; + + /* Strictly check lengths following example in SCM code. */ + switch (cmsg->cmsg_type) { + case SCTP_INIT: + /* SCTP Socket API Extension + * 5.2.1 SCTP Initiation Structure (SCTP_INIT) + * + * This cmsghdr structure provides information for + * initializing new SCTP associations with sendmsg(). + * The SCTP_INITMSG socket option uses this same data + * structure. This structure is not used for + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg + */ + if (cmsg->cmsg_len != + CMSG_LEN(sizeof(struct sctp_initmsg))) + return -EINVAL; + cmsgs->init = (struct sctp_initmsg *)CMSG_DATA(cmsg); + break; + + case SCTP_SNDRCV: + /* SCTP Socket API Extension + * 5.2.2 SCTP Header Information Structure(SCTP_SNDRCV) + * + * This cmsghdr structure specifies SCTP options for + * sendmsg() and describes SCTP header information + * about a received message through recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo + */ + if (cmsg->cmsg_len != + CMSG_LEN(sizeof(struct sctp_sndrcvinfo))) + return -EINVAL; + + cmsgs->info = + (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); + + /* Minimally, validate the sinfo_flags. */ + if (cmsgs->info->sinfo_flags & + ~(MSG_UNORDERED | MSG_ADDR_OVER | + MSG_ABORT | MSG_EOF)) + return -EINVAL; + break; + + default: + return -EINVAL; + }; + } + return 0; +} + +/* + * Wait for a packet.. + * Note: This function is the same function as in core/datagram.c + * with a few modifications to make lksctp work. + */ +static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p) +{ + int error; + DECLARE_WAITQUEUE(wait, current); + + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue_exclusive(sk->sleep, &wait); + + /* Socket errors? */ + error = sock_error(sk); + if (error) + goto out; + + if (!skb_queue_empty(&sk->receive_queue)) + goto ready; + + /* Socket shut down? */ + if (sk->shutdown & RCV_SHUTDOWN) + goto out; + + /* Sequenced packets can come disconnected. If so we report the + * problem. + */ + error = -ENOTCONN; + + /* Is there a good reason to think that we may receive some data? */ + if ((list_empty(&sctp_sk(sk)->ep->asocs)) && + (sk->state != SCTP_SS_LISTENING)) + goto out; + + /* Handle signals. */ + if (signal_pending(current)) + goto interrupted; + + /* Let another process have a go. Since we are going to sleep + * anyway. Note: This may cause odd behaviors if the message + * does not fit in the user's buffer, but this seems to be the + * only way to honor MSG_DONTWAIT realistically. + */ + sctp_release_sock(sk); + *timeo_p = schedule_timeout(*timeo_p); + sctp_lock_sock(sk); + +ready: + remove_wait_queue(sk->sleep, &wait); + __set_current_state(TASK_RUNNING); + return 0; + +interrupted: + error = sock_intr_errno(*timeo_p); + +out: + remove_wait_queue(sk->sleep, &wait); + __set_current_state(TASK_RUNNING); + *err = error; + return error; +} + +/* Receive a datagram. + * Note: This is pretty much the same routine as in core/datagram.c + * with a few changes to make lksctp work. + */ +static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, + int noblock, int *err) +{ + int error; + struct sk_buff *skb; + long timeo; + + /* Caller is allowed not to check sk->err before calling. */ + error = sock_error(sk); + if (error) + goto no_packet; + + timeo = sock_rcvtimeo(sk, noblock); + + SCTP_DEBUG_PRINTK("Timeout: timeo: %ld, MAX: %ld.\n", + timeo, MAX_SCHEDULE_TIMEOUT); + + do { + /* Again only user level code calls this function, + * so nothing interrupt level + * will suddenly eat the receive_queue. + * + * Look at current nfs client by the way... + * However, this function was corrent in any case. 8) + */ + if (flags & MSG_PEEK) { + unsigned long cpu_flags; + + sctp_spin_lock_irqsave(&sk->receive_queue.lock, + cpu_flags); + skb = skb_peek(&sk->receive_queue); + if (skb) + atomic_inc(&skb->users); + sctp_spin_unlock_irqrestore(&sk->receive_queue.lock, + cpu_flags); + } else { + skb = skb_dequeue(&sk->receive_queue); + } + + if (skb) + return skb; + + if (sk->shutdown & RCV_SHUTDOWN) + break; + + /* User doesn't want to wait. */ + error = -EAGAIN; + if (!timeo) + goto no_packet; + } while (sctp_wait_for_packet(sk, err, &timeo) == 0); + + return NULL; + +no_packet: + *err = error; + return NULL; +} + +/* Verify that this is a valid address. */ +static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, + int len) +{ + struct sctp_af *af; + + /* Verify basic sockaddr. */ + af = sctp_sockaddr_af(sctp_sk(sk), addr, len); + if (!af) + return -EINVAL; + + /* Is this a valid SCTP address? */ + if (!af->addr_valid(addr)) + return -EINVAL; + + if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr))) + return -EINVAL; + + return 0; +} + +/* Get the sndbuf space available at the time on the association. */ +static inline int sctp_wspace(struct sctp_association *asoc) +{ + struct sock *sk = asoc->base.sk; + int amt = 0; + + amt = sk->sndbuf - asoc->sndbuf_used; + if (amt < 0) + amt = 0; + return amt; +} + +/* Increment the used sndbuf space count of the corresponding association by + * the size of the outgoing data chunk. + * Also, set the skb destructor for sndbuf accounting later. + * + * Since it is always 1-1 between chunk and skb, and also a new skb is always + * allocated for chunk bundling in sctp_packet_transmit(), we can use the + * destructor in the data chunk skb for the purpose of the sndbuf space + * tracking. + */ +static inline void sctp_set_owner_w(sctp_chunk_t *chunk) +{ + struct sctp_association *asoc = chunk->asoc; + struct sock *sk = asoc->base.sk; + + /* The sndbuf space is tracked per association. */ + sctp_association_hold(asoc); + + chunk->skb->destructor = sctp_wfree; + /* Save the chunk pointer in skb for sctp_wfree to use later. */ + *((sctp_chunk_t **)(chunk->skb->cb)) = chunk; + + asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk); + sk->wmem_queued += SCTP_DATA_SNDSIZE(chunk); +} + +/* If sndbuf has changed, wake up per association sndbuf waiters. */ +static void __sctp_write_space(struct sctp_association *asoc) +{ + struct sock *sk = asoc->base.sk; + struct socket *sock = sk->socket; + + if ((sctp_wspace(asoc) > 0) && sock) { + if (waitqueue_active(&asoc->wait)) + wake_up_interruptible(&asoc->wait); + + if (sctp_writeable(sk)) { + if (sk->sleep && waitqueue_active(sk->sleep)) + wake_up_interruptible(sk->sleep); + + /* Note that we try to include the Async I/O support + * here by modeling from the current TCP/UDP code. + * We have not tested with it yet. + */ + if (sock->fasync_list && + !(sk->shutdown & SEND_SHUTDOWN)) + sock_wake_async(sock, 2, POLL_OUT); + } + } +} + +/* Do accounting for the sndbuf space. + * Decrement the used sndbuf space of the corresponding association by the + * data size which was just transmitted(freed). + */ +static void sctp_wfree(struct sk_buff *skb) +{ + struct sctp_association *asoc; + sctp_chunk_t *chunk; + struct sock *sk; + + /* Get the saved chunk pointer. */ + chunk = *((sctp_chunk_t **)(skb->cb)); + asoc = chunk->asoc; + sk = asoc->base.sk; + asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk); + sk->wmem_queued -= SCTP_DATA_SNDSIZE(chunk); + __sctp_write_space(asoc); + + sctp_association_put(asoc); +} + +/* Helper function to wait for space in the sndbuf. */ +static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, + int msg_len) +{ + struct sock *sk = asoc->base.sk; + int err = 0; + long current_timeo = *timeo_p; + DECLARE_WAITQUEUE(wait, current); + + SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%d\n", + asoc, (long)(*timeo_p), msg_len); + + /* Wait on the association specific sndbuf space. */ + add_wait_queue_exclusive(&asoc->wait, &wait); + + /* Increment the association's refcnt. */ + sctp_association_hold(asoc); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!*timeo_p) + goto do_nonblock; + if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING || + asoc->base.dead) + goto do_error; + if (signal_pending(current)) + goto do_interrupted; + if (msg_len <= sctp_wspace(asoc)) + break; + + /* Let another process have a go. Since we are going + * to sleep anyway. + */ + sctp_release_sock(sk); + current_timeo = schedule_timeout(current_timeo); + sctp_lock_sock(sk); + + *timeo_p = current_timeo; + } + +out: + remove_wait_queue(&asoc->wait, &wait); + + /* Release the association's refcnt. */ + sctp_association_put(asoc); + + __set_current_state(TASK_RUNNING); + return err; + +do_error: + err = -EPIPE; + goto out; + +do_interrupted: + err = sock_intr_errno(*timeo_p); + goto out; + +do_nonblock: + err = -EAGAIN; + goto out; +} + +/* If socket sndbuf has changed, wake up all per association waiters. */ +void sctp_write_space(struct sock *sk) +{ + struct sctp_association *asoc; + struct list_head *pos; + + /* Wake up the tasks in each wait queue. */ + list_for_each(pos, &((sctp_sk(sk))->ep->asocs)) { + asoc = list_entry(pos, struct sctp_association, asocs); + __sctp_write_space(asoc); + } +} + +/* Is there any sndbuf space available on the socket? + * + * Note that wmem_queued is the sum of the send buffers on all of the + * associations on the same socket. For a UDP-style socket with + * multiple associations, it is possible for it to be "unwriteable" + * prematurely. I assume that this is acceptable because + * a premature "unwriteable" is better than an accidental "writeable" which + * would cause an unwanted block under certain circumstances. For the 1-1 + * UDP-style sockets or TCP-style sockets, this code should work. + * - Daisy + */ +static int sctp_writeable(struct sock *sk) +{ + int amt = 0; + + amt = sk->sndbuf - sk->wmem_queued; + if (amt < 0) + amt = 0; + return amt; +} + +/* Wait for an association to go into ESTABLISHED state. If timeout is 0, + * returns immediately with EINPROGRESS. + */ +static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) +{ + struct sock *sk = asoc->base.sk; + int err = 0; + long current_timeo = *timeo_p; + DECLARE_WAITQUEUE(wait, current); + + SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __FUNCTION__, asoc, + (long)(*timeo_p)); + + add_wait_queue_exclusive(&asoc->wait, &wait); + + /* Increment the association's refcnt. */ + sctp_association_hold(asoc); + + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if (!*timeo_p) + goto do_nonblock; + if (sk->shutdown & RCV_SHUTDOWN) + break; + if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING || + asoc->base.dead) + goto do_error; + if (signal_pending(current)) + goto do_interrupted; + + if (asoc->state == SCTP_STATE_ESTABLISHED) + break; + + /* Let another process have a go. Since we are going + * to sleep anyway. + */ + sctp_release_sock(sk); + current_timeo = schedule_timeout(current_timeo); + sctp_lock_sock(sk); + + *timeo_p = current_timeo; + } + +out: + remove_wait_queue(&asoc->wait, &wait); + + /* Release the association's refcnt. */ + sctp_association_put(asoc); + + __set_current_state(TASK_RUNNING); + + return err; + +do_error: + err = -ECONNREFUSED; + goto out; + +do_interrupted: + err = sock_intr_errno(*timeo_p); + goto out; + +do_nonblock: + err = -EINPROGRESS; + goto out; +} + +static int sctp_wait_for_accept(struct sock *sk, long timeo) +{ + struct sctp_endpoint *ep; + int err = 0; + DECLARE_WAITQUEUE(wait, current); + + ep = sctp_sk(sk)->ep; + + add_wait_queue_exclusive(sk->sleep, &wait); + + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if (list_empty(&ep->asocs)) { + sctp_release_sock(sk); + timeo = schedule_timeout(timeo); + sctp_lock_sock(sk); + } + + err = -EINVAL; + if (sk->state != SCTP_SS_LISTENING) + break; + + err = 0; + if (!list_empty(&ep->asocs)) + break; + + err = sock_intr_errno(timeo); + if (signal_pending(current)) + break; + + err = -EAGAIN; + if (!timeo) + break; + } + + remove_wait_queue(sk->sleep, &wait); + __set_current_state(TASK_RUNNING); + + return err; +} + +/* Populate the fields of the newsk from the oldsk and migrate the assoc + * and its messages to the newsk. + */ +static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, + struct sctp_association *assoc, + sctp_socket_type_t type) +{ + struct sctp_opt *oldsp = sctp_sk(oldsk); + struct sctp_opt *newsp = sctp_sk(newsk); + struct sctp_endpoint *newep = newsp->ep; + struct sk_buff *skb, *tmp; + struct sctp_ulpevent *event; + + /* Migrate socket buffer sizes and all the socket level options to the + * new socket. + */ + newsk->sndbuf = oldsk->sndbuf; + newsk->rcvbuf = oldsk->rcvbuf; + *newsp = *oldsp; + + /* Restore the ep value that was overwritten with the above structure + * copy. + */ + newsp->ep = newep; + newsp->hmac = NULL; + + /* Move any messages in the old socket's receive queue that are for the + * peeled off association to the new socket's receive queue. + */ + sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) { + event = sctp_skb2event(skb); + if (event->event_asoc == assoc) { + __skb_unlink(skb, skb->list); + __skb_queue_tail(&newsk->receive_queue, skb); + } + } + + /* Clean up any messages pending delivery due to partial + * delivery. Three cases: + * 1) No partial deliver; no work. + * 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby. + * 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue. + */ + skb_queue_head_init(&newsp->pd_lobby); + sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;; + + if (sctp_sk(oldsk)->pd_mode) { + struct sk_buff_head *queue; + + /* Decide which queue to move pd_lobby skbs to. */ + if (assoc->ulpq.pd_mode) { + queue = &newsp->pd_lobby; + } else + queue = &newsk->receive_queue; + + /* Walk through the pd_lobby, looking for skbs that + * need moved to the new socket. + */ + sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) { + event = sctp_skb2event(skb); + if (event->event_asoc == assoc) { + __skb_unlink(skb, skb->list); + __skb_queue_tail(queue, skb); + } + } + + /* Clear up any skbs waiting for the partial + * delivery to finish. + */ + if (assoc->ulpq.pd_mode) + sctp_clear_pd(oldsk); + + } + + /* Set the type of socket to indicate that it is peeled off from the + * original UDP-style socket or created with the accept() call on a + * TCP-style socket.. + */ + newsp->type = type; + + /* Migrate the association to the new socket. */ + sctp_assoc_migrate(assoc, newsk); + + newsk->state = SCTP_SS_ESTABLISHED; +} + +/* This proto struct describes the ULP interface for SCTP. */ +struct proto sctp_prot = { + .name = "SCTP", + .close = sctp_close, + .connect = sctp_connect, + .disconnect = sctp_disconnect, + .accept = sctp_accept, + .ioctl = sctp_ioctl, + .init = sctp_init_sock, + .destroy = sctp_destroy_sock, + .shutdown = sctp_shutdown, + .setsockopt = sctp_setsockopt, + .getsockopt = sctp_getsockopt, + .sendmsg = sctp_sendmsg, + .recvmsg = sctp_recvmsg, + .bind = sctp_bind, + .backlog_rcv = sctp_backlog_rcv, + .hash = sctp_hash, + .unhash = sctp_unhash, + .get_port = sctp_get_port, +}; diff -urN linux-2.4.22-bk23/net/sctp/ssnmap.c linux-2.4.22-bk24/net/sctp/ssnmap.c --- linux-2.4.22-bk23/net/sctp/ssnmap.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/ssnmap.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,113 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 2003 International Business Machines, Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions manipulate sctp SSN tracker. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include + + +/* Storage size needed for map includes 2 headers and then the + * specific needs of in or out streams. + */ +static inline size_t sctp_ssnmap_size(__u16 in, __u16 out) +{ + return sizeof(struct sctp_ssnmap) + (in + out) * sizeof(__u16); +} + + +/* Create a new sctp_ssnmap. + * Allocate room to store at least 'len' contiguous TSNs. + */ +struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int gfp) +{ + struct sctp_ssnmap *retval; + + retval = kmalloc(sctp_ssnmap_size(in, out), gfp); + + if (!retval) + goto fail; + + if (!sctp_ssnmap_init(retval, in, out)) + goto fail_map; + + retval->malloced = 1; + SCTP_DBG_OBJCNT_INC(ssnmap); + + return retval; + +fail_map: + kfree(retval); +fail: + return NULL; +} + + +/* Initialize a block of memory as a ssnmap. */ +struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in, + __u16 out) +{ + memset(map, 0x00, sctp_ssnmap_size(in, out)); + + /* Start 'in' stream just after the map header. */ + map->in.ssn = (__u16 *)&map[1]; + map->in.len = in; + + /* Start 'out' stream just after 'in'. */ + map->out.ssn = &map->in.ssn[in]; + map->out.len = out; + + return map; +} + +/* Clear out the ssnmap streams. */ +void sctp_ssnmap_clear(struct sctp_ssnmap *map) +{ + size_t size; + + size = (map->in.len + map->out.len) * sizeof(__u16); + memset(map->in.ssn, 0x00, size); +} + +/* Dispose of a ssnmap. */ +void sctp_ssnmap_free(struct sctp_ssnmap *map) +{ + if (map && map->malloced) { + kfree(map); + SCTP_DBG_OBJCNT_DEC(ssnmap); + } +} diff -urN linux-2.4.22-bk23/net/sctp/sysctl.c linux-2.4.22-bk24/net/sctp/sysctl.c --- linux-2.4.22-bk23/net/sctp/sysctl.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/sysctl.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,187 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 2002 International Business Machines Corp. + * Copyright (c) 2002 Intel Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * Sysctl related interfaces for SCTP. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Mingqin Liu + * Jon Grimm + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include + +extern struct sctp_protocol sctp_proto; + +static ctl_table sctp_table[] = { + { + .ctl_name = NET_SCTP_RTO_INITIAL, + .procname = "rto_initial", + .data = &sctp_proto.rto_initial, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies + }, + { + .ctl_name = NET_SCTP_RTO_MIN, + .procname = "rto_min", + .data = &sctp_proto.rto_min, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies + }, + { + .ctl_name = NET_SCTP_RTO_MAX, + .procname = "rto_max", + .data = &sctp_proto.rto_max, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies + }, + { + .ctl_name = NET_SCTP_VALID_COOKIE_LIFE, + .procname = "valid_cookie_life", + .data = &sctp_proto.valid_cookie_life, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies + }, + { + .ctl_name = NET_SCTP_MAX_BURST, + .procname = "max_burst", + .data = &sctp_proto.max_burst, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = NET_SCTP_ASSOCIATION_MAX_RETRANS, + .procname = "association_max_retrans", + .data = &sctp_proto.max_retrans_association, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = NET_SCTP_PATH_MAX_RETRANS, + .procname = "path_max_retrans", + .data = &sctp_proto.max_retrans_path, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = NET_SCTP_MAX_INIT_RETRANSMITS, + .procname = "max_init_retransmits", + .data = &sctp_proto.max_retrans_init, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = NET_SCTP_HB_INTERVAL, + .procname = "hb_interval", + .data = &sctp_proto.hb_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies + }, + { + .ctl_name = NET_SCTP_PRESERVE_ENABLE, + .procname = "cookie_preserve_enable", + .data = &sctp_proto.cookie_preserve_enable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies + }, + { + .ctl_name = NET_SCTP_RTO_ALPHA, + .procname = "rto_alpha_exp_divisor", + .data = &sctp_proto.rto_alpha, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = NET_SCTP_RTO_BETA, + .procname = "rto_beta_exp_divisor", + .data = &sctp_proto.rto_beta, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { .ctl_name = 0 } +}; + +static ctl_table sctp_net_table[] = { + { + .ctl_name = NET_SCTP, + .procname = "sctp", + .maxlen = 0, + .mode = 0555, + .child = sctp_table + }, + { .ctl_name = 0 } +}; + +static ctl_table sctp_root_table[] = { + { + .ctl_name = CTL_NET, + .procname = "net", + .maxlen = 0, + .mode = 0555, + .child = sctp_net_table + }, + { .ctl_name = 0 } +}; + +static struct ctl_table_header * sctp_sysctl_header; + +/* Sysctl registration. */ +void sctp_sysctl_register(void) +{ + sctp_sysctl_header = register_sysctl_table(sctp_root_table, 0); +} + +/* Sysctl deregistration. */ +void sctp_sysctl_unregister(void) +{ + unregister_sysctl_table(sctp_sysctl_header); +} diff -urN linux-2.4.22-bk23/net/sctp/transport.c linux-2.4.22-bk24/net/sctp/transport.c --- linux-2.4.22-bk23/net/sctp/transport.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/transport.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,500 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This file is part of the SCTP kernel reference Implementation + * + * This module provides the abstraction for an SCTP tranport representing + * a remote transport address. For local transport addresses, we just use + * union sctp_addr. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Jon Grimm + * Xingang Guo + * Hui Huang + * Sridhar Samudrala + * Ardelle Fan + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include + +/* 1st Level Abstractions. */ + +/* Allocate and initialize a new transport. */ +struct sctp_transport *sctp_transport_new(const union sctp_addr *addr, int gfp) +{ + struct sctp_transport *transport; + + transport = t_new(struct sctp_transport, gfp); + if (!transport) + goto fail; + + if (!sctp_transport_init(transport, addr, gfp)) + goto fail_init; + + transport->malloced = 1; + SCTP_DBG_OBJCNT_INC(transport); + + return transport; + +fail_init: + kfree(transport); + +fail: + return NULL; +} + +/* Initialize a new transport from provided memory. */ +struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, + const union sctp_addr *addr, + int gfp) +{ + struct sctp_protocol *proto = sctp_get_protocol(); + + /* Copy in the address. */ + peer->ipaddr = *addr; + peer->af_specific = sctp_get_af_specific(addr->sa.sa_family); + peer->asoc = NULL; + + peer->dst = NULL; + memset(&peer->saddr, 0, sizeof(union sctp_addr)); + + /* From 6.3.1 RTO Calculation: + * + * C1) Until an RTT measurement has been made for a packet sent to the + * given destination transport address, set RTO to the protocol + * parameter 'RTO.Initial'. + */ + peer->rtt = 0; + peer->rto = proto->rto_initial; + peer->rttvar = 0; + peer->srtt = 0; + peer->rto_pending = 0; + + peer->last_time_heard = jiffies; + peer->last_time_used = jiffies; + peer->last_time_ecne_reduced = jiffies; + + peer->active = 1; + peer->hb_allowed = 0; + + /* Initialize the default path max_retrans. */ + peer->max_retrans = proto->max_retrans_path; + peer->error_threshold = 0; + peer->error_count = 0; + + peer->debug_name = "unnamedtransport"; + + INIT_LIST_HEAD(&peer->transmitted); + INIT_LIST_HEAD(&peer->send_ready); + INIT_LIST_HEAD(&peer->transports); + + /* Set up the retransmission timer. */ + init_timer(&peer->T3_rtx_timer); + peer->T3_rtx_timer.function = sctp_generate_t3_rtx_event; + peer->T3_rtx_timer.data = (unsigned long)peer; + + /* Set up the heartbeat timer. */ + init_timer(&peer->hb_timer); + peer->hb_interval = SCTP_DEFAULT_TIMEOUT_HEARTBEAT; + peer->hb_timer.function = sctp_generate_heartbeat_event; + peer->hb_timer.data = (unsigned long)peer; + + atomic_set(&peer->refcnt, 1); + peer->dead = 0; + + peer->malloced = 0; + return peer; +} + +/* This transport is no longer needed. Free up if possible, or + * delay until it last reference count. + */ +void sctp_transport_free(struct sctp_transport *transport) +{ + transport->dead = 1; + + /* Try to delete the heartbeat timer. */ + if (del_timer(&transport->hb_timer)) + sctp_transport_put(transport); + + sctp_transport_put(transport); +} + +/* Destroy the transport data structure. + * Assumes there are no more users of this structure. + */ +void sctp_transport_destroy(struct sctp_transport *transport) +{ + SCTP_ASSERT(transport->dead, "Transport is not dead", return); + + if (transport->asoc) + sctp_association_put(transport->asoc); + + dst_release(transport->dst); + kfree(transport); + SCTP_DBG_OBJCNT_DEC(transport); +} + +/* Start T3_rtx timer if it is not already running and update the heartbeat + * timer. This routine is called every time a DATA chunk is sent. + */ +void sctp_transport_reset_timers(struct sctp_transport *transport) +{ + /* RFC 2960 6.3.2 Retransmission Timer Rules + * + * R1) Every time a DATA chunk is sent to any address(including a + * retransmission), if the T3-rtx timer of that address is not running + * start it running so that it will expire after the RTO of that + * address. + */ + + if (!timer_pending(&transport->T3_rtx_timer)) + if (!mod_timer(&transport->T3_rtx_timer, + jiffies + transport->rto)) + sctp_transport_hold(transport); + + /* When a data chunk is sent, reset the heartbeat interval. */ + if (!mod_timer(&transport->hb_timer, + sctp_transport_timeout(transport))) + sctp_transport_hold(transport); +} + +/* This transport has been assigned to an association. + * Initialize fields from the association or from the sock itself. + * Register the reference count in the association. + */ +void sctp_transport_set_owner(struct sctp_transport *transport, + struct sctp_association *asoc) +{ + transport->asoc = asoc; + sctp_association_hold(asoc); +} + +/* Initialize the pmtu of a transport. */ +void sctp_transport_pmtu(struct sctp_transport *transport) +{ + struct dst_entry *dst; + + dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL); + + if (dst) { + transport->pmtu = dst_pmtu(dst); + dst_release(dst); + } else + transport->pmtu = SCTP_DEFAULT_MAXSEGMENT; +} + +/* Caches the dst entry and source address for a transport's destination + * address. + */ +void sctp_transport_route(struct sctp_transport *transport, + union sctp_addr *saddr, struct sctp_opt *opt) +{ + struct sctp_association *asoc = transport->asoc; + struct sctp_af *af = transport->af_specific; + union sctp_addr *daddr = &transport->ipaddr; + struct dst_entry *dst; + + dst = af->get_dst(asoc, daddr, saddr); + + if (saddr) + memcpy(&transport->saddr, saddr, sizeof(union sctp_addr)); + else + af->get_saddr(asoc, dst, daddr, &transport->saddr); + + transport->dst = dst; + if (dst) { + transport->pmtu = dst_pmtu(dst); + + /* Initialize sk->rcv_saddr, if the transport is the + * association's active path for getsockname(). + */ + if (asoc && (transport == asoc->peer.active_path)) + af->to_sk_saddr(&transport->saddr, asoc->base.sk); + } else + transport->pmtu = SCTP_DEFAULT_MAXSEGMENT; +} + +/* Hold a reference to a transport. */ +void sctp_transport_hold(struct sctp_transport *transport) +{ + atomic_inc(&transport->refcnt); +} + +/* Release a reference to a transport and clean up + * if there are no more references. + */ +void sctp_transport_put(struct sctp_transport *transport) +{ + if (atomic_dec_and_test(&transport->refcnt)) + sctp_transport_destroy(transport); +} + +/* Update transport's RTO based on the newly calculated RTT. */ +void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) +{ + struct sctp_protocol *proto = sctp_get_protocol(); + + /* Check for valid transport. */ + SCTP_ASSERT(tp, "NULL transport", return); + + /* We should not be doing any RTO updates unless rto_pending is set. */ + SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return); + + if (tp->rttvar || tp->srtt) { + /* 6.3.1 C3) When a new RTT measurement R' is made, set + * RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'| + * SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R' + */ + + /* Note: The above algorithm has been rewritten to + * express rto_beta and rto_alpha as inverse powers + * of two. + * For example, assuming the default value of RTO.Alpha of + * 1/8, rto_alpha would be expressed as 3. + */ + tp->rttvar = tp->rttvar - (tp->rttvar >> proto->rto_beta) + + ((abs(tp->srtt - rtt)) >> proto->rto_beta); + tp->srtt = tp->srtt - (tp->srtt >> proto->rto_alpha) + + (rtt >> proto->rto_alpha); + } else { + /* 6.3.1 C2) When the first RTT measurement R is made, set + * SRTT <- R, RTTVAR <- R/2. + */ + tp->srtt = rtt; + tp->rttvar = rtt >> 1; + } + + /* 6.3.1 G1) Whenever RTTVAR is computed, if RTTVAR = 0, then + * adjust RTTVAR <- G, where G is the CLOCK GRANULARITY. + */ + if (tp->rttvar == 0) + tp->rttvar = SCTP_CLOCK_GRANULARITY; + + /* 6.3.1 C3) After the computation, update RTO <- SRTT + 4 * RTTVAR. */ + tp->rto = tp->srtt + (tp->rttvar << 2); + + /* 6.3.1 C6) Whenever RTO is computed, if it is less than RTO.Min + * seconds then it is rounded up to RTO.Min seconds. + */ + if (tp->rto < tp->asoc->rto_min) + tp->rto = tp->asoc->rto_min; + + /* 6.3.1 C7) A maximum value may be placed on RTO provided it is + * at least RTO.max seconds. + */ + if (tp->rto > tp->asoc->rto_max) + tp->rto = tp->asoc->rto_max; + + tp->rtt = rtt; + + /* Reset rto_pending so that a new RTT measurement is started when a + * new data chunk is sent. + */ + tp->rto_pending = 0; + + SCTP_DEBUG_PRINTK("%s: transport: %p, rtt: %d, srtt: %d " + "rttvar: %d, rto: %d\n", __FUNCTION__, + tp, rtt, tp->srtt, tp->rttvar, tp->rto); +} + +/* This routine updates the transport's cwnd and partial_bytes_acked + * parameters based on the bytes acked in the received SACK. + */ +void sctp_transport_raise_cwnd(struct sctp_transport *transport, + __u32 sack_ctsn, __u32 bytes_acked) +{ + __u32 cwnd, ssthresh, flight_size, pba, pmtu; + + cwnd = transport->cwnd; + flight_size = transport->flight_size; + + /* The appropriate cwnd increase algorithm is performed if, and only + * if the cumulative TSN has advanced and the congestion window is + * being fully utilized. + */ + if ((transport->asoc->ctsn_ack_point >= sack_ctsn) || + (flight_size < cwnd)) + return; + + ssthresh = transport->ssthresh; + pba = transport->partial_bytes_acked; + pmtu = transport->asoc->pmtu; + + if (cwnd <= ssthresh) { + /* RFC 2960 7.2.1, sctpimpguide-05 2.14.2 When cwnd is less + * than or equal to ssthresh an SCTP endpoint MUST use the + * slow start algorithm to increase cwnd only if the current + * congestion window is being fully utilized and an incoming + * SACK advances the Cumulative TSN Ack Point. Only when these + * two conditions are met can the cwnd be increased otherwise + * the cwnd MUST not be increased. If these conditions are met + * then cwnd MUST be increased by at most the lesser of + * 1) the total size of the previously outstanding DATA + * chunk(s) acknowledged, and 2) the destination's path MTU. + */ + if (bytes_acked > pmtu) + cwnd += pmtu; + else + cwnd += bytes_acked; + SCTP_DEBUG_PRINTK("%s: SLOW START: transport: %p, " + "bytes_acked: %d, cwnd: %d, ssthresh: %d, " + "flight_size: %d, pba: %d\n", + __FUNCTION__, + transport, bytes_acked, cwnd, + ssthresh, flight_size, pba); + } else { + /* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh, + * upon each SACK arrival that advances the Cumulative TSN Ack + * Point, increase partial_bytes_acked by the total number of + * bytes of all new chunks acknowledged in that SACK including + * chunks acknowledged by the new Cumulative TSN Ack and by + * Gap Ack Blocks. + * + * When partial_bytes_acked is equal to or greater than cwnd + * and before the arrival of the SACK the sender had cwnd or + * more bytes of data outstanding (i.e., before arrival of the + * SACK, flightsize was greater than or equal to cwnd), + * increase cwnd by MTU, and reset partial_bytes_acked to + * (partial_bytes_acked - cwnd). + */ + pba += bytes_acked; + if (pba >= cwnd) { + cwnd += pmtu; + pba = ((cwnd < pba) ? (pba - cwnd) : 0); + } + SCTP_DEBUG_PRINTK("%s: CONGESTION AVOIDANCE: " + "transport: %p, bytes_acked: %d, cwnd: %d, " + "ssthresh: %d, flight_size: %d, pba: %d\n", + __FUNCTION__, + transport, bytes_acked, cwnd, + ssthresh, flight_size, pba); + } + + transport->cwnd = cwnd; + transport->partial_bytes_acked = pba; +} + +/* This routine is used to lower the transport's cwnd when congestion is + * detected. + */ +void sctp_transport_lower_cwnd(struct sctp_transport *transport, + sctp_lower_cwnd_t reason) +{ + switch (reason) { + case SCTP_LOWER_CWND_T3_RTX: + /* RFC 2960 Section 7.2.3, sctpimpguide-05 Section 2.9.2 + * When the T3-rtx timer expires on an address, SCTP should + * perform slow start by: + * ssthresh = max(cwnd/2, 2*MTU) + * cwnd = 1*MTU + * partial_bytes_acked = 0 + */ + transport->ssthresh = max(transport->cwnd/2, + 2*transport->asoc->pmtu); + transport->cwnd = transport->asoc->pmtu; + break; + + case SCTP_LOWER_CWND_FAST_RTX: + /* RFC 2960 7.2.4 Adjust the ssthresh and cwnd of the + * destination address(es) to which the missing DATA chunks + * were last sent, according to the formula described in + * Section 7.2.3. + * + * RFC 2960 7.2.3, sctpimpguide-05 2.9.2 Upon detection of + * packet losses from SACK (see Section 7.2.4), An endpoint + * should do the following: + * ssthresh = max(cwnd/2, 2*MTU) + * cwnd = ssthresh + * partial_bytes_acked = 0 + */ + transport->ssthresh = max(transport->cwnd/2, + 2*transport->asoc->pmtu); + transport->cwnd = transport->ssthresh; + break; + + case SCTP_LOWER_CWND_ECNE: + /* RFC 2481 Section 6.1.2. + * If the sender receives an ECN-Echo ACK packet + * then the sender knows that congestion was encountered in the + * network on the path from the sender to the receiver. The + * indication of congestion should be treated just as a + * congestion loss in non-ECN Capable TCP. That is, the TCP + * source halves the congestion window "cwnd" and reduces the + * slow start threshold "ssthresh". + * A critical condition is that TCP does not react to + * congestion indications more than once every window of + * data (or more loosely more than once every round-trip time). + */ + if ((jiffies - transport->last_time_ecne_reduced) > + transport->rtt) { + transport->ssthresh = max(transport->cwnd/2, + 2*transport->asoc->pmtu); + transport->cwnd = transport->ssthresh; + transport->last_time_ecne_reduced = jiffies; + } + break; + + case SCTP_LOWER_CWND_INACTIVE: + /* RFC 2960 Section 7.2.1, sctpimpguide-05 Section 2.14.2 + * When the association does not transmit data on a given + * transport address within an RTO, the cwnd of the transport + * address should be adjusted to 2*MTU. + * NOTE: Although the draft recommends that this check needs + * to be done every RTO interval, we do it every hearbeat + * interval. + */ + if ((jiffies - transport->last_time_used) > transport->rto) + transport->cwnd = 2*transport->asoc->pmtu; + break; + }; + + transport->partial_bytes_acked = 0; + SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: " + "%d ssthresh: %d\n", __FUNCTION__, + transport, reason, + transport->cwnd, transport->ssthresh); +} + +/* What is the next timeout value for this transport? */ +unsigned long sctp_transport_timeout(struct sctp_transport *t) +{ + unsigned long timeout; + timeout = t->hb_interval + t->rto + sctp_jitter(t->rto); + timeout += jiffies; + return timeout; +} diff -urN linux-2.4.22-bk23/net/sctp/tsnmap.c linux-2.4.22-bk24/net/sctp/tsnmap.c --- linux-2.4.22-bk23/net/sctp/tsnmap.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/tsnmap.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,406 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * These functions manipulate sctp tsn mapping array. + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * Jon Grimm + * Karl Knutson + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include + +static void sctp_tsnmap_update(struct sctp_tsnmap *map); +static void sctp_tsnmap_update_pending_data(struct sctp_tsnmap *map); +static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off, + __u16 len, __u16 base, + int *started, __u16 *start, + int *ended, __u16 *end); + +/* Create a new sctp_tsnmap. + * Allocate room to store at least 'len' contiguous TSNs. + */ +struct sctp_tsnmap *sctp_tsnmap_new(__u16 len, __u32 initial_tsn, int gfp) +{ + struct sctp_tsnmap *retval; + + retval = kmalloc(sizeof(struct sctp_tsnmap) + + sctp_tsnmap_storage_size(len), gfp); + if (!retval) + goto fail; + + if (!sctp_tsnmap_init(retval, len, initial_tsn)) + goto fail_map; + retval->malloced = 1; + return retval; + +fail_map: + kfree(retval); +fail: + return NULL; +} + +/* Initialize a block of memory as a tsnmap. */ +struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *map, __u16 len, + __u32 initial_tsn) +{ + map->tsn_map = map->raw_map; + map->overflow_map = map->tsn_map + len; + map->len = len; + + /* Clear out a TSN ack status. */ + memset(map->tsn_map, 0x00, map->len + map->len); + + /* Keep track of TSNs represented by tsn_map. */ + map->base_tsn = initial_tsn; + map->overflow_tsn = initial_tsn + map->len; + map->cumulative_tsn_ack_point = initial_tsn - 1; + map->max_tsn_seen = map->cumulative_tsn_ack_point; + map->malloced = 0; + map->pending_data = 0; + map->num_dup_tsns = 0; + + return map; +} + +/* Test the tracking state of this TSN. + * Returns: + * 0 if the TSN has not yet been seen + * >0 if the TSN has been seen (duplicate) + * <0 if the TSN is invalid (too large to track) + */ +int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn) +{ + __s32 gap; + int dup; + + /* Calculate the index into the mapping arrays. */ + gap = tsn - map->base_tsn; + + /* Verify that we can hold this TSN. */ + if (gap >= (/* base */ map->len + /* overflow */ map->len)) { + dup = -1; + goto out; + } + + /* Honk if we've already seen this TSN. + * We have three cases: + * 1. The TSN is ancient or belongs to a previous tsn_map. + * 2. The TSN is already marked in the tsn_map. + * 3. The TSN is already marked in the tsn_map_overflow. + */ + if (gap < 0 || + (gap < map->len && map->tsn_map[gap]) || + (gap >= map->len && map->overflow_map[gap - map->len])) + dup = 1; + else + dup = 0; + +out: + return dup; +} + +/* Is there a gap in the TSN map? */ +int sctp_tsnmap_has_gap(const struct sctp_tsnmap *map) +{ + int has_gap; + + has_gap = (map->cumulative_tsn_ack_point != map->max_tsn_seen); + return has_gap; +} + +/* Mark this TSN as seen. */ +void sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn) +{ + __s32 gap; + + /* Vacuously mark any TSN which precedes the map base or + * exceeds the end of the map. + */ + if (TSN_lt(tsn, map->base_tsn)) + return; + if (!TSN_lt(tsn, map->base_tsn + map->len + map->len)) + return; + + /* Bump the max. */ + if (TSN_lt(map->max_tsn_seen, tsn)) + map->max_tsn_seen = tsn; + + /* Assert: TSN is in range. */ + gap = tsn - map->base_tsn; + + /* Mark the TSN as received. */ + if (gap < map->len) + map->tsn_map[gap]++; + else + map->overflow_map[gap - map->len]++; + + /* Go fixup any internal TSN mapping variables including + * cumulative_tsn_ack_point. + */ + sctp_tsnmap_update(map); +} + +void sctp_tsnmap_report_dup(struct sctp_tsnmap *map, __u32 tsn) +{ +} + +/* Retrieve the Cumulative TSN Ack Point. */ +__u32 sctp_tsnmap_get_ctsn(const struct sctp_tsnmap *map) +{ + return map->cumulative_tsn_ack_point; +} + +/* Retrieve the highest TSN we've seen. */ +__u32 sctp_tsnmap_get_max_tsn_seen(const struct sctp_tsnmap *map) +{ + return map->max_tsn_seen; +} + +/* Dispose of a tsnmap. */ +void sctp_tsnmap_free(struct sctp_tsnmap *map) +{ + if (map->malloced) + kfree(map); +} + +/* Initialize a Gap Ack Block iterator from memory being provided. */ +void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map, + struct sctp_tsnmap_iter *iter) +{ + /* Only start looking one past the Cumulative TSN Ack Point. */ + iter->start = map->cumulative_tsn_ack_point + 1; +} + +/* Get the next Gap Ack Blocks. Returns 0 if there was not another block + * to get. + */ +int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map, + struct sctp_tsnmap_iter *iter, __u16 *start, __u16 *end) +{ + int started, ended; + __u16 _start, _end, offset; + + /* We haven't found a gap yet. */ + started = ended = 0; + + /* Search the first mapping array. */ + if (iter->start - map->base_tsn < map->len) { + + offset = iter->start - map->base_tsn; + sctp_tsnmap_find_gap_ack(map->tsn_map, offset, map->len, 0, + &started, &_start, &ended, &_end); + } + + /* Do we need to check the overflow map? */ + if (!ended) { + /* Fix up where we'd like to start searching in the + * overflow map. + */ + if (iter->start - map->base_tsn < map->len) + offset = 0; + else + offset = iter->start - map->base_tsn - map->len; + + /* Search the overflow map. */ + sctp_tsnmap_find_gap_ack(map->overflow_map, + offset, + map->len, + map->len, + &started, &_start, + &ended, &_end); + } + + /* The Gap Ack Block happens to end at the end of the + * overflow map. + */ + if (started && !ended) { + ended++; + _end = map->len + map->len - 1; + } + + /* If we found a Gap Ack Block, return the start and end and + * bump the iterator forward. + */ + if (ended) { + /* Fix up the start and end based on the + * Cumulative TSN Ack offset into the map. + */ + int gap = map->cumulative_tsn_ack_point - + map->base_tsn; + + *start = _start - gap; + *end = _end - gap; + + /* Move the iterator forward. */ + iter->start = map->cumulative_tsn_ack_point + *end + 1; + } + + return ended; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* This private helper function updates the tsnmap buffers and + * the Cumulative TSN Ack Point. + */ +static void sctp_tsnmap_update(struct sctp_tsnmap *map) +{ + __u32 ctsn; + + ctsn = map->cumulative_tsn_ack_point; + do { + ctsn++; + if (ctsn == map->overflow_tsn) { + /* Now tsn_map must have been all '1's, + * so we swap the map and check the overflow table + */ + __u8 *tmp = map->tsn_map; + memset(tmp, 0, map->len); + map->tsn_map = map->overflow_map; + map->overflow_map = tmp; + + /* Update the tsn_map boundaries. */ + map->base_tsn += map->len; + map->overflow_tsn += map->len; + } + } while (map->tsn_map[ctsn - map->base_tsn]); + + map->cumulative_tsn_ack_point = ctsn - 1; /* Back up one. */ + sctp_tsnmap_update_pending_data(map); +} + +static void sctp_tsnmap_update_pending_data(struct sctp_tsnmap *map) +{ + __u32 cum_tsn = map->cumulative_tsn_ack_point; + __u32 max_tsn = map->max_tsn_seen; + __u32 base_tsn = map->base_tsn; + __u16 pending_data; + __s32 gap, start, end, i; + + pending_data = max_tsn - cum_tsn; + gap = max_tsn - base_tsn; + + if (gap <= 0 || gap >= (map->len + map->len)) + goto out; + + start = ((cum_tsn >= base_tsn) ? (cum_tsn - base_tsn + 1) : 0); + end = ((gap > map->len ) ? map->len : gap + 1); + + for (i = start; i < end; i++) { + if (map->tsn_map[i]) + pending_data--; + } + + if (gap >= map->len) { + start = 0; + end = gap - map->len + 1; + for (i = start; i < end; i++) { + if (map->overflow_map[i]) + pending_data--; + } + } + +out: + map->pending_data = pending_data; +} + +/* This is a private helper for finding Gap Ack Blocks. It searches a + * single array for the start and end of a Gap Ack Block. + * + * The flags "started" and "ended" tell is if we found the beginning + * or (respectively) the end of a Gap Ack Block. + */ +static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off, + __u16 len, __u16 base, + int *started, __u16 *start, + int *ended, __u16 *end) +{ + int i = off; + + /* Look through the entire array, but break out + * early if we have found the end of the Gap Ack Block. + */ + + /* Look for the start. */ + if (!(*started)) { + for (; i < len; i++) { + if (map[i]) { + (*started)++; + *start = base + i; + break; + } + } + } + + /* Look for the end. */ + if (*started) { + /* We have found the start, let's find the + * end. If we find the end, break out. + */ + for (; i < len; i++) { + if (!map[i]) { + (*ended)++; + *end = base + i - 1; + break; + } + } + } +} + +/* Renege that we have seen a TSN. */ +void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn) +{ + __s32 gap; + + if (TSN_lt(tsn, map->base_tsn)) + return; + if (!TSN_lt(tsn, map->base_tsn + map->len + map->len)) + return; + + /* Assert: TSN is in range. */ + gap = tsn - map->base_tsn; + + /* Pretend we never saw the TSN. */ + if (gap < map->len) + map->tsn_map[gap] = 0; + else + map->overflow_map[gap - map->len] = 0; +} diff -urN linux-2.4.22-bk23/net/sctp/ulpevent.c linux-2.4.22-bk24/net/sctp/ulpevent.c --- linux-2.4.22-bk23/net/sctp/ulpevent.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/ulpevent.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,883 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * These functions manipulate an sctp event. The struct ulpevent is used + * to carry notifications and data to the ULP (sockets). + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * La Monte H.P. Yarroll + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include + +static void sctp_ulpevent_set_owner_r(struct sk_buff *skb, + struct sctp_association *asoc); +static void sctp_ulpevent_set_owner(struct sk_buff *skb, + const struct sctp_association *asoc); + +/* Create a new sctp_ulpevent. */ +struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp) +{ + struct sctp_ulpevent *event; + struct sk_buff *skb; + + skb = alloc_skb(size, gfp); + if (!skb) + goto fail; + + event = sctp_skb2event(skb); + event = sctp_ulpevent_init(event, msg_flags); + if (!event) + goto fail_init; + return event; + +fail_init: + kfree_skb(skb); +fail: + return NULL; +} + +/* Initialize an ULP event from an given skb. */ +struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *event, + int msg_flags) +{ + memset(event, sizeof(struct sctp_ulpevent), 0x00); + event->msg_flags = msg_flags; + return event; +} + +/* Dispose of an event. */ +void sctp_ulpevent_free(struct sctp_ulpevent *event) +{ + kfree_skb(sctp_event2skb(event)); +} + +/* Is this a MSG_NOTIFICATION? */ +int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event) +{ + return MSG_NOTIFICATION == (event->msg_flags & MSG_NOTIFICATION); +} + +/* Create and initialize an SCTP_ASSOC_CHANGE event. + * + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * Communication notifications inform the ULP that an SCTP association + * has either begun or ended. The identifier for a new association is + * provided by this notification. + * + * Note: There is no field checking here. If a field is unused it will be + * zero'd out. + */ +struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( + const struct sctp_association *asoc, + __u16 flags, __u16 state, __u16 error, __u16 outbound, + __u16 inbound, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_assoc_change *sac; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change), + MSG_NOTIFICATION, gfp); + if (!event) + goto fail; + skb = sctp_event2skb(event); + sac = (struct sctp_assoc_change *) + skb_put(skb, sizeof(struct sctp_assoc_change)); + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_type: + * It should be SCTP_ASSOC_CHANGE. + */ + sac->sac_type = SCTP_ASSOC_CHANGE; + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_state: 32 bits (signed integer) + * This field holds one of a number of values that communicate the + * event that happened to the association. + */ + sac->sac_state = state; + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_flags: 16 bits (unsigned integer) + * Currently unused. + */ + sac->sac_flags = 0; + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_length: sizeof (__u32) + * This field is the total length of the notification data, including + * the notification header. + */ + sac->sac_length = sizeof(struct sctp_assoc_change); + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_error: 32 bits (signed integer) + * + * If the state was reached due to a error condition (e.g. + * COMMUNICATION_LOST) any relevant error information is available in + * this field. This corresponds to the protocol error codes defined in + * [SCTP]. + */ + sac->sac_error = error; + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_outbound_streams: 16 bits (unsigned integer) + * sac_inbound_streams: 16 bits (unsigned integer) + * + * The maximum number of streams allowed in each direction are + * available in sac_outbound_streams and sac_inbound streams. + */ + sac->sac_outbound_streams = outbound; + sac->sac_inbound_streams = inbound; + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * sac_assoc_id: sizeof (sctp_assoc_t) + * + * The association id field, holds the identifier for the association. + * All notifications for a given association have the same association + * identifier. For TCP style socket, this field is ignored. + */ + sctp_ulpevent_set_owner(skb, asoc); + sac->sac_assoc_id = sctp_assoc2id(asoc); + + return event; + +fail: + return NULL; +} + +/* Create and initialize an SCTP_PEER_ADDR_CHANGE event. + * + * Socket Extensions for SCTP - draft-01 + * 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * When a destination address on a multi-homed peer encounters a change + * an interface details event is sent. + */ +struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change( + const struct sctp_association *asoc, + const struct sockaddr_storage *aaddr, + int flags, int state, int error, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_paddr_change *spc; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_paddr_change), + MSG_NOTIFICATION, gfp); + if (!event) + goto fail; + + skb = sctp_event2skb(event); + spc = (struct sctp_paddr_change *) + skb_put(skb, sizeof(struct sctp_paddr_change)); + + /* Sockets API Extensions for SCTP + * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * spc_type: + * + * It should be SCTP_PEER_ADDR_CHANGE. + */ + spc->spc_type = SCTP_PEER_ADDR_CHANGE; + + /* Sockets API Extensions for SCTP + * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * spc_length: sizeof (__u32) + * + * This field is the total length of the notification data, including + * the notification header. + */ + spc->spc_length = sizeof(struct sctp_paddr_change); + + /* Sockets API Extensions for SCTP + * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * spc_flags: 16 bits (unsigned integer) + * Currently unused. + */ + spc->spc_flags = 0; + + /* Sockets API Extensions for SCTP + * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * spc_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the address. + */ + spc->spc_state = state; + + /* Sockets API Extensions for SCTP + * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * spc_error: 32 bits (signed integer) + * + * If the state was reached due to any error condition (e.g. + * ADDRESS_UNREACHABLE) any relevant error information is available in + * this field. + */ + spc->spc_error = error; + + /* Socket Extensions for SCTP + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * spc_assoc_id: sizeof (sctp_assoc_t) + * + * The association id field, holds the identifier for the association. + * All notifications for a given association have the same association + * identifier. For TCP style socket, this field is ignored. + */ + sctp_ulpevent_set_owner(skb, asoc); + spc->spc_assoc_id = sctp_assoc2id(asoc); + + /* Sockets API Extensions for SCTP + * Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * spc_aaddr: sizeof (struct sockaddr_storage) + * + * The affected address field, holds the remote peer's address that is + * encountering the change of state. + */ + memcpy(&spc->spc_aaddr, aaddr, sizeof(struct sockaddr_storage)); + + return event; + +fail: + return NULL; +} + +/* Create and initialize an SCTP_REMOTE_ERROR notification. + * + * Note: This assumes that the chunk->skb->data already points to the + * operation error payload. + * + * Socket Extensions for SCTP - draft-01 + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * A remote peer may send an Operational Error message to its peer. + * This message indicates a variety of error conditions on an + * association. The entire error TLV as it appears on the wire is + * included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP + * specification [SCTP] and any extensions for a list of possible + * error formats. + */ +struct sctp_ulpevent *sctp_ulpevent_make_remote_error( + const struct sctp_association *asoc, sctp_chunk_t *chunk, + __u16 flags, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_remote_error *sre; + struct sk_buff *skb; + sctp_errhdr_t *ch; + __u16 cause; + int elen; + + ch = (sctp_errhdr_t *)(chunk->skb->data); + cause = ch->cause; + elen = ntohs(ch->length) - sizeof(sctp_errhdr_t); + + /* Pull off the ERROR header. */ + skb_pull(chunk->skb, sizeof(sctp_errhdr_t)); + + /* Copy the skb to a new skb with room for us to prepend + * notification with. + */ + skb = skb_copy_expand(chunk->skb, + sizeof(struct sctp_remote_error), /* headroom */ + 0, /* tailroom */ + gfp); + + /* Pull off the rest of the cause TLV from the chunk. */ + skb_pull(chunk->skb, elen); + if (!skb) + goto fail; + + /* Embed the event fields inside the cloned skb. */ + event = sctp_skb2event(skb); + event = sctp_ulpevent_init(event, MSG_NOTIFICATION); + + if (!event) + goto fail; + + sre = (struct sctp_remote_error *) + skb_push(skb, sizeof(struct sctp_remote_error)); + + /* Trim the buffer to the right length. */ + skb_trim(skb, sizeof(struct sctp_remote_error) + elen); + + /* Socket Extensions for SCTP + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * sre_type: + * It should be SCTP_REMOTE_ERROR. + */ + sre->sre_type = SCTP_REMOTE_ERROR; + + /* + * Socket Extensions for SCTP + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * sre_flags: 16 bits (unsigned integer) + * Currently unused. + */ + sre->sre_flags = 0; + + /* Socket Extensions for SCTP + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * sre_length: sizeof (__u32) + * + * This field is the total length of the notification data, + * including the notification header. + */ + sre->sre_length = skb->len; + + /* Socket Extensions for SCTP + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * sre_error: 16 bits (unsigned integer) + * This value represents one of the Operational Error causes defined in + * the SCTP specification, in network byte order. + */ + sre->sre_error = cause; + + /* Socket Extensions for SCTP + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * sre_assoc_id: sizeof (sctp_assoc_t) + * + * The association id field, holds the identifier for the association. + * All notifications for a given association have the same association + * identifier. For TCP style socket, this field is ignored. + */ + skb = sctp_event2skb(event); + sctp_ulpevent_set_owner(skb, asoc); + sre->sre_assoc_id = sctp_assoc2id(asoc); + + return event; + +fail: + return NULL; +} + +/* Create and initialize a SCTP_SEND_FAILED notification. + * + * Socket Extensions for SCTP - draft-01 + * 5.3.1.4 SCTP_SEND_FAILED + */ +struct sctp_ulpevent *sctp_ulpevent_make_send_failed( + const struct sctp_association *asoc, sctp_chunk_t *chunk, + __u16 flags, __u32 error, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_send_failed *ssf; + struct sk_buff *skb; + + /* Make skb with more room so we can prepend notification. */ + skb = skb_copy_expand(chunk->skb, + sizeof(struct sctp_send_failed), /* headroom */ + 0, /* tailroom */ + gfp); + if (!skb) + goto fail; + + /* Pull off the common chunk header and DATA header. */ + skb_pull(skb, sizeof(sctp_data_chunk_t)); + + /* Embed the event fields inside the cloned skb. */ + event = sctp_skb2event(skb); + event = sctp_ulpevent_init(event, MSG_NOTIFICATION); + if (!event) + goto fail; + + ssf = (struct sctp_send_failed *) + skb_push(skb, sizeof(struct sctp_send_failed)); + + /* Socket Extensions for SCTP + * 5.3.1.4 SCTP_SEND_FAILED + * + * ssf_type: + * It should be SCTP_SEND_FAILED. + */ + ssf->ssf_type = SCTP_SEND_FAILED; + + /* Socket Extensions for SCTP + * 5.3.1.4 SCTP_SEND_FAILED + * + * ssf_flags: 16 bits (unsigned integer) + * The flag value will take one of the following values + * + * SCTP_DATA_UNSENT - Indicates that the data was never put on + * the wire. + * + * SCTP_DATA_SENT - Indicates that the data was put on the wire. + * Note that this does not necessarily mean that the + * data was (or was not) successfully delivered. + */ + ssf->ssf_flags = flags; + + /* Socket Extensions for SCTP + * 5.3.1.4 SCTP_SEND_FAILED + * + * ssf_length: sizeof (__u32) + * This field is the total length of the notification data, including + * the notification header. + */ + ssf->ssf_length = skb->len; + + /* Socket Extensions for SCTP + * 5.3.1.4 SCTP_SEND_FAILED + * + * ssf_error: 16 bits (unsigned integer) + * This value represents the reason why the send failed, and if set, + * will be a SCTP protocol error code as defined in [SCTP] section + * 3.3.10. + */ + ssf->ssf_error = error; + + /* Socket Extensions for SCTP + * 5.3.1.4 SCTP_SEND_FAILED + * + * ssf_info: sizeof (struct sctp_sndrcvinfo) + * The original send information associated with the undelivered + * message. + */ + memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo)); + + /* Socket Extensions for SCTP + * 5.3.1.4 SCTP_SEND_FAILED + * + * ssf_assoc_id: sizeof (sctp_assoc_t) + * The association id field, sf_assoc_id, holds the identifier for the + * association. All notifications for a given association have the + * same association identifier. For TCP style socket, this field is + * ignored. + */ + skb = sctp_event2skb(event); + sctp_ulpevent_set_owner(skb, asoc); + ssf->ssf_assoc_id = sctp_assoc2id(asoc); + return event; + +fail: + return NULL; +} + +/* Create and initialize a SCTP_SHUTDOWN_EVENT notification. + * + * Socket Extensions for SCTP - draft-01 + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + */ +struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event( + const struct sctp_association *asoc, + __u16 flags, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_shutdown_event *sse; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change), + MSG_NOTIFICATION, gfp); + if (!event) + goto fail; + + skb = sctp_event2skb(event); + sse = (struct sctp_shutdown_event *) + skb_put(skb, sizeof(struct sctp_shutdown_event)); + + /* Socket Extensions for SCTP + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * sse_type + * It should be SCTP_SHUTDOWN_EVENT + */ + sse->sse_type = SCTP_SHUTDOWN_EVENT; + + /* Socket Extensions for SCTP + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * sse_flags: 16 bits (unsigned integer) + * Currently unused. + */ + sse->sse_flags = 0; + + /* Socket Extensions for SCTP + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * sse_length: sizeof (__u32) + * This field is the total length of the notification data, including + * the notification header. + */ + sse->sse_length = sizeof(struct sctp_shutdown_event); + + /* Socket Extensions for SCTP + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * sse_assoc_id: sizeof (sctp_assoc_t) + * The association id field, holds the identifier for the association. + * All notifications for a given association have the same association + * identifier. For TCP style socket, this field is ignored. + */ + sctp_ulpevent_set_owner(skb, asoc); + sse->sse_assoc_id = sctp_assoc2id(asoc); + + return event; + +fail: + return NULL; +} + +/* A message has been received. Package this message as a notification + * to pass it to the upper layers. Go ahead and calculate the sndrcvinfo + * even if filtered out later. + * + * Socket Extensions for SCTP + * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + */ +struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc, + sctp_chunk_t *chunk, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_sndrcvinfo *info; + struct sk_buff *skb, *list; + size_t padding, len; + + /* Clone the original skb, sharing the data. */ + skb = skb_clone(chunk->skb, gfp); + if (!skb) + goto fail; + + /* First calculate the padding, so we don't inadvertently + * pass up the wrong length to the user. + * + * RFC 2960 - Section 3.2 Chunk Field Descriptions + * + * The total length of a chunk(including Type, Length and Value fields) + * MUST be a multiple of 4 bytes. If the length of the chunk is not a + * multiple of 4 bytes, the sender MUST pad the chunk with all zero + * bytes and this padding is not included in the chunk length field. + * The sender should never pad with more than 3 bytes. The receiver + * MUST ignore the padding bytes. + */ + len = ntohs(chunk->chunk_hdr->length); + padding = WORD_ROUND(len) - len; + + /* Fixup cloned skb with just this chunks data. */ + skb_trim(skb, chunk->chunk_end - padding - skb->data); + + /* Set up a destructor to do rwnd accounting. */ + sctp_ulpevent_set_owner_r(skb, asoc); + + /* Embed the event fields inside the cloned skb. */ + event = sctp_skb2event(skb); + + /* Initialize event with flags 0. */ + event = sctp_ulpevent_init(event, 0); + if (!event) + goto fail_init; + + event->iif = sctp_chunk_iif(chunk); + /* Note: Not clearing the entire event struct as + * this is just a fragment of the real event. However, + * we still need to do rwnd accounting. + */ + for (list = skb_shinfo(skb)->frag_list; list; list = list->next) + sctp_ulpevent_set_owner_r(list, asoc); + + info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo; + + /* Sockets API Extensions for SCTP + * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * sinfo_stream: 16 bits (unsigned integer) + * + * For recvmsg() the SCTP stack places the message's stream number in + * this value. + */ + info->sinfo_stream = ntohs(chunk->subh.data_hdr->stream); + + /* Sockets API Extensions for SCTP + * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * sinfo_ssn: 16 bits (unsigned integer) + * + * For recvmsg() this value contains the stream sequence number that + * the remote endpoint placed in the DATA chunk. For fragmented + * messages this is the same number for all deliveries of the message + * (if more than one recvmsg() is needed to read the message). + */ + info->sinfo_ssn = ntohs(chunk->subh.data_hdr->ssn); + + /* Sockets API Extensions for SCTP + * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * sinfo_ppid: 32 bits (unsigned integer) + * + * In recvmsg() this value is + * the same information that was passed by the upper layer in the peer + * application. Please note that byte order issues are NOT accounted + * for and this information is passed opaquely by the SCTP stack from + * one end to the other. + */ + info->sinfo_ppid = ntohl(chunk->subh.data_hdr->ppid); + + /* Sockets API Extensions for SCTP + * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * sinfo_flags: 16 bits (unsigned integer) + * + * This field may contain any of the following flags and is composed of + * a bitwise OR of these values. + * + * recvmsg() flags: + * + * MSG_UNORDERED - This flag is present when the message was sent + * non-ordered. + */ + if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) { + info->sinfo_flags |= MSG_UNORDERED; + + /* sinfo_cumtsn: 32 bit (unsigned integer) + * + * This field will hold the current cumulative TSN as + * known by the underlying SCTP layer. Note this field is + * ignored when sending and only valid for a receive + * operation when sinfo_flags are set to MSG_UNORDERED. + */ + info->sinfo_cumtsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); + } + + /* Note: For reassembly, we need to have the fragmentation bits. + * For now, merge these into the msg_flags, since those bit + * possitions are not used. + */ + event->msg_flags |= chunk->chunk_hdr->flags; + + /* With 04 draft, tsn moves into sndrcvinfo. */ + info->sinfo_tsn = ntohl(chunk->subh.data_hdr->tsn); + + /* Context is not used on receive. */ + info->sinfo_context = 0; + + /* Sockets API Extensions for SCTP + * Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * sinfo_assoc_id: sizeof (sctp_assoc_t) + * + * The association handle field, sinfo_assoc_id, holds the identifier + * for the association announced in the COMMUNICATION_UP notification. + * All notifications for a given association have the same identifier. + * Ignored for TCP-style sockets. + */ + info->sinfo_assoc_id = sctp_assoc2id(asoc); + + return event; + +fail_init: + kfree_skb(skb); + +fail: + return NULL; +} + +/* Create a partial delivery related event. + * + * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT + * + * When a reciever is engaged in a partial delivery of a + * message this notification will be used to inidicate + * various events. + */ +struct sctp_ulpevent *sctp_ulpevent_make_pdapi( + const struct sctp_association *asoc, __u32 indication, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_rcv_pdapi_event *pd; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change), + MSG_NOTIFICATION, gfp); + if (!event) + goto fail; + + skb = sctp_event2skb(event); + pd = (struct sctp_rcv_pdapi_event *) + skb_put(skb, sizeof(struct sctp_rcv_pdapi_event)); + + /* pdapi_type + * It should be SCTP_PARTIAL_DELIVERY_EVENT + * + * pdapi_flags: 16 bits (unsigned integer) + * Currently unused. + */ + pd->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT; + pd->pdapi_flags = 0; + + /* pdapi_length: 32 bits (unsigned integer) + * + * This field is the total length of the notification data, including + * the notification header. It will generally be sizeof (struct + * sctp_rcv_pdapi_event). + */ + pd->pdapi_length = sizeof(struct sctp_rcv_pdapi_event); + + /* pdapi_indication: 32 bits (unsigned integer) + * + * This field holds the indication being sent to the application. + */ + pd->pdapi_indication = indication; + + /* pdapi_assoc_id: sizeof (sctp_assoc_t) + * + * The association id field, holds the identifier for the association. + */ + pd->pdapi_assoc_id = sctp_assoc2id(asoc); + + return event; +fail: + return NULL; +} + +/* Return the notification type, assuming this is a notification + * event. + */ +__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event) +{ + union sctp_notification *notification; + struct sk_buff *skb; + + skb = sctp_event2skb((struct sctp_ulpevent *)event); + notification = (union sctp_notification *) skb->data; + return notification->h.sn_type; +} + +/* Copy out the sndrcvinfo into a msghdr. */ +void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, + struct msghdr *msghdr) +{ + if (!sctp_ulpevent_is_notification(event)) { + put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV, + sizeof(struct sctp_sndrcvinfo), + (void *) &event->sndrcvinfo); + } +} + +/* Do accounting for bytes just read by user. */ +static void sctp_rcvmsg_rfree(struct sk_buff *skb) +{ + struct sctp_association *asoc; + struct sctp_ulpevent *event; + + /* Current stack structures assume that the rcv buffer is + * per socket. For UDP style sockets this is not true as + * multiple associations may be on a single UDP-style socket. + * Use the local private area of the skb to track the owning + * association. + */ + event = sctp_skb2event(skb); + asoc = event->event_asoc; + sctp_assoc_rwnd_increase(asoc, skb_headlen(skb)); + sctp_association_put(asoc); +} + +/* Charge receive window for bytes received. */ +static void sctp_ulpevent_set_owner_r(struct sk_buff *skb, + struct sctp_association *asoc) +{ + struct sctp_ulpevent *event; + + /* The current stack structures assume that the rcv buffer is + * per socket. For UDP-style sockets this is not true as + * multiple associations may be on a single UDP-style socket. + * We use the local private area of the skb to track the owning + * association. + */ + sctp_association_hold(asoc); + skb->sk = asoc->base.sk; + event = sctp_skb2event(skb); + event->event_asoc = asoc; + + skb->destructor = sctp_rcvmsg_rfree; + + sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb)); +} + +/* A simple destructor to give up the reference to the association. */ +static void sctp_ulpevent_rfree(struct sk_buff *skb) +{ + struct sctp_ulpevent *event; + + event = sctp_skb2event(skb); + sctp_association_put(event->event_asoc); +} + +/* Hold the association in case the msg_name needs read out of + * the association. + */ +static void sctp_ulpevent_set_owner(struct sk_buff *skb, + const struct sctp_association *asoc) +{ + struct sctp_ulpevent *event; + + /* Cast away the const, as we are just wanting to + * bump the reference count. + */ + sctp_association_hold((struct sctp_association *)asoc); + skb->sk = asoc->base.sk; + event = sctp_skb2event(skb); + event->event_asoc = (struct sctp_association *)asoc; + skb->destructor = sctp_ulpevent_rfree; +} diff -urN linux-2.4.22-bk23/net/sctp/ulpqueue.c linux-2.4.22-bk24/net/sctp/ulpqueue.c --- linux-2.4.22-bk23/net/sctp/ulpqueue.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.22-bk24/net/sctp/ulpqueue.c 2003-09-23 03:07:18.000000000 -0700 @@ -0,0 +1,820 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * Copyright (c) 2001 Intel Corp. + * Copyright (c) 2001 Nokia, Inc. + * Copyright (c) 2001 La Monte H.P. Yarroll + * + * This abstraction carries sctp events to the ULP (sockets). + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * The SCTP reference implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Jon Grimm + * La Monte H.P. Yarroll + * Sridhar Samudrala + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#include +#include +#include +#include +#include +#include + +/* Forward declarations for internal helpers. */ +static inline struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq, + struct sctp_ulpevent *); +static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *, + struct sctp_ulpevent *); + +/* 1st Level Abstractions */ + +/* Create a new ULP queue. */ +struct sctp_ulpq *sctp_ulpq_new(struct sctp_association *asoc, int gfp) +{ + struct sctp_ulpq *ulpq; + + ulpq = kmalloc(sizeof(struct sctp_ulpq), gfp); + if (!ulpq) + goto fail; + if (!sctp_ulpq_init(ulpq, asoc)) + goto fail_init; + ulpq->malloced = 1; + return ulpq; + +fail_init: + kfree(ulpq); +fail: + return NULL; +} + +/* Initialize a ULP queue from a block of memory. */ +struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq, + struct sctp_association *asoc) +{ + memset(ulpq, sizeof(struct sctp_ulpq), 0x00); + + ulpq->asoc = asoc; + skb_queue_head_init(&ulpq->reasm); + skb_queue_head_init(&ulpq->lobby); + ulpq->pd_mode = 0; + ulpq->malloced = 0; + + return ulpq; +} + + +/* Flush the reassembly and ordering queues. */ +void sctp_ulpq_flush(struct sctp_ulpq *ulpq) +{ + struct sk_buff *skb; + struct sctp_ulpevent *event; + + while ((skb = __skb_dequeue(&ulpq->lobby))) { + event = sctp_skb2event(skb); + sctp_ulpevent_free(event); + } + + while ((skb = __skb_dequeue(&ulpq->reasm))) { + event = sctp_skb2event(skb); + sctp_ulpevent_free(event); + } + +} + +/* Dispose of a ulpqueue. */ +void sctp_ulpq_free(struct sctp_ulpq *ulpq) +{ + sctp_ulpq_flush(ulpq); + if (ulpq->malloced) + kfree(ulpq); +} + +/* Process an incoming DATA chunk. */ +int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, sctp_chunk_t *chunk, + int gfp) +{ + struct sk_buff_head temp; + sctp_data_chunk_t *hdr; + struct sctp_ulpevent *event; + + hdr = (sctp_data_chunk_t *) chunk->chunk_hdr; + + /* Create an event from the incoming chunk. */ + event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp); + if (!event) + return -ENOMEM; + + /* Do reassembly if needed. */ + event = sctp_ulpq_reasm(ulpq, event); + + /* Do ordering if needed. */ + if ((event) && (event->msg_flags & MSG_EOR)){ + /* Create a temporary list to collect chunks on. */ + skb_queue_head_init(&temp); + __skb_queue_tail(&temp, sctp_event2skb(event)); + + event = sctp_ulpq_order(ulpq, event); + } + + /* Send event to the ULP. */ + if (event) + sctp_ulpq_tail_event(ulpq, event); + + return 0; +} + +/* Add a new event for propagation to the ULP. */ +/* Clear the partial delivery mode for this socket. Note: This + * assumes that no association is currently in partial delivery mode. + */ +int sctp_clear_pd(struct sock *sk) +{ + struct sctp_opt *sp; + sp = sctp_sk(sk); + + sp->pd_mode = 0; + if (!skb_queue_empty(&sp->pd_lobby)) { + struct list_head *list; + sctp_skb_list_tail(&sp->pd_lobby, &sk->receive_queue); + list = (struct list_head *)&sctp_sk(sk)->pd_lobby; + INIT_LIST_HEAD(list); + return 1; + } + return 0; +} + +/* Clear the pd_mode and restart any pending messages waiting for delivery. */ +static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) +{ + ulpq->pd_mode = 0; + return sctp_clear_pd(ulpq->asoc->base.sk); +} + + + +int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) +{ + struct sock *sk = ulpq->asoc->base.sk; + struct sk_buff_head *queue; + int clear_pd = 0; + + /* If the socket is just going to throw this away, do not + * even try to deliver it. + */ + if ((sk->dead) || (sk->shutdown & RCV_SHUTDOWN)) + goto out_free; + + /* Check if the user wishes to receive this event. */ + if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe)) + goto out_free; + + /* If we are in partial delivery mode, post to the lobby until + * partial delivery is cleared, unless, of course _this_ is + * the association the cause of the partial delivery. + */ + + if (!sctp_sk(sk)->pd_mode) { + queue = &sk->receive_queue; + } else if (ulpq->pd_mode) { + if (event->msg_flags & MSG_NOTIFICATION) + queue = &sctp_sk(sk)->pd_lobby; + else { + clear_pd = event->msg_flags & MSG_EOR; + queue = &sk->receive_queue; + } + } else + queue = &sctp_sk(sk)->pd_lobby; + + + /* If we are harvesting multiple skbs they will be + * collected on a list. + */ + if (sctp_event2skb(event)->list) + sctp_skb_list_tail(sctp_event2skb(event)->list, queue); + else + __skb_queue_tail(queue, sctp_event2skb(event)); + + /* Did we just complete partial delivery and need to get + * rolling again? Move pending data to the receive + * queue. + */ + if (clear_pd) + sctp_ulpq_clear_pd(ulpq); + + if (queue == &sk->receive_queue) + sk->data_ready(sk, 0); + return 1; + +out_free: + if (sctp_event2skb(event)->list) + skb_queue_purge(sctp_event2skb(event)->list); + else + kfree_skb(sctp_event2skb(event)); + return 0; +} + +/* 2nd Level Abstractions */ + +/* Helper function to store chunks that need to be reassembled. */ +static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq, + struct sctp_ulpevent *event) +{ + struct sk_buff *pos; + struct sctp_ulpevent *cevent; + __u32 tsn, ctsn; + + tsn = event->sndrcvinfo.sinfo_tsn; + + /* See if it belongs at the end. */ + pos = skb_peek_tail(&ulpq->reasm); + if (!pos) { + __skb_queue_tail(&ulpq->reasm, sctp_event2skb(event)); + return; + } + + /* Short circuit just dropping it at the end. */ + cevent = sctp_skb2event(pos); + ctsn = cevent->sndrcvinfo.sinfo_tsn; + if (TSN_lt(ctsn, tsn)) { + __skb_queue_tail(&ulpq->reasm, sctp_event2skb(event)); + return; + } + + /* Find the right place in this list. We store them by TSN. */ + skb_queue_walk(&ulpq->reasm, pos) { + cevent = sctp_skb2event(pos); + ctsn = cevent->sndrcvinfo.sinfo_tsn; + + if (TSN_lt(tsn, ctsn)) + break; + } + + /* Insert before pos. */ + __skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->reasm); + +} + +/* Helper function to return an event corresponding to the reassembled + * datagram. + * This routine creates a re-assembled skb given the first and last skb's + * as stored in the reassembly queue. The skb's may be non-linear if the sctp + * payload was fragmented on the way and ip had to reassemble them. + * We add the rest of skb's to the first skb's fraglist. + */ +static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag) +{ + struct sk_buff *pos; + struct sctp_ulpevent *event; + struct sk_buff *pnext, *last; + struct sk_buff *list = skb_shinfo(f_frag)->frag_list; + + /* Store the pointer to the 2nd skb */ + if (f_frag == l_frag) + pos = NULL; + else + pos = f_frag->next; + + /* Get the last skb in the f_frag's frag_list if present. */ + for (last = list; list; last = list, list = list->next); + + /* Add the list of remaining fragments to the first fragments + * frag_list. + */ + if (last) + last->next = pos; + else + skb_shinfo(f_frag)->frag_list = pos; + + /* Remove the first fragment from the reassembly queue. */ + __skb_unlink(f_frag, f_frag->list); + while (pos) { + + pnext = pos->next; + + /* Update the len and data_len fields of the first fragment. */ + f_frag->len += pos->len; + f_frag->data_len += pos->len; + + /* Remove the fragment from the reassembly queue. */ + __skb_unlink(pos, pos->list); + + /* Break if we have reached the last fragment. */ + if (pos == l_frag) + break; + + pos->next = pnext; + pos = pnext; + }; + + event = sctp_skb2event(f_frag); + SCTP_INC_STATS(SctpReasmUsrMsgs); + + return event; +} + + +/* Helper function to check if an incoming chunk has filled up the last + * missing fragment in a SCTP datagram and return the corresponding event. + */ +static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq) +{ + struct sk_buff *pos; + struct sctp_ulpevent *cevent; + struct sk_buff *first_frag = NULL; + __u32 ctsn, next_tsn; + struct sctp_ulpevent *retval = NULL; + + /* Initialized to 0 just to avoid compiler warning message. Will + * never be used with this value. It is referenced only after it + * is set when we find the first fragment of a message. + */ + next_tsn = 0; + + /* The chunks are held in the reasm queue sorted by TSN. + * Walk through the queue sequentially and look for a sequence of + * fragmented chunks that complete a datagram. + * 'first_frag' and next_tsn are reset when we find a chunk which + * is the first fragment of a datagram. Once these 2 fields are set + * we expect to find the remaining middle fragments and the last + * fragment in order. If not, first_frag is reset to NULL and we + * start the next pass when we find another first fragment. + */ + skb_queue_walk(&ulpq->reasm, pos) { + cevent = sctp_skb2event(pos); + ctsn = cevent->sndrcvinfo.sinfo_tsn; + + switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) { + case SCTP_DATA_FIRST_FRAG: + first_frag = pos; + next_tsn = ctsn + 1; + break; + + case SCTP_DATA_MIDDLE_FRAG: + if ((first_frag) && (ctsn == next_tsn)) + next_tsn++; + else + first_frag = NULL; + break; + + case SCTP_DATA_LAST_FRAG: + if (first_frag && (ctsn == next_tsn)) + goto found; + else + first_frag = NULL; + break; + }; + + } +done: + return retval; +found: + retval = sctp_make_reassembled_event(first_frag, pos); + if (retval) + retval->msg_flags |= MSG_EOR; + goto done; +} + +/* Retrieve the next set of fragments of a partial message. */ +static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq) +{ + struct sk_buff *pos, *last_frag, *first_frag; + struct sctp_ulpevent *cevent; + __u32 ctsn, next_tsn; + int is_last; + struct sctp_ulpevent *retval; + + /* The chunks are held in the reasm queue sorted by TSN. + * Walk through the queue sequentially and look for the first + * sequence of fragmented chunks. + */ + + if (skb_queue_empty(&ulpq->reasm)) + return NULL; + + last_frag = first_frag = NULL; + retval = NULL; + next_tsn = 0; + is_last = 0; + + skb_queue_walk(&ulpq->reasm, pos) { + cevent = sctp_skb2event(pos); + ctsn = cevent->sndrcvinfo.sinfo_tsn; + + switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) { + case SCTP_DATA_MIDDLE_FRAG: + if (!first_frag) { + first_frag = pos; + next_tsn = ctsn + 1; + last_frag = pos; + } else if (next_tsn == ctsn) + next_tsn++; + else + goto done; + break; + case SCTP_DATA_LAST_FRAG: + if (!first_frag) + first_frag = pos; + else if (ctsn != next_tsn) + goto done; + last_frag = pos; + is_last = 1; + goto done; + default: + return NULL; + }; + } + + /* We have the reassembled event. There is no need to look + * further. + */ +done: + retval = sctp_make_reassembled_event(first_frag, last_frag); + if (retval && is_last) + retval->msg_flags |= MSG_EOR; + + return retval; +} + + +/* Helper function to reassemble chunks. Hold chunks on the reasm queue that + * need reassembling. + */ +static inline struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq, + struct sctp_ulpevent *event) +{ + struct sctp_ulpevent *retval = NULL; + + /* Check if this is part of a fragmented message. */ + if (SCTP_DATA_NOT_FRAG == (event->msg_flags & SCTP_DATA_FRAG_MASK)) { + event->msg_flags |= MSG_EOR; + return event; + } + + sctp_ulpq_store_reasm(ulpq, event); + if (!ulpq->pd_mode) + retval = sctp_ulpq_retrieve_reassembled(ulpq); + else { + __u32 ctsn, ctsnap; + + /* Do not even bother unless this is the next tsn to + * be delivered. + */ + ctsn = event->sndrcvinfo.sinfo_tsn; + ctsnap = sctp_tsnmap_get_ctsn(&ulpq->asoc->peer.tsn_map); + if (TSN_lte(ctsn, ctsnap)) + retval = sctp_ulpq_retrieve_partial(ulpq); + } + + return retval; +} + +/* Retrieve the first part (sequential fragments) for partial delivery. */ +static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq) +{ + struct sk_buff *pos, *last_frag, *first_frag; + struct sctp_ulpevent *cevent; + __u32 ctsn, next_tsn; + struct sctp_ulpevent *retval; + + /* The chunks are held in the reasm queue sorted by TSN. + * Walk through the queue sequentially and look for a sequence of + * fragmented chunks that start a datagram. + */ + + if (skb_queue_empty(&ulpq->reasm)) + return NULL; + + last_frag = first_frag = NULL; + retval = NULL; + next_tsn = 0; + + skb_queue_walk(&ulpq->reasm, pos) { + cevent = sctp_skb2event(pos); + ctsn = cevent->sndrcvinfo.sinfo_tsn; + + switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) { + case SCTP_DATA_FIRST_FRAG: + if (!first_frag) { + first_frag = pos; + next_tsn = ctsn + 1; + last_frag = pos; + } else + goto done; + break; + + case SCTP_DATA_MIDDLE_FRAG: + if (!first_frag) + return NULL; + if (ctsn == next_tsn) { + next_tsn++; + last_frag = pos; + } else + goto done; + break; + default: + return NULL; + }; + } + + /* We have the reassembled event. There is no need to look + * further. + */ +done: + retval = sctp_make_reassembled_event(first_frag, last_frag); + return retval; +} + +/* Helper function to gather skbs that have possibly become + * ordered by an an incoming chunk. + */ +static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq, + struct sctp_ulpevent *event) +{ + struct sk_buff *pos, *tmp; + struct sctp_ulpevent *cevent; + struct sctp_stream *in; + __u16 sid, csid; + __u16 ssn, cssn; + + sid = event->sndrcvinfo.sinfo_stream; + ssn = event->sndrcvinfo.sinfo_ssn; + in = &ulpq->asoc->ssnmap->in; + + /* We are holding the chunks by stream, by SSN. */ + sctp_skb_for_each(pos, &ulpq->lobby, tmp) { + cevent = sctp_skb2event(pos); + csid = cevent->sndrcvinfo.sinfo_stream; + cssn = cevent->sndrcvinfo.sinfo_ssn; + + /* Have we gone too far? */ + if (csid > sid) + break; + + /* Have we not gone far enough? */ + if (csid < sid) + continue; + + if (cssn != sctp_ssn_peek(in, sid)) + break; + + /* Found it, so mark in the ssnmap. */ + sctp_ssn_next(in, sid); + + __skb_unlink(pos, pos->list); + + /* Attach all gathered skbs to the event. */ + __skb_queue_tail(sctp_event2skb(event)->list, pos); + } +} + +/* Helper function to store chunks needing ordering. */ +static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq, + struct sctp_ulpevent *event) +{ + struct sk_buff *pos; + struct sctp_ulpevent *cevent; + __u16 sid, csid; + __u16 ssn, cssn; + + pos = skb_peek_tail(&ulpq->lobby); + if (!pos) { + __skb_queue_tail(&ulpq->lobby, sctp_event2skb(event)); + return; + } + + sid = event->sndrcvinfo.sinfo_stream; + ssn = event->sndrcvinfo.sinfo_ssn; + + cevent = sctp_skb2event(pos); + csid = cevent->sndrcvinfo.sinfo_stream; + cssn = cevent->sndrcvinfo.sinfo_ssn; + if (sid > csid) { + __skb_queue_tail(&ulpq->lobby, sctp_event2skb(event)); + return; + } + + if ((sid == csid) && SSN_lt(cssn, ssn)) { + __skb_queue_tail(&ulpq->lobby, sctp_event2skb(event)); + return; + } + + /* Find the right place in this list. We store them by + * stream ID and then by SSN. + */ + skb_queue_walk(&ulpq->lobby, pos) { + cevent = sctp_skb2event(pos); + csid = cevent->sndrcvinfo.sinfo_stream; + cssn = cevent->sndrcvinfo.sinfo_ssn; + + if (csid > sid) + break; + if (csid == sid && SSN_lt(ssn, cssn)) + break; + } + + + /* Insert before pos. */ + __skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->lobby); + +} + +static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq, + struct sctp_ulpevent *event) +{ + __u16 sid, ssn; + struct sctp_stream *in; + + /* Check if this message needs ordering. */ + if (SCTP_DATA_UNORDERED & event->msg_flags) + return event; + + /* Note: The stream ID must be verified before this routine. */ + sid = event->sndrcvinfo.sinfo_stream; + ssn = event->sndrcvinfo.sinfo_ssn; + in = &ulpq->asoc->ssnmap->in; + + /* Is this the expected SSN for this stream ID? */ + if (ssn != sctp_ssn_peek(in, sid)) { + /* We've received something out of order, so find where it + * needs to be placed. We order by stream and then by SSN. + */ + sctp_ulpq_store_ordered(ulpq, event); + return NULL; + } + + /* Mark that the next chunk has been found. */ + sctp_ssn_next(in, sid); + + /* Go find any other chunks that were waiting for + * ordering. + */ + sctp_ulpq_retrieve_ordered(ulpq, event); + + return event; +} + +/* Renege 'needed' bytes from the ordering queue. */ +static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed) +{ + __u16 freed = 0; + __u32 tsn; + struct sk_buff *skb; + struct sctp_ulpevent *event; + struct sctp_tsnmap *tsnmap; + + tsnmap = &ulpq->asoc->peer.tsn_map; + + while ((skb = __skb_dequeue_tail(&ulpq->lobby))) { + freed += skb_headlen(skb); + event = sctp_skb2event(skb); + tsn = event->sndrcvinfo.sinfo_tsn; + + sctp_ulpevent_free(event); + sctp_tsnmap_renege(tsnmap, tsn); + if (freed >= needed) + return freed; + } + + return freed; +} + +/* Renege 'needed' bytes from the reassembly queue. */ +static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed) +{ + __u16 freed = 0; + __u32 tsn; + struct sk_buff *skb; + struct sctp_ulpevent *event; + struct sctp_tsnmap *tsnmap; + + tsnmap = &ulpq->asoc->peer.tsn_map; + + /* Walk backwards through the list, reneges the newest tsns. */ + while ((skb = __skb_dequeue_tail(&ulpq->reasm))) { + freed += skb_headlen(skb); + event = sctp_skb2event(skb); + tsn = event->sndrcvinfo.sinfo_tsn; + + sctp_ulpevent_free(event); + sctp_tsnmap_renege(tsnmap, tsn); + if (freed >= needed) + return freed; + } + + return freed; +} + +/* Partial deliver the first message as there is pressure on rwnd. */ +void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq, + struct sctp_chunk *chunk, int gfp) +{ + struct sctp_ulpevent *event; + struct sctp_association *asoc; + + asoc = ulpq->asoc; + + /* Are we already in partial delivery mode? */ + if (!sctp_sk(asoc->base.sk)->pd_mode) { + + /* Is partial delivery possible? */ + event = sctp_ulpq_retrieve_first(ulpq); + /* Send event to the ULP. */ + if (event) { + sctp_ulpq_tail_event(ulpq, event); + sctp_sk(asoc->base.sk)->pd_mode = 1; + ulpq->pd_mode = 1; + return; + } + } +} + +/* Renege some packets to make room for an incoming chunk. */ +void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk, + int gfp) +{ + struct sctp_association *asoc; + __u16 needed, freed; + + asoc = ulpq->asoc; + + if (chunk) { + needed = ntohs(chunk->chunk_hdr->length); + needed -= sizeof(sctp_data_chunk_t); + } else + needed = SCTP_DEFAULT_MAXWINDOW; + + freed = 0; + + if (skb_queue_empty(&asoc->base.sk->receive_queue)) { + freed = sctp_ulpq_renege_order(ulpq, needed); + if (freed < needed) { + freed += sctp_ulpq_renege_frags(ulpq, needed - freed); + } + } + /* If able to free enough room, accept this chunk. */ + if (chunk && (freed >= needed)) { + __u32 tsn; + tsn = ntohl(chunk->subh.data_hdr->tsn); + sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn); + sctp_ulpq_tail_data(ulpq, chunk, gfp); + + sctp_ulpq_partial_delivery(ulpq, chunk, gfp); + } + + return; +} + + + +/* Notify the application if an association is aborted and in + * partial delivery mode. Send up any pending received messages. + */ +void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int gfp) +{ + struct sctp_ulpevent *ev = NULL; + struct sock *sk; + + if (!ulpq->pd_mode) + return; + + sk = ulpq->asoc->base.sk; + if (sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT, + &sctp_sk(sk)->subscribe)) + ev = sctp_ulpevent_make_pdapi(ulpq->asoc, + SCTP_PARTIAL_DELIVERY_ABORTED, + gfp); + if (ev) + __skb_queue_tail(&sk->receive_queue, sctp_event2skb(ev)); + + /* If there is data waiting, send it up the socket now. */ + if (sctp_ulpq_clear_pd(ulpq) || ev) + sk->data_ready(sk, 0); +} diff -urN linux-2.4.22-bk23/net/socket.c linux-2.4.22-bk24/net/socket.c --- linux-2.4.22-bk23/net/socket.c 2003-08-25 04:44:44.000000000 -0700 +++ linux-2.4.22-bk24/net/socket.c 2003-09-23 03:07:18.000000000 -0700 @@ -325,7 +325,7 @@ * but we take care of internal coherence yet. */ -static int sock_map_fd(struct socket *sock) +int sock_map_fd(struct socket *sock) { int fd; struct qstr this;