diff options
author | Linus Torvalds <torvalds@cc.helsinki.fi> | 1994-01-07 14:50:53 +0000 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2007-08-19 14:19:25 -0400 |
commit | a9cc257a8016df80d8bbfdc3ffc6dd2193280c0a (patch) | |
tree | 30593a304fc18da6479c71c994e4625b0c27b72d | |
parent | eb54995bc4b5a31293b1cd153d1460570f32125d (diff) | |
download | archive-a9cc257a8016df80d8bbfdc3ffc6dd2193280c0a.tar.gz |
ALPHA-pl14n
69 files changed, 13645 insertions, 8215 deletions
@@ -1,6 +1,6 @@ VERSION = 0.99 PATCHLEVEL = 14 -ALPHA = m +ALPHA = n all: Version zImage diff --git a/drivers/char/console.c b/drivers/char/console.c index 0823dc7..c153a74 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -10,7 +10,7 @@ * This module exports the console io functions: * * 'long con_init(long)' - * 'void con_open(struct tty_queue * queue, struct )' + * 'int con_open(struct tty_struct *tty, struct file * filp)' * 'void update_screen(int new_console)' * 'void blank_screen(void)' * 'void unblank_screen(void)' @@ -73,9 +73,10 @@ static char sel_buffer[SEL_BUFFER_SIZE] = { '\0' }; extern void vt_init(void); extern void register_console(void (*proc)(const char *)); +extern void compute_shiftstate(void); unsigned long video_num_columns; /* Number of text columns */ -unsigned long video_num_lines; /* Number of test lines */ +unsigned long video_num_lines; /* Number of text lines */ static unsigned char video_type; /* Type of display being used */ static unsigned long video_mem_base; /* Base of video memory */ @@ -215,29 +216,29 @@ static unsigned char * translations[] = { /* 8-bit Latin-1 mapped to the PC charater set: '\0' means non-printable */ (unsigned char *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0" " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" "`abcdefghijklmnopqrstuvwxyz{|}~\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" + "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" - "\376\245\376\376\376\376\231\376\235\376\376\376\232\376\376\341" + "\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341" "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" - "\376\244\225\242\223\376\224\366\233\227\243\226\201\376\376\230", + "\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230", /* vt100 graphics */ (unsigned char *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0" " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ " "\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304" "\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" + "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341" @@ -498,12 +499,16 @@ static inline void bs(int currcons) static inline void del(int currcons) { +#if 0 if (x) { - pos -= 2; - x--; + if (!need_wrap) { /* this is not the right condition */ + pos -= 2; + x--; + } *(unsigned short *)pos = video_erase_char; need_wrap = 0; } +#endif } static void csi_J(int currcons, int vpar) @@ -1547,6 +1552,7 @@ void update_screen(int new_console) set_origin(fg_console); set_cursor(new_console); set_leds(); + compute_shiftstate(); lock = 0; } diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c index 3193e1f..171931e 100644 --- a/drivers/char/defkeymap.c +++ b/drivers/char/defkeymap.c @@ -3,6 +3,7 @@ #include <linux/types.h> #include <linux/keyboard.h> +#include <linux/kd.h> u_short key_map[NR_KEYMAPS][NR_KEYS] = { { @@ -20,7 +21,7 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x001b, 0x0021, 0x0040, 0x0023, 0x0024, 0x0025, 0x005e, @@ -37,16 +38,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x020b, 0x0601, 0x0602, 0x0117, 0x0600, 0x020a, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0040, 0x0200, 0x0024, 0x0200, 0x0200, 0x007b, 0x005b, 0x005d, 0x007d, 0x005c, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x007e, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0071, 0x0077, 0x0065, 0x0072, 0x0074, 0x0079, 0x0075, 0x0069, + 0x006f, 0x0070, 0x0200, 0x007e, 0x0201, 0x0702, 0x0061, 0x0073, + 0x0064, 0x0066, 0x0067, 0x0068, 0x006a, 0x006b, 0x006c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x007a, 0x0078, 0x0063, 0x0076, + 0x0062, 0x006e, 0x006d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x050c, 0x050d, 0x050e, 0x050f, 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0208, 0x0202, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -54,16 +55,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0517, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0051, 0x0057, 0x0045, 0x0052, 0x0054, 0x0059, 0x0055, 0x0049, + 0x004f, 0x0050, 0x0200, 0x0200, 0x0201, 0x0702, 0x0041, 0x0053, + 0x0044, 0x0046, 0x0047, 0x0048, 0x004a, 0x004b, 0x004c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x005a, 0x0058, 0x0043, 0x0056, + 0x0042, 0x004e, 0x004d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -71,7 +72,7 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0000, 0x001b, 0x001c, 0x001d, 0x001e, @@ -80,7 +81,7 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x000f, 0x0010, 0x001b, 0x001d, 0x0201, 0x0702, 0x0001, 0x0013, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200, 0x0007, 0x0000, 0x0700, 0x001c, 0x001a, 0x0018, 0x0003, 0x0016, - 0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x007f, 0x0700, 0x030c, + 0x0002, 0x000e, 0x000d, 0x0200, 0x020e, 0x007f, 0x0700, 0x030c, 0x0703, 0x0000, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0204, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -88,16 +89,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0200, 0x0200, 0x0200, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200, + 0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009, + 0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013, + 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016, + 0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -105,16 +106,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009, + 0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013, + 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016, + 0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -122,16 +123,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009, + 0x000f, 0x0010, 0x0200, 0x0200, 0x0201, 0x0702, 0x0001, 0x0013, + 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x001a, 0x0018, 0x0003, 0x0016, + 0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -139,7 +140,7 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x081b, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836, @@ -156,16 +157,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x050b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0851, 0x0857, 0x0845, 0x0852, 0x0854, 0x0859, 0x0855, 0x0849, + 0x084f, 0x0850, 0x0200, 0x0200, 0x0201, 0x0702, 0x0841, 0x0853, + 0x0844, 0x0846, 0x0847, 0x0848, 0x084a, 0x084b, 0x084c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x085a, 0x0858, 0x0843, 0x0856, + 0x0842, 0x084e, 0x084d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -173,16 +174,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869, + 0x086f, 0x0870, 0x0200, 0x0200, 0x0201, 0x0702, 0x0861, 0x0873, + 0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x087a, 0x0878, 0x0863, 0x0876, + 0x0862, 0x086e, 0x086d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -190,16 +191,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0851, 0x0857, 0x0845, 0x0852, 0x0854, 0x0859, 0x0855, 0x0849, + 0x084f, 0x0850, 0x0200, 0x0200, 0x0201, 0x0702, 0x0841, 0x0853, + 0x0844, 0x0846, 0x0847, 0x0848, 0x084a, 0x084b, 0x084c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x085a, 0x0858, 0x0843, 0x0856, + 0x0842, 0x084e, 0x084d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -207,16 +208,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809, + 0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813, + 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816, + 0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -224,16 +225,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809, + 0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813, + 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816, + 0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -241,16 +242,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809, + 0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813, + 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816, + 0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -258,16 +259,16 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, + 0x0811, 0x0817, 0x0805, 0x0812, 0x0814, 0x0819, 0x0815, 0x0809, + 0x080f, 0x0810, 0x0200, 0x0200, 0x0201, 0x0702, 0x0801, 0x0813, + 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, 0x080b, 0x080c, 0x0200, + 0x0200, 0x0200, 0x0700, 0x0200, 0x081a, 0x0818, 0x0803, 0x0816, + 0x0802, 0x080e, 0x080d, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c, 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307, 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301, @@ -275,7 +276,7 @@ u_short key_map[NR_KEYMAPS][NR_KEYS] = { 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603, 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x011a, 0x010c, 0x010d, 0x011b, 0x011c, 0x0110, 0x0311, 0x011d, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, }, }; @@ -307,6 +308,10 @@ char func_buf[FUNC_BUFSIZE] = { '\033', '[', '4', '~', 0, '\033', '[', '5', '~', 0, '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + 0, + 0, + '\033', '[', 'P', 0, 0, 0, 0, @@ -343,9 +348,52 @@ char *func_table[NR_FUNC] = { func_buf + 135, func_buf + 140, func_buf + 145, - func_buf + 146, - func_buf + 147, - func_buf + 148, func_buf + 149, func_buf + 150, + func_buf + 151, + func_buf + 155, + func_buf + 156, + func_buf + 157, + func_buf + 158, + func_buf + 159, + func_buf + 160, }; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', '\300'}, {'`', 'a', '\340'}, + {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, + {'^', 'A', '\302'}, {'^', 'a', '\342'}, + {'~', 'A', '\303'}, {'~', 'a', '\343'}, + {'"', 'A', '\304'}, {'"', 'a', '\344'}, + {'O', 'A', '\305'}, {'o', 'a', '\345'}, + {'0', 'A', '\305'}, {'0', 'a', '\345'}, + {'A', 'A', '\305'}, {'a', 'a', '\345'}, + {'A', 'E', '\306'}, {'a', 'e', '\346'}, + {',', 'C', '\307'}, {',', 'c', '\347'}, + {'`', 'E', '\310'}, {'`', 'e', '\350'}, + {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, + {'^', 'E', '\312'}, {'^', 'e', '\352'}, + {'"', 'E', '\313'}, {'"', 'e', '\353'}, + {'`', 'I', '\314'}, {'`', 'i', '\354'}, + {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, + {'^', 'I', '\316'}, {'^', 'i', '\356'}, + {'"', 'I', '\317'}, {'"', 'i', '\357'}, + {'-', 'D', '\320'}, {'-', 'd', '\360'}, + {'~', 'N', '\321'}, {'~', 'n', '\361'}, + {'`', 'O', '\322'}, {'`', 'o', '\362'}, + {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, + {'^', 'O', '\324'}, {'^', 'o', '\364'}, + {'~', 'O', '\325'}, {'~', 'o', '\365'}, + {'"', 'O', '\326'}, {'"', 'o', '\366'}, + {'/', 'O', '\330'}, {'/', 'o', '\370'}, + {'`', 'U', '\331'}, {'`', 'u', '\371'}, + {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, + {'^', 'U', '\333'}, {'^', 'u', '\373'}, + {'"', 'U', '\334'}, {'"', 'u', '\374'}, + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, + {'T', 'H', '\336'}, {'t', 'h', '\376'}, + {'s', 's', '\337'}, {'"', 'y', '\377'}, + {'s', 'z', '\337'}, {'i', 'j', '\377'}, +}; + +unsigned int accent_table_size = 68; diff --git a/drivers/char/defkeymap.map b/drivers/char/defkeymap.map index cf43e69..47d39ab 100644 --- a/drivers/char/defkeymap.map +++ b/drivers/char/defkeymap.map @@ -5,6 +5,7 @@ keycode 2 = one exclam alt keycode 2 = Meta_one keycode 3 = two at at control keycode 3 = nul + shift control keycode 3 = nul alt keycode 3 = Meta_two keycode 4 = three numbersign control keycode 4 = Escape @@ -30,6 +31,7 @@ keycode 11 = zero parenright braceright alt keycode 11 = Meta_zero keycode 12 = minus underscore backslash control keycode 12 = Control_underscore + shift control keycode 12 = Control_underscore alt keycode 12 = Meta_minus keycode 13 = equal plus alt keycode 13 = Meta_equal @@ -54,7 +56,7 @@ keycode 27 = bracketright braceright asciitilde control keycode 27 = Control_bracketright alt keycode 27 = Meta_bracketright keycode 28 = Return - alt keycode 28 = 0x080d + alt keycode 28 = Meta_Control_m keycode 29 = Control keycode 30 = a keycode 31 = s @@ -87,6 +89,7 @@ keycode 50 = m keycode 51 = comma less alt keycode 51 = Meta_comma keycode 52 = period greater + control keycode 52 = Compose alt keycode 52 = Meta_period keycode 53 = slash question control keycode 53 = Delete @@ -196,14 +199,14 @@ keycode 110 = Insert keycode 111 = Remove altgr control keycode 111 = Boot control alt keycode 111 = Boot -keycode 112 = -keycode 113 = -keycode 114 = -keycode 115 = -keycode 116 = -keycode 117 = -keycode 118 = -keycode 119 = +keycode 112 = Macro +keycode 113 = F13 +keycode 114 = F14 +keycode 115 = Help +keycode 116 = Do +keycode 117 = F17 +keycode 118 = KP_MinPlus +keycode 119 = Pause keycode 120 = keycode 121 = keycode 122 = @@ -238,9 +241,81 @@ string Remove = "\033[3~" string Select = "\033[4~" string Prior = "\033[5~" string Next = "\033[6~" +string Help = "" +string Do = "" +string Macro = "\033[M" +string Pause = "\033[P" string F21 = "" string F22 = "" string F23 = "" string F24 = "" string F25 = "" string F26 = "" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff --git a/drivers/char/diacr.h b/drivers/char/diacr.h new file mode 100644 index 0000000..1c1a3ff --- /dev/null +++ b/drivers/char/diacr.h @@ -0,0 +1,8 @@ +#ifndef _DIACR_H +#define _DIACR_H +#include <linux/kd.h> + +extern struct kbdiacr accent_table[]; +extern unsigned int accent_table_size; + +#endif /* _DIACR_H */ diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 0b1ae66..c509482 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -7,8 +7,8 @@ * the assembly version by Linus (with diacriticals added) * * Some additional features added by Christoph Niemann (ChN), March 1993 - * * Loadable keymaps by Risto Kankkunen, May 1993 + * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 */ #define KEYBOARD_IRQ 1 @@ -25,6 +25,10 @@ #include <asm/bitops.h> +#include "diacr.h" + +#define SIZE(x) (sizeof(x)/sizeof((x)[0])) + #ifndef KBD_DEFFLAGS #ifdef CONFIG_KBD_META @@ -64,8 +68,11 @@ __asm__ __volatile__("int $0x21") unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ -unsigned long kbd_dead_keys = 0; -unsigned long kbd_prev_dead_keys = 0; +/* + * global state includes the following, and various static variables + * in this module: prev_scancode, shift_state, diacr, npadch, + * dead_key_next, last_console + */ /* shift state counters.. */ static unsigned char k_down[NR_SHIFT] = {0, }; @@ -74,6 +81,10 @@ static unsigned long key_down[8] = { 0, }; static int want_console = -1; static int last_console = 0; /* last used VC */ +static int dead_key_next = 0; +static int shift_state = 0; +static int npadch = -1; /* -1 or number assembled on pad */ +static unsigned char diacr = 0; static char rep = 0; /* flag telling character repeat */ struct kbd_struct kbd_table[NR_CONSOLES]; static struct kbd_struct * kbd = kbd_table; @@ -103,25 +114,18 @@ static k_hand key_handler[] = { /* maximum values each key_handler can handle */ const int max_vals[] = { - 255, NR_FUNC - 1, 13, 16, 4, 255, 3, NR_SHIFT, + 255, NR_FUNC - 1, 14, 17, 4, 255, 3, NR_SHIFT, 255, 9, 3 }; -const int NR_TYPES = (sizeof(max_vals) / sizeof(int)); - -#define E0_BASE 96 - -static int shift_state = 0; -static int diacr = -1; -static int npadch = 0; +const int NR_TYPES = SIZE(max_vals); static void put_queue(int); -static unsigned int handle_diacr(unsigned int); +static unsigned char handle_diacr(unsigned char); +/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ static struct pt_regs * pt_regs; -static inline void translate(unsigned char scancode); - static inline void kb_wait(void) { int i; @@ -137,15 +141,67 @@ static inline void send_cmd(unsigned char c) outb(c,0x64); } +/* + * Translation of escaped scancodes to keysyms. + * This should be user-settable. + */ +#define E0_BASE 96 + +#define E0_KPENTER (E0_BASE+0) +#define E0_RCTRL (E0_BASE+1) +#define E0_KPSLASH (E0_BASE+2) +#define E0_PRSCR (E0_BASE+3) +#define E0_RALT (E0_BASE+4) +#define E0_BREAK (E0_BASE+5) /* (control-pause) */ +#define E0_HOME (E0_BASE+6) +#define E0_UP (E0_BASE+7) +#define E0_PGUP (E0_BASE+8) +#define E0_LEFT (E0_BASE+9) +#define E0_RIGHT (E0_BASE+10) +#define E0_END (E0_BASE+11) +#define E0_DOWN (E0_BASE+12) +#define E0_PGDN (E0_BASE+13) +#define E0_INS (E0_BASE+14) +#define E0_DEL (E0_BASE+15) +/* BTC */ +#define E0_MACRO (E0_BASE+16) +/* LK450 */ +#define E0_F13 (E0_BASE+17) +#define E0_F14 (E0_BASE+18) +#define E0_HELP (E0_BASE+19) +#define E0_DO (E0_BASE+20) +#define E0_F17 (E0_BASE+21) +#define E0_KPMINPLUS (E0_BASE+22) + +#define E1_PAUSE (E0_BASE+23) + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + static void keyboard_interrupt(int int_pt_regs) { unsigned char scancode; + static unsigned int prev_scancode = 0; /* remember E0, E1 */ + char up_flag; /* 0 or 0200 */ + char raw_mode; pt_regs = (struct pt_regs *) int_pt_regs; - kbd_prev_dead_keys |= kbd_dead_keys; - if (!kbd_dead_keys) - kbd_prev_dead_keys = 0; - kbd_dead_keys = 0; send_cmd(0xAD); /* disable keyboard */ kb_wait(); if ((inb_p(0x64) & kbd_read_mask) != 0x01) @@ -160,113 +216,106 @@ static void keyboard_interrupt(int int_pt_regs) goto end_kbd_intr; } tty = TTY_TABLE(0); - kbd = kbd_table + fg_console; - if (vc_kbd_flag(kbd,VC_RAW)) { - memset(k_down, 0, sizeof(k_down)); - memset(key_down, 0, sizeof(key_down)); - shift_state = 0; - put_queue(scancode); + kbd = kbd_table + fg_console; + if ((raw_mode = vc_kbd_flag(kbd,VC_RAW))) { + put_queue(scancode); + /* we do not return yet, because we want to maintain + the key_down array, so that we have the correct + values when finishing RAW mode or when changing VT's */ + } + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; goto end_kbd_intr; - } else - translate(scancode); -end_kbd_intr: - send_cmd(0xAE); /* enable keyboard */ - return; -} - -static inline void translate(unsigned char scancode) -{ - char break_flag; - static unsigned char e0_keys[] = { - 0x1c, /* keypad enter */ - 0x1d, /* right control */ - 0x35, /* keypad slash */ - 0x37, /* print screen */ - 0x38, /* right alt */ - 0x46, /* break (control-pause) */ - 0x47, /* editpad home */ - 0x48, /* editpad up */ - 0x49, /* editpad pgup */ - 0x4b, /* editpad left */ - 0x4d, /* editpad right */ - 0x4f, /* editpad end */ - 0x50, /* editpad dn */ - 0x51, /* editpad pgdn */ - 0x52, /* editpad ins */ - 0x53, /* editpad del */ -#ifdef LK450 - 0x3d, /* f13 */ - 0x3e, /* f14 */ - 0x3f, /* help */ - 0x40, /* do */ - 0x41, /* f17 */ - 0x4e /* keypad minus/plus */ -#endif -#ifdef BTC - 0x6f /* macro */ -#endif - }; + } - if (scancode == 0xe0) { - set_kbd_dead(KGD_E0); - return; - } - if (scancode == 0xe1) { - set_kbd_dead(KGD_E1); - return; - } + /* + * Convert scancode to keysym, using prev_scancode. + */ + up_flag = (scancode & 0200); + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + goto end_kbd_intr; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + scancode = E1_PAUSE; + prev_scancode = 0; + } else { + printk("keyboard: unknown e1 escape sequence\n"); + prev_scancode = 0; + goto end_kbd_intr; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + goto end_kbd_intr; + + if (e0_keys[scancode]) + scancode = e0_keys[scancode]; + else if (!raw_mode) { + printk("keyboard: unknown scancode e0 %02x\n", scancode); + goto end_kbd_intr; + } + } + } else if (scancode >= E0_BASE && !raw_mode) { + printk("keyboard: scancode (%02x) not in range 00 - %2x\n", + scancode, E0_BASE - 1); + goto end_kbd_intr; + } + /* - * The keyboard maintains its own internal caps lock and num lock - * statuses. In caps lock mode E0 AA precedes make code and E0 2A - * follows break code. In num lock mode, E0 2A precedes make - * code and E0 AA follows break code. We do our own book-keeping, - * so we will just ignore these. + * At this point the variable `scancode' contains the keysym. + * We keep track of the up/down status of the key, and + * return the keysym if in MEDIUMRAW mode. + * (Note: earlier kernels had a bug and did not pass the up/down + * bit to applications.) */ - if (kbd_dead(KGD_E0) && (scancode == 0x2a || scancode == 0xaa || - scancode == 0x36 || scancode == 0xb6)) - return; - - /* map two byte scancodes into one byte id's */ - - break_flag = scancode > 0x7f; - scancode &= 0x7f; - - if (kbd_dead(KGD_E0)) { - int i; - for (i = 0; i < sizeof(e0_keys); i++) - if (scancode == e0_keys[i]) { - scancode = E0_BASE + i; - i = -1; - break; - } - if (i != -1) { -#if 0 - printk("keyboard: unknown scancode e0 %02x\n", scancode); -#endif - return; - } - } else if (scancode >= E0_BASE) { -#if 0 - printk("keyboard: scancode (%02x) not in range 00 - %2x\n", scancode, E0_BASE - 1); -#endif - return; - } - rep = 0; - if (break_flag) - clear_bit(scancode, key_down); - else - rep = set_bit(scancode, key_down); + if (up_flag) { + clear_bit(scancode, key_down); + rep = 0; + } else + rep = set_bit(scancode, key_down); + + if (raw_mode) + goto end_kbd_intr; - if (vc_kbd_flag(kbd, VC_MEDIUMRAW)) { - put_queue(scancode); - return; - } + if (vc_kbd_flag(kbd, VC_MEDIUMRAW)) { + put_queue(scancode + up_flag); + goto end_kbd_intr; + } + + /* + * Small change in philosophy: earlier we defined repetition by + * rep = scancode == prev_keysym; + * prev_keysym = scancode; + * but now by the fact that the depressed key was down already. + * Does this ever make a difference? + */ /* - * Repeat a key only if the input buffers are empty or the - * characters get echoed locally. This makes key repeat usable - * with slow applications and under heavy loads. + * Repeat a key only if the input buffers are empty or the + * characters get echoed locally. This makes key repeat usable + * with slow applications and under heavy loads. */ if (!rep || (vc_kbd_flag(kbd,VC_REPEAT) && tty && @@ -275,8 +324,11 @@ static inline void translate(unsigned char scancode) u_short key_code; key_code = key_map[shift_state][scancode]; - (*key_handler[key_code >> 8])(key_code & 0xff, break_flag); + (*key_handler[key_code >> 8])(key_code & 0xff, up_flag); } + +end_kbd_intr: + send_cmd(0xAE); /* enable keyboard */ } static void put_queue(int ch) @@ -371,7 +423,7 @@ static void hold(void) /* pressing srcoll lock 2nd time sends ^Q, ChN */ put_queue(START_CHAR(tty)); else - /* pressing srcoll lock 1st time sends ^S, ChN */ + /* pressing scroll lock 1st time sends ^S, ChN */ put_queue(STOP_CHAR(tty)); chg_vc_kbd_flag(kbd,VC_SCROLLOCK); } @@ -420,21 +472,25 @@ static void boot_it(void) ctrl_alt_del(); } +static void compose(void) +{ + dead_key_next = 1; +} static void do_spec(unsigned char value, char up_flag) { typedef void (*fnp)(void); fnp fn_table[] = { NULL, enter, show_ptregs, show_mem, - show_state, send_intr, lastcons, caps_toggle, + show_state, send_intr, lastcons, caps_toggle, num, hold, scrll_forw, scrll_back, - boot_it, caps_on + boot_it, caps_on, compose }; - if (value >= sizeof(fn_table)/sizeof(fnp)) - return; if (up_flag) return; + if (value >= SIZE(fn_table)) + return; if (!fn_table[value]) return; fn_table[value](); @@ -445,7 +501,14 @@ static void do_self(unsigned char value, char up_flag) if (up_flag) return; /* no action, if this is a key release */ - value = handle_diacr(value); + if (diacr) + value = handle_diacr(value); + + if (dead_key_next) { + dead_key_next = 0; + diacr = value; + return; + } /* kludge... but works for ISO 8859-1 */ if (vc_kbd_flag(kbd,VC_CAPSLOCK)) @@ -457,8 +520,13 @@ static void do_self(unsigned char value, char up_flag) put_queue(value); } +#define A_GRAVE '`' +#define A_ACUTE '\'' +#define A_CFLEX '^' +#define A_TILDE '~' +#define A_DIAER '"' static unsigned char ret_diacr[] = - {'`', '\'', '^', '~', '"' }; /* Must not end with 0 */ + {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER }; /* If a dead key pressed twice, output a character corresponding to it, */ /* otherwise just remember the dead key. */ @@ -468,52 +536,34 @@ static void do_dead(unsigned char value, char up_flag) if (up_flag) return; - if (diacr == value) { /* pressed twice */ - diacr = -1; - put_queue(ret_diacr[value]); - return; - } + value = ret_diacr[value]; + if (diacr == value) { /* pressed twice */ + diacr = 0; + put_queue(value); + return; + } diacr = value; } -/* If no pending dead key, return the character unchanged. Otherwise, */ -/* if space if pressed, return a character corresponding the pending */ + +/* If space is pressed, return the character corresponding the pending */ /* dead key, otherwise try to combine the two. */ -unsigned int handle_diacr(unsigned int ch) +unsigned char handle_diacr(unsigned char ch) { - static unsigned char accent_table[5][64] = { - " \300BCD\310FGH\314JKLMN\322PQRST\331VWXYZ[\\]^_" - "`\340bcd\350fgh\354jklmn\362pqrst\371vwxyz{|}~", /* accent grave */ - - " \301BCD\311FGH\315JKLMN\323PQRST\332VWX\335Z[\\]^_" - "`\341bcd\351fgh\355jklmn\363pqrst\372vwx\375z{|}~", /* accent acute */ - - " \302BCD\312FGH\316JKLMN\324PQRST\333VWXYZ[\\]^_" - "`\342bcd\352fgh\356jklmn\364pqrst\373vwxyz{|}~", /* circumflex */ - - " \303BCDEFGHIJKLM\321\325PQRSTUVWXYZ[\\]^_" - "`\343bcdefghijklm\361\365pqrstuvwxyz{|}~", /* tilde */ - - " \304BCD\313FGH\317JKLMN\326PQRST\334VWXYZ[\\]^_" - "`\344bcd\353fgh\357jklmn\366pqrst\374vwx\377z{|}~" /* dieresis */ - }; - int d = diacr, e; + int d = diacr; + int i; - if (diacr == -1) - return ch; + diacr = 0; + if (ch == ' ') + return d; - diacr = -1; - if (ch == ' ') - return ret_diacr[d]; + for (i = 0; i < accent_table_size; i++) + if(accent_table[i].diacr == d && accent_table[i].base == ch) + return accent_table[i].result; - if (ch >= 64 && ch <= 122) { - e = accent_table[d][ch - 64]; - if (e != ch) - return e; - } - put_queue(ret_diacr[d]); - return ch; + put_queue(d); + return ch; } static void do_cons(unsigned char value, char up_flag) @@ -527,13 +577,16 @@ static void do_fn(unsigned char value, char up_flag) { if (up_flag) return; - puts_queue(func_table[value]); + if (value < SIZE(func_table)) + puts_queue(func_table[value]); + else + printk("do_fn called with value=%d\n", value); } static void do_pad(unsigned char value, char up_flag) { - static char *pad_chars = "0123456789+-*/\015,."; - static char *app_map = "pqrstuvwxylSRQMnn"; + static char *pad_chars = "0123456789+-*/\015,.?"; + static char *app_map = "pqrstuvwxylSRQMnn?"; if (up_flag) return; /* no action, if this is a key release */ @@ -610,6 +663,8 @@ static void do_shift(unsigned char value, char up_flag) } if (up_flag) { + /* handle the case that two shift or control + keys are depressed simultaneously */ if (k_down[value]) k_down[value]--; } else @@ -621,12 +676,37 @@ static void do_shift(unsigned char value, char up_flag) shift_state &= ~ (1 << value); /* kludge */ - if (up_flag && shift_state != old_state && npadch != 0) { + if (up_flag && shift_state != old_state && npadch != -1) { put_queue(npadch); - npadch = 0; + npadch = -1; } } +/* called after returning from RAW mode or when changing consoles - + recompute k_down[] and shift_state from key_down[] */ +void compute_shiftstate(void) +{ + int i, j, k, sym, val; + + shift_state = 0; + for(i=0; i < SIZE(k_down); i++) + k_down[i] = 0; + + for(i=0; i < SIZE(key_down); i++) + if(key_down[i]) { /* skip this word if not a single bit on */ + k = (i<<5); + for(j=0; j<32; j++,k++) + if(test_bit(k, key_down)) { + sym = key_map[0][k]; + if(KTYP(sym) == KT_SHIFT) { + val = KVAL(sym); + k_down[val]++; + shift_state |= (1<<val); + } + } + } +} + static void do_meta(unsigned char value, char up_flag) { if (up_flag) @@ -644,7 +724,10 @@ static void do_ascii(unsigned char value, char up_flag) if (up_flag) return; - npadch = (npadch * 10 + value) % 1000; + if (npadch == -1) + npadch = value; + else + npadch = (npadch * 10 + value) % 1000; } /* done stupidly to avoid coding in any dependencies of diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 44f094b..5422dcc 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -586,57 +586,41 @@ void copy_to_cooked(struct tty_struct * tty) if (c == __DISABLED_CHAR) tty->lnext = 1; if (L_CANON(tty) && !tty->lnext) { - if (c == KILL_CHAR(tty) || c == WERASE_CHAR(tty)) { + if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || c == WERASE_CHAR(tty)) { int seen_alnums = (c == WERASE_CHAR(tty)) ? 0 : -1; + int cc; - /* deal with killing the input line */ + /* deal with killing in the input line */ while(!(EMPTY(&tty->secondary) || - (c=LAST(&tty->secondary))==10 || + (cc=LAST(&tty->secondary))==10 || ((EOF_CHAR(tty) != __DISABLED_CHAR) && - (c==EOF_CHAR(tty))))) { + (cc==EOF_CHAR(tty))))) { /* if killing just a word, kill all non-alnum chars, then all alnum chars. */ if (seen_alnums >= 0) { - if (isalnum(c)) + if (isalnum(cc)) seen_alnums++; else if (seen_alnums) break; } if (L_ECHO(tty)) { - if (c<32) { + int ct = 1; + if (cc < 32) + ct = (L_ECHOCTL(tty) ? 2 : 0); + while(ct--) { put_tty_queue('\b', &tty->write_q); put_tty_queue(' ', &tty->write_q); put_tty_queue('\b',&tty->write_q); } - put_tty_queue('\b',&tty->write_q); - put_tty_queue(' ',&tty->write_q); - put_tty_queue('\b',&tty->write_q); } DEC(tty->secondary.head); + if(c == ERASE_CHAR(tty)) + break; } continue; } - if (c == ERASE_CHAR(tty)) { - if (EMPTY(&tty->secondary) || - (c=LAST(&tty->secondary))==10 || - ((EOF_CHAR(tty) != __DISABLED_CHAR) && - (c==EOF_CHAR(tty)))) - continue; - if (L_ECHO(tty)) { - if (c<32) { - put_tty_queue('\b',&tty->write_q); - put_tty_queue(' ',&tty->write_q); - put_tty_queue('\b',&tty->write_q); - } - put_tty_queue('\b',&tty->write_q); - put_tty_queue(' ',&tty->write_q); - put_tty_queue('\b',&tty->write_q); - } - DEC(tty->secondary.head); - continue; - } if (c == LNEXT_CHAR(tty)) { tty->lnext = 1; if (L_ECHO(tty)) { diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d1069be..28b5e40 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2,6 +2,7 @@ * kernel/chr_drv/vt.c * * Copyright (C) 1992 obz under the linux copyright + * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 */ #include <linux/types.h> @@ -19,6 +20,7 @@ #include <asm/segment.h> #include "vt_kern.h" +#include "diacr.h" /* * Console (vt and kd) routines, as defined by USL SVR4 manual, and by @@ -37,6 +39,7 @@ struct vt_struct vt_cons[NR_CONSOLES]; asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); +extern void compute_shiftstate(void); extern void change_console(unsigned int new_console); extern void complete_change_console(unsigned int new_console); extern int vt_waitactive(void); @@ -212,6 +215,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } else if (arg == K_XLATE) { clr_vc_kbd_flag(kbd, VC_RAW); clr_vc_kbd_flag(kbd, VC_MEDIUMRAW); + compute_shiftstate(); } else if (arg == K_MEDIUMRAW) { clr_vc_kbd_flag(kbd, VC_RAW); set_vc_kbd_flag(kbd, VC_MEDIUMRAW); @@ -278,10 +282,17 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry)); if (i) return i; - if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC) + if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC || i < 0) return -EINVAL; q = a->kb_string; - for (p = func_table[i]; *p; p++) + p = func_table[i]; + if(!p) { + /* beware of tables generated for a smaller NR_FUNC */ + printk("KDGKBSENT error: func_table[%d] is nil.\n", + i); + return -EINVAL; + } + for ( ; *p; p++) put_fs_byte(*p, q++); put_fs_byte(0, q); return 0; @@ -301,7 +312,14 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return i; if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC) return -EINVAL; - delta = -strlen(func_table[i]); + q = func_table[i]; + if (!q) { + /* beware of tables generated for a smaller NR_FUNC */ + printk("KDSKBSENT error: func_table[%d] is nil.\n", + i); + return -EINVAL; + } + delta = -strlen(q); for (p = a->kb_string; get_fs_byte(p); p++) delta++; first_free = func_table[NR_FUNC - 1] + @@ -317,6 +335,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, func_table[i + 1], first_free - func_table[i + 1]); for (k = i + 1; k < NR_FUNC; k++) + if (func_table[k]) /* just to be sure */ func_table[k] += delta; } for (p = a->kb_string, q = func_table[i]; ; p++, q++) @@ -325,6 +344,35 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return 0; } + case KDGKBDIACR: + { + struct kbdiacrs *a = (struct kbdiacrs *)arg; + + i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs)); + if (i) + return i; + put_fs_long(accent_table_size, &a->kb_cnt); + memcpy_tofs(a->kbdiacr, accent_table, + accent_table_size*sizeof(struct kbdiacr)); + return 0; + } + + case KDSKBDIACR: + { + struct kbdiacrs *a = (struct kbdiacrs *)arg; + unsigned int ct; + + i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs)); + if (i) + return i; + ct = get_fs_long(&a->kb_cnt); + if (ct >= MAX_DIACR) + return -EINVAL; + accent_table_size = ct; + memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr)); + return 0; + } + case KDGETLED: i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char)); if (i) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 51c1c8d..e6d1140 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -8,8 +8,8 @@ include CONFIG NETDRV_OBJS := net.a(Space.o) net.a(auto_irq.o) net.a(net_init.o) -CFLAGS := $(CFLAGS) -I../../net/inet -CPP := $(CPP) -I../../net/inet +CFLAGS := $(CFLAGS) -I../../net/inet -I../../net/socket -I../../net +CPP := $(CPP) -I../../net/inet -I../../net/socket -I../../net # The point of the makefile... all: net.a diff --git a/drivers/net/slip.c b/drivers/net/slip.c index b900956..a3f2e1c 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -18,6 +18,7 @@ * Pauline Middelink : Slip driver fixes. * Alan Cox : Honours the old SL_COMPRESSED flag * Alan Cox : KISS AX.25 and AXUI IP support + * Michael Riepe : Automatic CSLIP recognition added */ #include <asm/segment.h> @@ -39,9 +40,9 @@ #include <linux/tty.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #ifdef CONFIG_AX25 -#include "ax25.h" +#include "ax25/ax25.h" #endif #include "eth.h" #include "ip.h" @@ -135,10 +136,14 @@ sl_initialize(struct slip *sl, struct device *dev) sl->sending = 0; sl->escape = 0; sl->flags = 0; +#ifdef SL_ADAPTIVE + sl->mode = SL_MODE_ADAPTIVE; /* automatic CSLIP recognition */ +#else #ifdef SL_COMPRESSED - sl->mode = SL_MODE_CSLIP; /* Default */ + sl->mode = SL_MODE_CSLIP | SL_MODE_ADAPTIVE; /* Default */ #else sl->mode = SL_MODE_SLIP; /* Default for non compressors */ +#endif #endif sl->line = dev->base_addr; @@ -351,8 +356,15 @@ sl_bump(struct slip *sl) int count; count = sl->rcount; - if (sl->mode & SL_MODE_CSLIP) { + if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) { if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) { +#if 1 + /* ignore compressed packets when CSLIP is off */ + if (!(sl->mode & SL_MODE_CSLIP)) { + printk("SLIP: compressed packet ignored\n"); + return; + } +#endif /* make sure we've reserved enough space for uncompress to use */ save_flags(flags); cli(); @@ -374,6 +386,11 @@ sl_bump(struct slip *sl) return; } } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) { + if (!(sl->mode & SL_MODE_CSLIP)) { + /* turn on header compression */ + sl->mode |= SL_MODE_CSLIP; + printk("SLIP: header compression turned on\n"); + } sl->rbuff[0] &= 0x4f; if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) { sl->errors++; @@ -485,6 +502,21 @@ sl_encaps(struct slip *sl, unsigned char *icp, int len) } } +/*static void sl_hex_dump(unsigned char *x,int l) +{ + int n=0; + printk("sl_xmit: (%d bytes)\n",l); + while(l) + { + printk("%2X ",(int)*x++); + l--; + n++; + if(n%32==0) + printk("\n"); + } + if(n%32) + printk("\n"); +}*/ /* Encapsulate an IP datagram and kick it into a TTY queue. */ static int @@ -525,6 +557,7 @@ sl_xmit(struct sk_buff *skb, struct device *dev) } #endif sl_lock(sl); +/* sl_hex_dump((unsigned char *)(skb+1),skb->len);*/ sl_encaps(sl, (unsigned char *) (skb + 1), skb->len); if (skb->free) kfree_skb(skb, FREE_WRITE); } diff --git a/drivers/net/slip.h b/drivers/net/slip.h index 7f04909..b0be292 100644 --- a/drivers/net/slip.h +++ b/drivers/net/slip.h @@ -66,8 +66,9 @@ struct slip { #define SL_MODE_SLIP 0 #define SL_MODE_CSLIP 1 #define SL_MODE_SLIP6 2 /* Matt Dillon's printable slip */ -#define SL_MODE_CSLIP6 (SL_MODE_SLIP|SL_MODE_CSLIP) +#define SL_MODE_CSLIP6 (SL_MODE_SLIP6|SL_MODE_CSLIP) #define SL_MODE_AX25 4 +#define SL_MODE_ADAPTIVE 8 int xdata,xbits; /* 6 bit slip controls */ }; diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index 4423188..c572fd0 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -315,6 +315,7 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigne } /* Now mask and substitute our own volume and reuse the rest */ + buffer[0] = 0; /* Clear reserved field */ buffer[21] = volctrl.channel0 & mask[21]; buffer[23] = volctrl.channel1 & mask[23]; diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 1f3760a..d8518d7 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -25,7 +25,7 @@ all: local.h sound.a /usr/include/sys/soundcard.h: @echo "WARNING! Your /usr/include/sys/soundcard.h not found." - @echo "Please make a new /usr/include/sys/soundcard.h containing + @echo "Please make a new /usr/include/sys/soundcard.h containing" @echo "just a line #include <linux/soundcard.h>" sound.a: $(OBJS) diff --git a/drivers/sound/sb_dsp.c b/drivers/sound/sb_dsp.c index e66fe32..d6297e5 100644 --- a/drivers/sound/sb_dsp.c +++ b/drivers/sound/sb_dsp.c @@ -682,7 +682,7 @@ sb_dsp_init (long mem_start, struct address_info *hw_config) sb_mixer_init(major); #endif -#ifndef EXCLUDE_YM8312 +#ifndef EXCLUDE_YM3812 if (major > 3 || (major == 3 && minor > 0)) /* SB Pro2 or later */ { enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); diff --git a/fs/ext2/CHANGES b/fs/ext2/CHANGES new file mode 100644 index 0000000..258bf23 --- /dev/null +++ b/fs/ext2/CHANGES @@ -0,0 +1,92 @@ +Changes from version 0.4a to version 0.4b +========================================= + - Copyrights changed to include the name of my laboratory. + - Clean up of balloc.c and ialloc.c. + - More consistency checks. + - Block preallocation added by Stephen Tweedie. + - Direct reads of directories disallowed if CONFIG_EXT2_FS_DIR_READ not + defined. + - Readahead implemented in readdir by Stephen Tweedie. + - Bugs in block and inodes allocation fixed. + - Readahead implemented in ext2_find_entry by Chip Salzenberg. + - New mount options: + `check=none|normal|strict' + `debug' + `errors=continue|remount-ro|panic' + `grpid', `bsdgroups' + `nocheck' + `nogrpid', `sysvgroups' + - truncate() now tries to deallocate contigous blocks in a single call + to ext2_free_blocks(). + - lots of cosmetic changes. + +Changes from version 0.4 to version 0.4a +======================================== + - the `sync' option support is now complete. Version 0.4 was not + supporting it when truncating a file. I have tested the synchronous + writes and they work but they make the system very slow :-( I have + to work again on this to make it faster. + - when detecting an error on a mounted filesystem, version 0.4 used + to try to write a flag in the super block even if the filesystem had + been mounted read-only. This is fixed. + - the `sb=#' option now causes the kernel code to use the filesystem + descriptors located at block #+1. Version 0.4 used the superblock + backup located at block # but used the main copy of the descriptors. + - a new file attribute `S' is supported. This attribute causes + synchronous writes but is applied to a file not to the entire file + system (thanks to Michael Kraehe <kraehe@bakunin.north.de> for + suggesting it). + - the directory cache is inhibited by default. The cache management + code seems to be buggy and I have to look at it carefully before + using it again. + - deleting a file with the `s' attribute (secure deletion) causes its + blocks to be overwritten with random values not with zeros (thanks to + Michael A. Griffith <grif@cs.ucr.edu> for suggesting it). + - lots of cosmetic changes have been made. + +Changes from version 0.3 to version 0.4 +======================================= + - Three new mount options are supported: `check', `sync' and `sb=#'. + `check' tells the kernel code to make more consistency checks + when the file system is mounted. Currently, the kernel code checks + that the blocks and inodes bitmaps are consistent with the free + blocks and inodes counts. More checks will be added in future + releases. + `sync' tells the kernel code to use synchronous writes when updating + an inode, a bitmap, a directory entry or an indirect block. This + can make the file system much slower but can be a big win for files + recovery in case of a crash (and we can now say to the BSD folks + that Linux also supports synchronous updates :-). + `sb=#' tells the kernel code to use an alternate super block instead + of its master copy. `#' is the number of the block (counted in + 1024 bytes blocks) which contains the alternate super block. + An ext2 file system typically contains backups of the super block + at blocks 8193, 16385, and so on. + - I have change the meaning of the valid flag used by e2fsck. it + now contains the state of the file system. If the kernel code + detects an inconsistency while the file system is mounted, it flags + it as erroneous and e2fsck will detect that on next run. + - The super block now contains a mount counter. This counter is + incremented each time the file system is mounted read/write. When + this counter becomes bigger than a maximal mount counts (also stored + in the super block), e2fsck checks the file system, even if it had + been unmounted cleany, and resets this counter to 0. + - File attributes are now supported. One can associate a set of + attributes to a file. Three attributes are defined: + `c': the file is marked for automatic compression, + `s': the file is marked for secure deletion: when the file is + deleted, its blocks are zeroed and written back to the disk, + `u': the file is marked for undeletion: when the file is deleted, + its contents are saved to allow a future undeletion. + Currently, only the `s' attribute is implemented in the kernel + code. Support for the other attributes will be added in a future + release. + - a few bugs related to times updates have been fixed by Bruce + Evans and me. + - a bug related to the links count of deleted inodes has been fixed. + Previous versions used to keep the links count set to 1 when a file + was deleted. The new version now sets links_count to 0 when deleting + the last link. + - a race condition when deallocating an inode has been fixed by + Stephen Tweedie. + diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 96d0ab1..984e574 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -462,7 +462,7 @@ repeat: EXT2_BLOCKS_PER_GROUP(sb)); if (j >= EXT2_BLOCKS_PER_GROUP(sb)) { ext2_error (sb, "ext2_new_block", - "Unable to locate free bit in block group %d", i); + "Free blocks count corrupted for block group %d", i); unlock_super (sb); return 0; } @@ -486,8 +486,8 @@ got_block: tmp == gdp->bg_inode_bitmap || in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group))) ext2_panic (sb, "ext2_new_block", - "Allocating block in system zone\nblock = %u", - tmp); + "Allocating block in system zone\n" + "block = %u", tmp); if (set_bit (j, bh->b_data)) { ext2_warning (sb, "ext2_new_block", diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index d44856b..de2144a 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -410,7 +410,7 @@ repeat: /* * That failed: try linear search for a free inode */ - i = dir->u.ext2_i.i_block_group + 2; + i = dir->u.ext2_i.i_block_group + 1; for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) { if (++i >= sb->u.ext2_sb.s_groups_count) i = 0; @@ -443,8 +443,17 @@ repeat: ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - } else + } else { + if (gdp->bg_free_inodes_count != 0) { + ext2_error (sb, "ext2_new_inode", + "Free inodes count corrupted in group %d", + i); + unlock_super (sb); + iput (inode); + return NULL; + } goto repeat; + } j += i * EXT2_INODES_PER_GROUP(sb) + 1; if (j < EXT2_FIRST_INO || j > es->s_inodes_count) { ext2_error (sb, "ext2_new_inode", @@ -467,9 +476,13 @@ repeat: inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->euid; - if ((dir->i_mode & S_ISGID) || test_opt (sb, GRPID)) + if (test_opt (sb, GRPID)) + inode->i_gid = dir->i_gid; + else if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; - else + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else inode->i_gid = current->egid; inode->i_dirt = 1; inode->i_ino = j; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index bbc1e52..3f0230e 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -299,7 +299,7 @@ repeat: } } if (!goal) - goal = bh->b_blocknr + 1; + goal = bh->b_blocknr; } tmp = ext2_alloc_block (inode, goal); if (!tmp) { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 766179f..d04caea 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -233,13 +233,15 @@ static int parse_options (char * options, unsigned long * sb_block, return 0; } } - else if (!strcmp (this_char, "grpid")) + else if (!strcmp (this_char, "grpid") || + !strcmp (this_char, "bsdgroups")) set_opt (*mount_options, GRPID); else if (!strcmp (this_char, "nocheck")) { clear_opt (*mount_options, CHECK_NORMAL); clear_opt (*mount_options, CHECK_STRICT); } - else if (!strcmp (this_char, "nogrpid")) + else if (!strcmp (this_char, "nogrpid") || + !strcmp (this_char, "sysvgroups")) clear_opt (*mount_options, GRPID); else if (!strcmp (this_char, "sb")) { if (!value || !*value) { @@ -271,7 +273,8 @@ static void ext2_setup_super (struct super_block * sb, else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS)) printk ("EXT2-fs warning: mounting fs with errors, " "running e2fsck is recommended\n"); - else if (es->s_mnt_count >= es->s_max_mnt_count) + else if (es->s_max_mnt_count >= 0 && + es->s_mnt_count >= es->s_max_mnt_count) printk ("EXT2-fs warning: maximal mount count reached, " "running e2fsck is recommended\n"); if (!(sb->s_flags & MS_RDONLY)) { @@ -390,8 +393,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, unlock_super (sb); brelse (bh); if (!silent) - printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n", - dev); + printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n", + MAJOR(dev), MINOR(dev)); return NULL; } sb->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size; @@ -477,8 +480,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, unlock_super (sb); brelse (bh); if (!silent) - printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n", - dev); + printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n", + MAJOR(dev), MINOR(dev)); return NULL; } if (sb->s_blocksize != bh->b_size) { @@ -522,7 +525,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, sb->s_dev = 0; unlock_super (sb); for (j = 0; j < i; j++) - brelse (sb->u.ext2_sb.s_group_desc[i]); + brelse (sb->u.ext2_sb.s_group_desc[j]); brelse (bh); printk ("EXT2-fs: unable to read group descriptors\n"); return NULL; @@ -532,7 +535,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, sb->s_dev = 0; unlock_super (sb); for (j = 0; j < i; j++) - brelse (sb->u.ext2_sb.s_group_desc[i]); + brelse (sb->u.ext2_sb.s_group_desc[j]); brelse (bh); printk ("EXT2-fs: group descriptors corrupted !\n"); return NULL; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index aa366c7..cc28496 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -46,6 +46,8 @@ static int trunc_direct (struct inode * inode) int i, tmp; unsigned long * p; struct buffer_head * bh; + unsigned long block_to_free = 0; + unsigned long free_count = 0; int retry = 0; int blocks = inode->i_sb->s_blocksize / 512; #define DIRECT_BLOCK ((inode->i_size + inode->i_sb->s_blocksize - 1) / \ @@ -82,8 +84,20 @@ repeat: bh->b_dirt = 1; } brelse (bh); - ext2_free_blocks (inode->i_sb, tmp, 1); + if (free_count == 0) { + block_to_free = tmp; + free_count++; + } else if (free_count > 0 && block_to_free == tmp - free_count) + free_count++; + else { + ext2_free_blocks (inode->i_sb, block_to_free, free_count); + block_to_free = tmp; + free_count = 1; + } +/* ext2_free_blocks (inode->i_sb, tmp, 1); */ } + if (free_count > 0) + ext2_free_blocks (inode->i_sb, block_to_free, free_count); return retry; } @@ -93,6 +107,8 @@ static int trunc_indirect (struct inode * inode, int offset, unsigned long * p) struct buffer_head * bh; struct buffer_head * ind_bh; unsigned long * ind; + unsigned long block_to_free = 0; + unsigned long free_count = 0; int retry = 0; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int blocks = inode->i_sb->s_blocksize / 512; @@ -144,10 +160,22 @@ repeat: bh->b_dirt = 1; } brelse (bh); - ext2_free_blocks (inode->i_sb, tmp, 1); + if (free_count == 0) { + block_to_free = tmp; + free_count++; + } else if (free_count > 0 && block_to_free == tmp - free_count) + free_count++; + else { + ext2_free_blocks (inode->i_sb, block_to_free, free_count); + block_to_free = tmp; + free_count = 1; + } +/* ext2_free_blocks (inode->i_sb, tmp, 1); */ inode->i_blocks -= blocks; inode->i_dirt = 1; } + if (free_count > 0) + ext2_free_blocks (inode->i_sb, block_to_free, free_count); ind = (unsigned long *) ind_bh->b_data; for (i = 0; i < addr_per_block; i++) if (*(ind++)) @@ -486,6 +486,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, return retval; } } + page = 0; if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) { flags = new_flags & ~MS_MGC_MSK; retval = copy_mount_options(data, &page); diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 2888383..3c384b1 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -52,7 +52,7 @@ /* * The second extended file system version */ -#define EXT2FS_DATE "93/12/30" +#define EXT2FS_DATE "94/01/05" #define EXT2FS_VERSION "0.4b" /* @@ -289,7 +289,7 @@ struct ext2_super_block { unsigned long s_mtime; /* Mount time */ unsigned long s_wtime; /* Write time */ unsigned short s_mnt_count; /* Mount count */ - unsigned short s_max_mnt_count; /* Maximal mount count */ + short s_max_mnt_count; /* Maximal mount count */ unsigned short s_magic; /* Magic signature */ unsigned short s_state; /* File system state */ unsigned short s_errors; /* Behaviour when detecting errors */ diff --git a/include/linux/ip.h b/include/linux/ip.h index 26d4a59..887b2cb 100644 --- a/include/linux/ip.h +++ b/include/linux/ip.h @@ -59,6 +59,8 @@ struct options { unsigned short handling; unsigned short stream; unsigned tcc; + int option_length; + void *option_data; }; diff --git a/include/linux/kd.h b/include/linux/kd.h index 5db5a5f..d751504 100644 --- a/include/linux/kd.h +++ b/include/linux/kd.h @@ -184,4 +184,14 @@ struct kbsentry { #define KDGKBSENT 0x4B48 /* gets one function key string entry */ #define KDSKBSENT 0x4B49 /* sets one function key string entry */ +struct kbdiacr { + u_char diacr, base, result; +}; +struct kbdiacrs { + unsigned int kb_cnt; /* number of entries in following array */ + struct kbdiacr kbdiacr[256]; /* MAX_DIACR from keyboard.h */ +}; +#define KDGKBDIACR 0x4B4A /* read kernel accent table */ +#define KDSKBDIACR 0x4B4B /* write kernel accent table */ + #endif /* _LINUX_KD_H */ diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h index 8b0fcab..9b34bcd 100644 --- a/include/linux/keyboard.h +++ b/include/linux/keyboard.h @@ -5,19 +5,6 @@ #define set_leds() mark_bh(KEYBOARD_BH) /* - * "dead" keys - prefix key values that are valid only for the next - * character code (sticky shift, E0/E1 special scancodes, diacriticals) - */ -extern unsigned long kbd_dead_keys; -extern unsigned long kbd_prev_dead_keys; - -/* - * these are the hardcoded dead key flags - */ -#define KGD_E0 0 -#define KGD_E1 1 - -/* * kbd->xxx contains the VC-local things (flag settings etc..) * The low 3 local flags are hardcoded to be the led setting.. */ @@ -52,26 +39,6 @@ extern struct kbd_struct kbd_table[]; extern unsigned long kbd_init(unsigned long); -extern inline int kbd_dead(int flag) -{ - return kbd_prev_dead_keys & (1 << flag); -} - -extern inline void set_kbd_dead(int flag) -{ - kbd_dead_keys |= 1 << flag; -} - -extern inline void clr_kbd_dead(int flag) -{ - kbd_dead_keys &= ~(1 << flag); -} - -extern inline void chg_kbd_dead(int flag) -{ - kbd_dead_keys ^= 1 << flag; -} - extern inline int vc_kbd_flag(struct kbd_struct * kbd, int flag) { return ((kbd->flags >> flag) & 1); @@ -98,7 +65,7 @@ extern const int NR_TYPES; extern const int max_vals[]; extern unsigned short key_map[NR_KEYMAPS][NR_KEYS]; -#define NR_FUNC 32 +#define NR_FUNC 36 #define FUNC_BUFSIZE 512 extern char func_buf[FUNC_BUFSIZE]; extern char *func_table[NR_FUNC]; @@ -145,6 +112,10 @@ extern char *func_table[NR_FUNC]; #define K_SELECT K(KT_FN,23) #define K_PGUP K(KT_FN,24) #define K_PGDN K(KT_FN,25) +#define K_MACRO K(KT_FN,26) +#define K_HELP K(KT_FN,27) +#define K_DO K(KT_FN,28) +#define K_PAUSE K(KT_FN,29) #define K_HOLE K(KT_SPEC,0) #define K_ENTER K(KT_SPEC,1) @@ -160,6 +131,7 @@ extern char *func_table[NR_FUNC]; #define K_SCROLLBACK K(KT_SPEC,11) #define K_BOOT K(KT_SPEC,12) #define K_CAPSON K(KT_SPEC,13) +#define K_COMPOSE K(KT_SPEC,14) #define K_P0 K(KT_PAD,0) #define K_P1 K(KT_PAD,1) @@ -178,6 +150,7 @@ extern char *func_table[NR_FUNC]; #define K_PENTER K(KT_PAD,14) /* key-pad enter */ #define K_PCOMMA K(KT_PAD,15) /* key-pad comma: kludge... */ #define K_PDOT K(KT_PAD,16) /* key-pad dot (period): kludge... */ +#define K_PPLUSMINUS K(KT_PAD,17) /* key-pad plus/minus */ #define K_DGRAVE K(KT_DEAD,0) #define K_DACUTE K(KT_DEAD,1) @@ -220,4 +193,5 @@ extern char *func_table[NR_FUNC]; #define K_ALTLOCK K(KT_LOCK,3) #define K_ALTGRLOCK K(KT_LOCK,1) +#define MAX_DIACR 256 #endif diff --git a/include/linux/socket.h b/include/linux/socket.h index 4e5fdbd..e6a520b 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -30,10 +30,14 @@ struct linger { #define AF_UNSPEC 0 #define AF_UNIX 1 #define AF_INET 2 +#define AF_AX25 3 +#define AF_IPX 4 /* Protocol families, same as address families. */ #define PF_UNIX AF_UNIX #define PF_INET AF_INET +#define PF_AX25 AF_AX25 +#define PF_IPX AF_IPX /* Flags we can use with send/ and recv. */ #define MSG_OOB 1 @@ -41,6 +45,10 @@ struct linger { /* Setsockoptions(2) level. */ #define SOL_SOCKET 1 +#define SOL_IP 2 +#define SOL_IPX 3 +#define SOL_AX25 4 +#define SOL_TCP 5 /* For setsockoptions(2) */ #define SO_DEBUG 1 @@ -56,6 +64,19 @@ struct linger { #define SO_NO_CHECK 11 #define SO_PRIORITY 12 #define SO_LINGER 13 +/* IP options */ +#define IP_TOS 1 +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IP_TTL 2 +/* IPX options */ +#define IPX_TYPE 1 +/* AX.25 options */ +#define AX25_WINDOW 1 +/* TCP options */ +#define TCP_MSS 1 +#define TCP_NODELAY 2 /* The various priorities. */ #define SOPRI_INTERACTIVE 0 diff --git a/include/linux/sockios.h b/include/linux/sockios.h index 499b219..4484b14 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -24,11 +24,11 @@ #define IP_SET_DEV 0x2401 struct ip_config { - char name[MAX_IP_NAME]; - unsigned long paddr; - unsigned long router; - unsigned long net; - unsigned int up:1,destroy:1; + char name[MAX_IP_NAME]; + unsigned long paddr; + unsigned long router; + unsigned long net; + unsigned int up:1,destroy:1; }; #endif /* FIXME: */ diff --git a/kernel/module.c b/kernel/module.c index e949327..2e38de5 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -149,7 +149,9 @@ sys_get_kernel_syms(struct kernel_sym *table) if (table != NULL) { from = symbol_table; to = table; - verify_area(VERIFY_WRITE, to, symbol_table_size * sizeof *table); + i = verify_area(VERIFY_WRITE, to, symbol_table_size * sizeof *table); + if (i) + return i; for (i = symbol_table_size ; --i >= 0 ; ) { sym.value = from->addr; strncpy(sym.name, from->name, sizeof sym.name); diff --git a/net/Makefile b/net/Makefile index 6478d1e..8299a0e 100644 --- a/net/Makefile +++ b/net/Makefile @@ -7,10 +7,17 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -# only these two lines should need to be changed to remove inet sockets. -# (and the inet/tcpip.o in net.o) +SUBDIRS := unix socket -SUBDIRS := unix inet +ifdef CONFIG_INET +SUBDIRS := $(SUBDIRS) inet +endif +ifdef CONFIG_IPX +SUBDIRS := $(SUBDIRS) ipx +endif +ifdef CONFIG_AX25 +SUBDIRS := $(SUBDIRS) ax25 +endif SUBOBJS := $(foreach f,$(SUBDIRS),$f/$f.o) diff --git a/net/Space.c b/net/Space.c index b2cc011..5c90082 100644 --- a/net/Space.c +++ b/net/Space.c @@ -30,10 +30,10 @@ # include "inet/inet.h" #endif #ifdef CONFIG_IPX -#include "inet/ipxcall.h" +#include "ipx/ipxcall.h" #endif #ifdef CONFIG_AX25 -#include "inet/ax25call.h" +#include "ax25/ax25call.h" #endif struct ddi_proto protocols[] = { @@ -53,43 +53,3 @@ struct ddi_proto protocols[] = { }; -/* - * Section B: Device Driver Modules. - * This section defines which network device drivers - * get linked into the Linux kernel. It is currently - * only used by the INET protocol. Any takers for the - * other protocols like XNS or Novell? - * - * WARNING: THIS SECTION IS NOT YET USED BY THE DRIVERS !!!!! - */ -/*#include "drv/we8003/we8003.h" Western Digital WD-80[01]3 */ -/*#include "drv/dp8390/dp8390.h" Donald Becker's DP8390 kit */ -/*#inclde "drv/slip/slip.h" Laurence Culhane's SLIP kit */ - - -struct ddi_device devices[] = { -#if CONF_WE8003 - { "WD80x3[EBT]", - "", 0, 1, we8003_init, NULL, - 19, 0, DDI_FCHRDEV, - { 0x280, 0, 15, 0, 32768, 0xD0000 } }, -#endif -#if CONF_DP8390 - { "DP8390/WD80x3", - "", 0, 1, dpwd8003_init, NULL, - 20, 0, DDI_FCHRDEV, - { 0, 0, 0, 0, 0, 0, } }, - { "DP8390/NE-x000", - "", 0, 1, dpne2000_init, NULL, - 20, 8, DDI_FCHRDEV, - { 0, 0, 0, 0, 0, 0, } }, - { "DP8390/3C50x", - "", 0, 1, dpec503_init, NULL, - 20, 16, DDI_FCHRDEV, - { 0, 0, 0, 0, 0, 0, } }, -#endif - { NULL, - "", 0, 0, NULL, NULL, - 0, 0, 0, - { 0, 0, 0, 0, 0, 0 } } -}; @@ -4,9 +4,11 @@ * but it eventually might move to an upper directory of * the system. * - * Version: @(#)ddi.c 1.0.5 04/22/93 + * Version: @(#)ddi.c 1.28 27/12/93 * * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * + * Unused pieces nobbled. */ #include <asm/segment.h> #include <asm/system.h> @@ -18,6 +20,9 @@ #include <linux/mm.h> #include <linux/socket.h> #include <linux/ddi.h> +#include <linux/interrupt.h> + +#include "socket/dev.h" #undef DDI_DEBUG @@ -28,64 +33,32 @@ #endif -extern struct ddi_device devices[]; /* device driver map */ extern struct ddi_proto protocols[]; /* network protocols */ /* - * This function gets called with an ASCII string representing the - * ID of some DDI driver. We loop through the DDI Devices table - * and return the address of the control block that has a matching - * "name" field. It is used by upper-level layers that want to - * dynamically bind some UNIX-domain "/dev/XXXX" file name to a - * DDI device driver. The "iflink(8)" program is an example of - * this behaviour. - */ -struct ddi_device * -ddi_map(const char *id) -{ - register struct ddi_device *dev; - - PRINTK (("DDI: MAP: looking for \"%s\": ", id)); - dev = devices; - while (dev->title != NULL) { - if (strncmp(dev->name, id, DDI_MAXNAME) == 0) { - PRINTK (("OK at 0x%X\n", dev)); - return(dev); - } - dev++; - } - PRINTK (("NOT FOUND\n")); - return(NULL); -} - - -/* * This is the function that is called by a kernel routine during * system startup. Its purpose is to walk trough the "devices" * table (defined above), and to call all moduled defined in it. */ -void -ddi_init(void) + +void ddi_init(void) { - struct ddi_proto *pro; - struct ddi_device *dev; + struct ddi_proto *pro; - PRINTK (("DDI: Starting up!\n")); + PRINTK (("DDI: Starting up!\n")); - /* First off, kick all configured protocols. */ - pro = protocols; - while (pro->name != NULL) { - (*pro->init)(pro); - pro++; - } + /* First off, kick all configured protocols. */ + pro = protocols; + while (pro->name != NULL) + { + (*pro->init)(pro); + pro++; + } - /* Done. Now kick all configured device drivers. */ - dev = devices; - while (dev->title != NULL) { - (*dev->init)(dev); - dev++; - } - - /* We're all done... */ -} + dev_init(); + /* Initialize the "Buffer Head" pointers. */ + bh_base[INET_BH].routine = inet_bh; + + /* We're all done... */ +} diff --git a/net/inet/Makefile b/net/inet/Makefile index a10caa3..4c1aa76 100644 --- a/net/inet/Makefile +++ b/net/inet/Makefile @@ -7,6 +7,9 @@ # # Note 2! The CFLAGS definition is now in the main makefile... +CFLAGS := $(CFLAGS) -I../socket -I.. +CPP := $(CPP) -I../socket -I.. + .c.o: $(CC) $(CFLAGS) -c -o $*.o $< .s.o: @@ -15,10 +18,8 @@ $(CC) $(CFLAGS) -S -o $*.s $< -OBJS = sock.o utils.o route.o proc.o timer.o protocol.o loopback.o \ - eth.o packet.o arp.o dev.o ip.o raw.o icmp.o tcp.o udp.o \ - datagram.o skbuff.o -# ipx.o ax25.o ax25_in.o ax25_out.o ax25_subr.o ax25_timer.o +OBJS = sockinet.o utils.o route.o proc.o timer.o protocol.o loopback.o \ + eth.o packet.o arp.o devinet.o ip.o raw.o icmp.o tcp.o udp.o ifdef CONFIG_INET diff --git a/net/inet/README b/net/inet/README index 79f957f..f216f16 100644 --- a/net/inet/README +++ b/net/inet/README @@ -1,27 +1,8 @@ -NET2Debugged 1.24 README +NET2Debugged 1.28 README ------------------------ -Major Changes - -o PLIP driver sort of works -o UDP and RAW have been partially rewritten for speed -o Internals heavily cleaned up, and memory monitoring of network - memory is now done. (On shift-scroll-lock) -o ARP should now not generate garbage -o Using MSG_PEEK can't cause race conditions and crashes -o Support for bootp clients. -o Supports RFC931 TAP authd -o NFS problems with certain types of network configuration are - fixed. -o Doesn't forward packets for other subnet (can cause packet storms) -o TCP won't ack rst frames causing packet storms (especially with - Lan workplace for DOS). -o Numerous fixes for solidity -o Verify_area used properly. -o MSG_PEEK is faster again -o Minor TCP fixes. Hopefully no more TCP lockups (ha!) -o Donald's promiscuous mode. Go forth and write protocol analysers... + ------------------------------------------------------------------------- NOTE: Drivers for this stack set must be using alloc_skb() not just diff --git a/net/inet/arp.c b/net/inet/arp.c index 0b69e1f..c272817 100644 --- a/net/inet/arp.c +++ b/net/inet/arp.c @@ -13,7 +13,7 @@ * resolver, like it should be. It will be put in a separate * directory under 'net', being a protocol of its own. -FvK * - * Version: @(#)arp.c 1.0.15 05/25/93 + * Version: @(#)arp.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -36,6 +36,11 @@ * Alan Cox : skb->link3 maintained by letting the other xmit queue kill the packet. * Alan Cox : Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet * one. + * Dominik Kubla : Better checking + * Tegge : Assorted corrections on cross port stuff + * Alan Cox : Heavily reformatted & recommented ready for the big day + * Alan Cox : ATF_PERM was backwards! - might be useful now (sigh) + * * * To Fix: * : arp response allocates an skbuff to send. However there is a perfectly @@ -64,52 +69,27 @@ #include <asm/segment.h> #include <stdarg.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "eth.h" #include "ip.h" #include "route.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "arp.h" +/* + * We will try an ARP the recommended three times before we abandon it. If we + * do abandon it all is not lost as the next frame will also try. + */ #define ARP_MAX_TRIES 3 - -static char *unk_print(unsigned char *, int); -static char *eth_aprint(unsigned char *, int); - - -static char *arp_cmds[] = { - "0x%04X", - "REQUEST", - "REPLY", - "REVERSE REQUEST", - "REVERSE REPLY", - NULL -}; -#define ARP_MAX_CMDS (sizeof(arp_cmds) / sizeof(arp_cmds[0])) - -static struct { - char *name; - char *(*print)(unsigned char *ptr, int len); -} arp_types[] = { - { "0x%04X", unk_print }, - { "10 Mbps Ethernet", eth_aprint }, - { "3 Mbps Ethernet", eth_aprint }, - { "AX.25", unk_print }, - { "Pronet", unk_print }, - { "Chaos", unk_print }, - { "IEEE 802.2 Ethernet (?)", eth_aprint }, - { "Arcnet", unk_print }, - { "AppleTalk", unk_print }, - { NULL, NULL } -}; -#define ARP_MAX_TYPE (sizeof(arp_types) / sizeof(arp_types[0])) - - +/* + * The ARP table itself + */ + struct arp_table *arp_tables[ARP_TABLE_SIZE] = { NULL, }; @@ -118,103 +98,43 @@ static int arp_proxies=0; /* So we can avoid the proxy arp overhead with the usual case of no proxy arps */ +/* + * Every packet awaiting an ARP resolution is stuffed on this + * queue until resolved or deleted. Note items in this queue + * may be on other (tcp retransmit) queues and we must not + * be the one to delete them. + */ + struct sk_buff * volatile arp_q = NULL; static struct arp_table *arp_lookup(unsigned long addr); static struct arp_table *arp_lookup_proxy(unsigned long addr); -/* Dump the ADDRESS bytes of an unknown hardware type. */ -static char * -unk_print(unsigned char *ptr, int len) -{ - static char buff[32]; - char *bufp = buff; - int i; - - for (i = 0; i < len; i++) - bufp += sprintf(bufp, "%02X ", (*ptr++ & 0377)); - return(buff); -} - - -/* Dump the ADDRESS bytes of an Ethernet hardware type. */ -static char * -eth_aprint(unsigned char *ptr, int len) -{ - if (len != ETH_ALEN) return(""); - return(eth_print(ptr)); -} - - -/* Dump an ARP packet. Not complete yet for non-Ethernet packets. */ -static void -arp_print(struct arphdr *arp) -{ - int len, idx; - unsigned char *ptr; - - if (inet_debug != DBG_ARP) return; - - printk("ARP: "); - if (arp == NULL) { - printk("(null)\n"); - return; - } - - /* Print the opcode name. */ - len = htons(arp->ar_op); - if (len < ARP_MAX_CMDS) idx = len; - else idx = 0; - printk("op "); - printk(arp_cmds[idx], len); - - /* Print the ARP header. */ - len = htons(arp->ar_hrd); - if (len < ARP_MAX_TYPE) idx = len; - else idx = 0; - printk(" hrd = "); printk(arp_types[idx].name, len); - printk(" pro = 0x%04X\n", htons(arp->ar_pro)); - printk(" hlen = %d plen = %d\n", arp->ar_hln, arp->ar_pln); - - /* - * Print the variable data. - * When ARP gets redone (after the formal introduction of NET-2), - * this part will be redone. ARP will then be a multi-family address - * resolver, and the code below will be made more general. -FvK - */ - ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); - printk(" sender HA = %s ", arp_types[idx].print(ptr, arp->ar_hln)); - ptr += arp->ar_hln; - printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr)); - ptr += arp->ar_pln; - printk(" target HA = %s ", arp_types[idx].print(ptr, arp->ar_hln)); - ptr += arp->ar_hln; - printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr)); -} - +/* + * We grab the arp queue, empty it and walk down it adding anything we can't + * resolve back onto the queue. We MUST do things this way as other entries + * may (will) get added as we walk the old list. + */ -/* This will try to retransmit everything on the queue. */ -static void -arp_send_q(void) +static void arp_send_q(void) { - struct sk_buff *skb; - struct sk_buff *volatile work_q; - cli(); - work_q = arp_q; - skb_new_list_head(&work_q); - arp_q = NULL; - sti(); - while((skb=skb_dequeue(&work_q))!=NULL) - { - IS_SKB(skb); - skb->magic = 0; - skb->next = NULL; - skb->prev = NULL; - - /* Decrement the 'tries' counter. */ + struct sk_buff *skb; + struct sk_buff *volatile work_q; cli(); - skb->tries--; - if (skb->tries == 0) { + work_q = arp_q; + skb_new_list_head(&work_q); + arp_q = NULL; + sti(); + while((skb=skb_dequeue(&work_q))!=NULL) + { + IS_SKB(skb); + skb->magic = 0; /* So everyone knows this is _NOT_ on the arp queue */ + + /* Decrement the 'tries' counter. */ + cli(); + skb->tries--; + if (skb->tries == 0) + { /* * Grmpf. * We have tried ARP_MAX_TRIES to resolve the IP address @@ -224,231 +144,270 @@ arp_send_q(void) * In any case, trying further is useless. So, we kill * this packet from the queue. (grinnik) -FvK */ - skb->sk = NULL; - if(skb->free) - kfree_skb(skb, FREE_WRITE); + skb->sk = NULL; + if(skb->free) + kfree_skb(skb, FREE_WRITE); /* If free was 0, magic is now 0, next is 0 and the write queue will notice and kill */ - sti(); - continue; - } + sti(); + continue; + } - /* Can we now complete this packet? */ - sti(); - if (skb->arp || !skb->dev->rebuild_header(skb+1, skb->dev)) { - skb->arp = 1; - skb->dev->queue_xmit(skb, skb->dev, 0); - } else { - /* Alas. Re-queue it... */ - skb->magic = ARP_QUEUE_MAGIC; - skb_queue_head(&arp_q,skb); - } - } + /* Can we now complete this packet? */ + sti(); + if (skb->arp || !skb->dev->rebuild_header(skb+1, skb->dev)) + { + skb->arp = 1; + skb->dev->queue_xmit(skb, skb->dev, 0); + } + else + { + /* Alas. Re-queue it... */ + skb->magic = ARP_QUEUE_MAGIC; + skb_queue_head(&arp_q,skb); + } + } } -/* Create and send our response to an ARP request. */ -static int -arp_response(struct arphdr *arp1, struct device *dev, int addrtype) +/* + * Create and send our response to an ARP request. + */ + +static int arp_response(struct arphdr *arp1, struct device *dev, int addrtype) { - struct arphdr *arp2; - struct sk_buff *skb; - unsigned long src, dst; - unsigned char *ptr1, *ptr2; - int hlen; - struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */ - - /* Decode the source (REQUEST) message. */ - ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short); - src = *((unsigned long *) (ptr1 + arp1->ar_hln)); - dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln)); + struct arphdr *arp2; + struct sk_buff *skb; + unsigned long src, dst; + unsigned char *ptr1, *ptr2; + int hlen; + struct arp_table *apt = NULL;/* =NULL otherwise the compiler gives warnings */ + + /* Decode the source (REQUEST) message. */ + ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short); + src = *((unsigned long *) (ptr1 + arp1->ar_hln)); + dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln)); - if(addrtype!=IS_MYADDR) - { - apt=arp_lookup_proxy(dst); - if(apt==NULL) - return(1); - } - - /* Get some mem and initialize it for the return trip. */ - skb = alloc_skb(sizeof(struct sk_buff) + + if(addrtype!=IS_MYADDR) + { + apt=arp_lookup_proxy(dst); + if(apt==NULL) + return(1); + } + + /* Get some mem and initialize it for the return trip. */ + skb = alloc_skb(sizeof(struct sk_buff) + sizeof(struct arphdr) + (2 * arp1->ar_hln) + (2 * arp1->ar_pln) + dev->hard_header_len, GFP_ATOMIC); - if (skb == NULL) { - printk("ARP: no memory available for ARP REPLY!\n"); - return(1); - } - - skb->mem_addr = skb; - skb->len = sizeof(struct arphdr) + (2 * arp1->ar_hln) + - (2 * arp1->ar_pln) + dev->hard_header_len; - skb->mem_len = sizeof(struct sk_buff) + skb->len; - hlen = dev->hard_header((unsigned char *)(skb+1), dev, + + if (skb == NULL) + { + printk("ARP: no memory available for ARP REPLY!\n"); + return(1); + } + + skb->len = sizeof(struct arphdr) + (2 * arp1->ar_hln) + + (2 * arp1->ar_pln) + dev->hard_header_len; + hlen = dev->hard_header((unsigned char *)(skb+1), dev, ETH_P_ARP, src, dst, skb->len); - if (hlen < 0) { - printk("ARP: cannot create HW frame header for REPLY !\n"); - kfree_skb(skb, FREE_WRITE); - return(1); - } + if (hlen < 0) + { + printk("ARP: cannot create HW frame header for REPLY !\n"); + kfree_skb(skb, FREE_WRITE); + return(1); + } /* * Fill in the ARP REPLY packet. * This looks ugly, but we have to deal with the variable-length * ARP packets and such. It is not as bad as it looks- FvK */ - arp2 = (struct arphdr *) ((unsigned char *) (skb+1) + hlen); - ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short); - arp2->ar_hrd = arp1->ar_hrd; - arp2->ar_pro = arp1->ar_pro; - arp2->ar_hln = arp1->ar_hln; - arp2->ar_pln = arp1->ar_pln; - arp2->ar_op = htons(ARPOP_REPLY); - if(addrtype==IS_MYADDR) - memcpy(ptr2, dev->dev_addr, arp2->ar_hln); - else /* Proxy arp, so pull from the table */ - memcpy(ptr2, apt->ha, arp2->ar_hln); - ptr2 += arp2->ar_hln; - memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln); - ptr2 += arp2->ar_pln; - memcpy(ptr2, ptr1, arp2->ar_hln); - ptr2 += arp2->ar_hln; - memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln); - - skb->free = 1; - skb->arp = 1; - skb->sk = NULL; - skb->next = NULL; - - DPRINTF((DBG_ARP, ">>")); - arp_print(arp2); - - /* Queue the packet for transmission. */ - dev->queue_xmit(skb, dev, 0); - return(0); + arp2 = (struct arphdr *) ((unsigned char *) (skb+1) + hlen); + ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short); + arp2->ar_hrd = arp1->ar_hrd; + arp2->ar_pro = arp1->ar_pro; + arp2->ar_hln = arp1->ar_hln; + arp2->ar_pln = arp1->ar_pln; + arp2->ar_op = htons(ARPOP_REPLY); + + if(addrtype==IS_MYADDR) + memcpy(ptr2, dev->dev_addr, arp2->ar_hln); + else /* Proxy arp, so pull from the table */ + memcpy(ptr2, apt->ha, arp2->ar_hln); + + ptr2 += arp2->ar_hln; + memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln); + ptr2 += arp2->ar_pln; + memcpy(ptr2, ptr1, arp2->ar_hln); + ptr2 += arp2->ar_hln; + memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln); + + skb->free = 1; + skb->arp = 1; + skb->sk = NULL; + skb->next = NULL; + + /* Queue the packet for transmission. */ + dev->queue_xmit(skb, dev, 0); + return(0); } -/* This will find an entry in the ARP table by looking at the IP address. */ -static struct arp_table * -arp_lookup(unsigned long paddr) +/* + * This will find an entry in the ARP table by looking at the IP address. + */ + +static struct arp_table *arp_lookup(unsigned long paddr) { - struct arp_table *apt; - unsigned long hash; - - DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr))); - - /* We don't want to ARP ourselves. */ - if (chk_addr(paddr) == IS_MYADDR) { - printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr)); - return(NULL); - } - - /* Loop through the table for the desired address. */ - hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); - cli(); - apt = arp_tables[hash]; - while(apt != NULL) { - if (apt->ip == paddr) { - sti(); - return(apt); - } - apt = apt->next; - } - sti(); - return(NULL); + struct arp_table *apt; + unsigned long hash; + + DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr))); + + /* We don't want to ARP ourselves. */ + if (chk_addr(paddr) == IS_MYADDR) + { + printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr)); + return(NULL); + } + + /* Loop through the table for the desired address. */ + hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); + cli(); + apt = arp_tables[hash]; + while(apt != NULL) + { + if (apt->ip == paddr) + { + sti(); + return(apt); + } + apt = apt->next; + } + sti(); + return(NULL); } -/* This will find a proxy in the ARP table by looking at the IP address. */ +/* + * This will find a proxy in the ARP table by looking at the IP address. + */ + static struct arp_table *arp_lookup_proxy(unsigned long paddr) { - struct arp_table *apt; - unsigned long hash; + struct arp_table *apt; + unsigned long hash; - DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr))); + DPRINTF((DBG_ARP, "ARP: lookup proxy(%s)\n", in_ntoa(paddr))); - /* Loop through the table for the desired address. */ - hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); - cli(); - apt = arp_tables[hash]; - while(apt != NULL) { - if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) { - sti(); - return(apt); - } - apt = apt->next; - } - sti(); - return(NULL); + /* Loop through the table for the desired address. */ + hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); + cli(); + apt = arp_tables[hash]; + while(apt != NULL) + { + if (apt->ip == paddr && (apt->flags & ATF_PUBL) ) + { + sti(); + return(apt); + } + apt = apt->next; + } + sti(); + return(NULL); } -/* Delete an ARP mapping entry in the cache. */ -void -arp_destroy(unsigned long paddr) +/* + * Delete an ARP mapping entry in the cache. + */ + +static void arp_destructor(unsigned long paddr, int force) { - struct arp_table *apt; - struct arp_table **lapt; - unsigned long hash; + struct arp_table *apt; + struct arp_table **lapt; + unsigned long hash; - DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr))); + DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr))); - /* We cannot destroy our own ARP entry. */ - if (chk_addr(paddr) == IS_MYADDR) { - DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n", + /* We cannot destroy our own ARP entry. */ + if (chk_addr(paddr) == IS_MYADDR) + { + DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n", in_ntoa(paddr))); - return; - } - hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); - - cli(); - lapt = &arp_tables[hash]; - while ((apt = *lapt) != NULL) { - if (apt->ip == paddr) { - *lapt = apt->next; - if(apt->flags&ATF_PUBL) - arp_proxies--; - kfree_s(apt, sizeof(struct arp_table)); - sti(); return; } - lapt = &apt->next; - } - sti(); + hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); + + cli(); + lapt = &arp_tables[hash]; + while ((apt = *lapt) != NULL) + { + if (apt->ip == paddr) + { + if((apt->flags&ATF_PERM) && !force) + return; + *lapt = apt->next; + if(apt->flags&ATF_PUBL) + arp_proxies--; + kfree_s(apt, sizeof(struct arp_table)); + sti(); + return; + } + lapt = &apt->next; + } + sti(); } +/* + * Kill an entry - eg for ioctl() + */ -/* Create an ARP entry. The caller should check for duplicates! */ -static struct arp_table * -arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype) +void arp_destroy(unsigned long paddr) +{ + arp_destructor(paddr,1); +} + +/* + * Delete a possibly invalid entry (see timer.c) + */ + +void arp_destroy_maybe(unsigned long paddr) { - struct arp_table *apt; - unsigned long hash; - - DPRINTF((DBG_ARP, "ARP: create(%s, ", in_ntoa(paddr))); - DPRINTF((DBG_ARP, "%s, ", eth_print(addr))); - DPRINTF((DBG_ARP, "%d, %d)\n", hlen, htype)); - - apt = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC); - if (apt == NULL) { - printk("ARP: no memory available for new ARP entry!\n"); - return(NULL); - } - - /* Fill in the allocated ARP cache entry. */ - hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); - apt->ip = paddr; - apt->hlen = hlen; - apt->htype = htype; - apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */ - memcpy(apt->ha, addr, hlen); - apt->last_used = jiffies; - cli(); - apt->next = arp_tables[hash]; - arp_tables[hash] = apt; - sti(); - return(apt); + arp_destructor(paddr,0); +} + +/* + * Create an ARP entry. The caller should check for duplicates! + */ + +static struct arp_table *arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype) +{ + struct arp_table *apt; + unsigned long hash; + + + apt = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC); + if (apt == NULL) + { + printk("ARP: no memory available for new ARP entry!\n"); + return(NULL); + } + + /* Fill in the allocated ARP cache entry. */ + hash = htonl(paddr) & (ARP_TABLE_SIZE - 1); + apt->ip = paddr; + apt->hlen = hlen; + apt->htype = htype; + apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */ + memcpy(apt->ha, addr, hlen); + apt->last_used = jiffies; + cli(); + apt->next = arp_tables[hash]; + arp_tables[hash] = apt; + sti(); + return(apt); } @@ -461,281 +420,295 @@ arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype) * one of our own IP addresses), we set up and send out an ARP REPLY * packet to the sender. */ -int -arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { - struct arphdr *arp; - struct arp_table *tbl; - unsigned long src, dst; - unsigned char *ptr; - int ret; - int addr_hint; - - DPRINTF((DBG_ARP, "<<\n")); - arp = skb->h.arp; - arp_print(arp); - - /* If this test doesn't pass, its not IP. Might be DECNET or friends */ - if (arp->ar_hln != dev->addr_len || dev->type != NET16(arp->ar_hrd)) - { - DPRINTF((DBG_ARP,"ARP: Bad packet received on device \"%s\" !\n", dev->name)); - kfree_skb(skb, FREE_READ); - return(0); - } - - /* For now we will only deal with IP addresses. */ - if (((arp->ar_pro != NET16(0x00CC) && dev->type==3) || (arp->ar_pro != NET16(ETH_P_IP) && dev->type!=3) ) || arp->ar_pln != 4) - { - if (arp->ar_op != NET16(ARPOP_REQUEST)) - DPRINTF((DBG_ARP,"ARP: Non-IP request on device \"%s\" !\n", dev->name)); - kfree_skb(skb, FREE_READ); - return(0); - } + struct arphdr *arp; + struct arp_table *tbl; + unsigned long src, dst; + unsigned char *ptr; + int ret; + int addr_hint; + + arp = skb->h.arp; + + /* If this test doesn't pass, its not IP. Might be DECNET or friends */ + if (arp->ar_hln != dev->addr_len || dev->type != NET16(arp->ar_hrd)) + { + DPRINTF((DBG_ARP,"ARP: Bad packet received on device \"%s\" !\n", dev->name)); + kfree_skb(skb, FREE_READ); + return(0); + } - /* - * As said before, we try to be smart by using the - * info already present in the packet: the sender's - * IP and hardware address. - */ - ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); - memcpy(&src, ptr + arp->ar_hln, arp->ar_pln); - tbl = arp_lookup(src); - if (tbl != NULL) { - DPRINTF((DBG_ARP, "ARP: udating entry for %s\n", in_ntoa(src))); - memcpy(tbl->ha, ptr, arp->ar_hln); - tbl->hlen = arp->ar_hln; - tbl->flags |= ATF_COM; - tbl->last_used = jiffies; - } else { - memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); - if (chk_addr(dst) != IS_MYADDR) { + /* For now we will only deal with IP addresses. */ + if (((arp->ar_pro != NET16(0x00CC) && dev->type==3) || (arp->ar_pro != NET16(ETH_P_IP) && dev->type!=3) ) || arp->ar_pln != 4) + { + if (arp->ar_op != NET16(ARPOP_REQUEST)) + DPRINTF((DBG_ARP,"ARP: Non-IP request on device \"%s\" !\n", dev->name)); kfree_skb(skb, FREE_READ); return(0); - } else { - tbl = arp_create(src, ptr, arp->ar_hln, arp->ar_hrd); - if (tbl == NULL) { + } + + /* + * As said before, we try to be smart by using the + * info already present in the packet: the sender's + * IP and hardware address. + */ + ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); + memcpy(&src, ptr + arp->ar_hln, arp->ar_pln); + tbl = arp_lookup(src); + if (tbl != NULL) + { + DPRINTF((DBG_ARP, "ARP: udating entry for %s\n", in_ntoa(src))); + memcpy(tbl->ha, ptr, arp->ar_hln); + tbl->hlen = arp->ar_hln; + tbl->flags |= ATF_COM; + tbl->last_used = jiffies; + } + else + { + memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); + if (chk_addr(dst) != IS_MYADDR) + { kfree_skb(skb, FREE_READ); return(0); + } + else + { + tbl = arp_create(src, ptr, arp->ar_hln, arp->ar_hrd); + if (tbl == NULL) + { + kfree_skb(skb, FREE_READ); + return(0); + } } - } - } + } /* * Since we updated the ARP cache, we might have enough * information to send out some previously queued IP * datagrams.... */ - arp_send_q(); + arp_send_q(); /* * OK, we used that part of the info. Now check if the * request was an ARP REQUEST for one of our own addresses.. */ - if (arp->ar_op != NET16(ARPOP_REQUEST)) { - kfree_skb(skb, FREE_READ); - return(0); - } + if (arp->ar_op != NET16(ARPOP_REQUEST)) + { + kfree_skb(skb, FREE_READ); + return(0); + } /* * A broadcast arp, ignore it */ - if((dst&0xFF)==0xFF) - { - kfree_skb(skb, FREE_READ); - return 0; - } - - memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); - if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) { - DPRINTF((DBG_ARP, "ARP: request was not for me!\n")); - kfree_skb(skb, FREE_READ); - return(0); - } + if(chk_addr(dst)==IS_BROADCAST) + { + kfree_skb(skb, FREE_READ); + return 0; + } - /* - * Yes, it is for us. - * Allocate, fill in and send an ARP REPLY packet. - */ - ret = arp_response(arp, dev, addr_hint); - kfree_skb(skb, FREE_READ); - return(ret); + memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); + if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) + { + DPRINTF((DBG_ARP, "ARP: request was not for me!\n")); + kfree_skb(skb, FREE_READ); + return(0); + } + + /* + * Yes, it is for us. + * Allocate, fill in and send an ARP REPLY packet. + */ + ret = arp_response(arp, dev, addr_hint); + kfree_skb(skb, FREE_READ); + return(ret); } -/* Create and send an ARP REQUEST packet. */ -void -arp_send(unsigned long paddr, struct device *dev, unsigned long saddr) +/* + * Create and send an ARP REQUEST packet. + */ + +void arp_send(unsigned long paddr, struct device *dev, unsigned long saddr) { - struct sk_buff *skb; - struct arphdr *arp; - unsigned char *ptr; - int tmp; + struct sk_buff *skb; + struct arphdr *arp; + unsigned char *ptr; + int tmp; - DPRINTF((DBG_ARP, "ARP: send(paddr=%s, ", in_ntoa(paddr))); - DPRINTF((DBG_ARP, "dev=%s, ", dev->name)); - DPRINTF((DBG_ARP, "saddr=%s)\n", in_ntoa(saddr))); + DPRINTF((DBG_ARP, "ARP: send(paddr=%s, ", in_ntoa(paddr))); + DPRINTF((DBG_ARP, "dev=%s, ", dev->name)); + DPRINTF((DBG_ARP, "saddr=%s)\n", in_ntoa(saddr))); - skb = alloc_skb(sizeof(struct sk_buff) + + skb = alloc_skb(sizeof(struct sk_buff) + sizeof(struct arphdr) + (2 * dev->addr_len) + dev->hard_header_len + (2 * 4 /* arp->plen */), GFP_ATOMIC); - if (skb == NULL) { - printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr)); - return; - } + if (skb == NULL) + { + printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr)); + return; + } - /* Fill in the request. */ - skb->sk = NULL; - skb->mem_addr = skb; - skb->len = sizeof(struct arphdr) + + /* Fill in the request. */ + skb->sk = NULL; + skb->len = sizeof(struct arphdr) + dev->hard_header_len + (2 * dev->addr_len) + 8; - skb->mem_len = sizeof(struct sk_buff) + skb->len; - skb->arp = 1; - skb->dev = dev; - skb->next = NULL; - skb->free = 1; - tmp = dev->hard_header((unsigned char *)(skb+1), dev, + skb->arp = 1; + skb->dev = dev; + skb->next = NULL; + skb->free = 1; + tmp = dev->hard_header((unsigned char *)(skb+1), dev, ETH_P_ARP, 0, saddr, skb->len); - if (tmp < 0) { - kfree_skb(skb,FREE_WRITE); - return; - } - arp = (struct arphdr *) ((unsigned char *) (skb+1) + tmp); - arp->ar_hrd = htons(dev->type); - if(dev->type!=3) /* AX.25 */ - arp->ar_pro = htons(ETH_P_IP); - else - arp->ar_pro = htons(0xCC); - arp->ar_hln = dev->addr_len; - arp->ar_pln = 4; - arp->ar_op = htons(ARPOP_REQUEST); - - ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); - memcpy(ptr, dev->dev_addr, arp->ar_hln); - ptr += arp->ar_hln; - memcpy(ptr, &saddr, arp->ar_pln); - ptr += arp->ar_pln; - /*memcpy(ptr, dev->broadcast, arp->ar_hln);*/ - memset(ptr,0,arp->ar_hln); - ptr += arp->ar_hln; - memcpy(ptr, &paddr, arp->ar_pln); - - DPRINTF((DBG_ARP, ">>\n")); - arp_print(arp); - dev->queue_xmit(skb, dev, 0); + if (tmp < 0) + { + kfree_skb(skb,FREE_WRITE); + return; + } + arp = (struct arphdr *) ((unsigned char *) (skb+1) + tmp); + arp->ar_hrd = htons(dev->type); + if(dev->type!=3) /* AX.25 */ + arp->ar_pro = htons(ETH_P_IP); + else + arp->ar_pro = htons(0xCC); + arp->ar_hln = dev->addr_len; + arp->ar_pln = 4; + arp->ar_op = htons(ARPOP_REQUEST); + + ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short); + memcpy(ptr, dev->dev_addr, arp->ar_hln); + ptr += arp->ar_hln; + memcpy(ptr, &saddr, arp->ar_pln); + ptr += arp->ar_pln; + /*memcpy(ptr, dev->broadcast, arp->ar_hln);*/ + memset(ptr,0,arp->ar_hln); + ptr += arp->ar_hln; + memcpy(ptr, &paddr, arp->ar_pln); + + dev->queue_xmit(skb, dev, 0); } -/* Find an ARP mapping in the cache. If not found, post a REQUEST. */ -int -arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, - unsigned long saddr) +/* + * Find an ARP mapping in the cache. If not found, post a REQUEST. + */ + +int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, + unsigned long saddr) { - struct arp_table *apt; + struct arp_table *apt; - DPRINTF((DBG_ARP, "ARP: find(haddr=%s, ", eth_print(haddr))); - DPRINTF((DBG_ARP, "paddr=%s, ", in_ntoa(paddr))); - DPRINTF((DBG_ARP, "dev=%s, saddr=%s)\n", dev->name, in_ntoa(saddr))); - - switch(chk_addr(paddr)) { - case IS_MYADDR: - memcpy(haddr, dev->dev_addr, dev->addr_len); - return(0); - case IS_BROADCAST: - memcpy(haddr, dev->broadcast, dev->addr_len); - return(0); - } + switch(chk_addr(paddr)) + { + case IS_MYADDR: + memcpy(haddr, dev->dev_addr, dev->addr_len); + return(0); + case IS_BROADCAST: + memcpy(haddr, dev->broadcast, dev->addr_len); + return(0); + } - apt = arp_lookup(paddr); - if (apt != NULL) { + apt = arp_lookup(paddr); + if (apt != NULL) + { /* * Make sure it's not too old. If it is too old, we will * just pretend we did not find it, and then arp_send will * verify the address for us. */ - if ((!(apt->flags & ATF_PERM)) || - (!before(apt->last_used, jiffies+ARP_TIMEOUT) && apt->hlen != 0)) { - apt->last_used = jiffies; - memcpy(haddr, apt->ha, dev->addr_len); - return(0); - } else { - DPRINTF((DBG_ARP, "ARP: find: found expired entry for %s\n", - in_ntoa(apt->ip))); - } - } + if ((apt->flags & ATF_PERM) || + (apt->last_used < jiffies+ARP_TIMEOUT && apt->hlen != 0)) + { + apt->last_used = jiffies; + memcpy(haddr, apt->ha, dev->addr_len); + return(0); + } + else + { + DPRINTF((DBG_ARP, "ARP: find: found expired entry for %s\n", + in_ntoa(apt->ip))); + } + } /* * This assume haddr are at least 4 bytes. * If this isn't true we can use a lookup table, one for every dev. - * NOTE: this bit of code still looks fishy to me- FvK */ - *(unsigned long *)haddr = paddr; + + *(unsigned long *)haddr = paddr; - /* If we didn't find an entry, we will try to send an ARP packet. */ - arp_send(paddr, dev, saddr); + /* If we didn't find an entry, we will try to send an ARP packet. */ + arp_send(paddr, dev, saddr); - return(1); + return(1); } -/* Add an entry to the ARP cache. Check for dupes! */ -void -arp_add(unsigned long addr, unsigned char *haddr, struct device *dev) +/* + * Add an entry to the ARP cache. Check for dupes! + */ + +void arp_add(unsigned long addr, unsigned char *haddr, struct device *dev) { - struct arp_table *apt; - - DPRINTF((DBG_ARP, "ARP: add(%s, ", in_ntoa(addr))); - DPRINTF((DBG_ARP, "%s, ", eth_print(haddr))); - DPRINTF((DBG_ARP, "%d, %d)\n", dev->hard_header_len, dev->type)); - - /* This is probably a good check... */ - if (addr == 0) { - printk("ARP: add: will not add entry for 0.0.0.0 !\n"); - return; - } - - /* First see if the address is already in the table. */ - apt = arp_lookup(addr); - if (apt != NULL) { - DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr))); - apt->last_used = jiffies; - memcpy(apt->ha, haddr , dev->addr_len); - return; - } - arp_create(addr, haddr, dev->addr_len, dev->type); + struct arp_table *apt; + + /* This is probably a good check... */ + if (addr == 0) + { + printk("ARP: add: will not add entry for 0.0.0.0 !\n"); + return; + } + + /* First see if the address is already in the table. */ + apt = arp_lookup(addr); + if (apt != NULL) + { + DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr))); + apt->last_used = jiffies; + memcpy(apt->ha, haddr , dev->addr_len); + return; + } + arp_create(addr, haddr, dev->addr_len, dev->type); } -/* Create an ARP entry for a device's broadcast address. */ -void -arp_add_broad(unsigned long addr, struct device *dev) -{ - struct arp_table *apt; +/* + * Create an ARP entry for a device's broadcast address. + */ - arp_add(addr, dev->broadcast, dev); - apt = arp_lookup(addr); - if (apt != NULL) { - apt->flags |= ATF_PERM; - } +void arp_add_broad(unsigned long addr, struct device *dev) +{ + struct arp_table *apt; + + arp_add(addr, dev->broadcast, dev); + apt = arp_lookup(addr); + if (apt != NULL) + { + apt->flags |= ATF_PERM; + } } /* Queue an IP packet, while waiting for the ARP reply packet. */ -void -arp_queue(struct sk_buff *skb) +void arp_queue(struct sk_buff *skb) { - cli(); - skb->tries = ARP_MAX_TRIES; + unsigned long flags; + save_flags(flags); + cli(); + skb->tries = ARP_MAX_TRIES; - if (skb->next != NULL) { - sti(); - printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic); - return; - } - skb_queue_tail(&arp_q,skb); - skb->magic = ARP_QUEUE_MAGIC; - sti(); + if (skb->next != NULL) + { + sti(); + printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic); + return; + } + skb_queue_tail(&arp_q,skb); + skb->magic = ARP_QUEUE_MAGIC; + restore_flags(flags); } @@ -754,172 +727,196 @@ arp_queue(struct sk_buff *skb) * * Perhaps we should redo PROCfs to handle larger buffers? Michael? */ -int -arp_get_info(char *buffer) + +int arp_get_info(char *buffer) { - struct arpreq *req; - struct arp_table *apt; - int i; - char *pos; - - /* Loop over the ARP table and copy structures to the buffer. */ - pos = buffer; - i = 0; - for (i = 0; i < ARP_TABLE_SIZE; i++) { - cli(); - apt = arp_tables[i]; - sti(); - while (apt != NULL) { - if (pos < (buffer + 4000)) { - req = (struct arpreq *) pos; - memset((char *) req, 0, sizeof(struct arpreq)); - req->arp_pa.sa_family = AF_INET; - memcpy((char *) req->arp_pa.sa_data, (char *) &apt->ip, 4); - req->arp_ha.sa_family = apt->htype; - memcpy((char *) req->arp_ha.sa_data, - (char *) &apt->ha, apt->hlen); - } - pos += sizeof(struct arpreq); + struct arpreq *req; + struct arp_table *apt; + int i; + char *pos; + + /* Loop over the ARP table and copy structures to the buffer. */ + pos = buffer; + i = 0; + for (i = 0; i < ARP_TABLE_SIZE; i++) + { cli(); - apt = apt->next; + apt = arp_tables[i]; sti(); - } - } - return(pos - buffer); + while (apt != NULL) + { + if (pos < (buffer + 4000)) + { + req = (struct arpreq *) pos; + memset((char *) req, 0, sizeof(struct arpreq)); + req->arp_pa.sa_family = AF_INET; + memcpy((char *) req->arp_pa.sa_data, (char *) &apt->ip, 4); + req->arp_ha.sa_family = apt->htype; + memcpy((char *) req->arp_ha.sa_data, + (char *) &apt->ha, apt->hlen); + } + pos += sizeof(struct arpreq); + cli(); + apt = apt->next; + sti(); + } + } + return(pos - buffer); } -/* Set (create) an ARP cache entry. */ -static int -arp_req_set(struct arpreq *req) +/* + * Set (create) an ARP cache entry. + */ + +static int arp_req_set(struct arpreq *req) { - struct arpreq r; - struct arp_table *apt; - struct sockaddr_in *si; - int htype, hlen; - - /* We only understand about IP addresses... */ - memcpy_fromfs(&r, req, sizeof(r)); - if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT); - - /* - * Find out about the hardware type. - * We have to be compatible with BSD UNIX, so we have to - * assume that a "not set" value (i.e. 0) means Ethernet. - */ - si = (struct sockaddr_in *) &r.arp_pa; - switch(r.arp_ha.sa_family) { - case 0: - case ARPHRD_ETHER: - htype = ARPHRD_ETHER; - hlen = ETH_ALEN; - break; - default: - return(-EPFNOSUPPORT); - } - - /* Is there an existing entry for this address? */ - if (si->sin_addr.s_addr == 0) { - printk("ARP: SETARP: requested PA is 0.0.0.0 !\n"); - return(-EINVAL); - } - apt = arp_lookup(si->sin_addr.s_addr); - if (apt == NULL) { - apt = arp_create(si->sin_addr.s_addr, - (unsigned char *) r.arp_ha.sa_data, hlen, htype); - if (apt == NULL) return(-ENOMEM); - } - - /* We now have a pointer to an ARP entry. Update it! */ - memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen); - apt->last_used = jiffies; - apt->flags = r.arp_flags; - if(apt->flags&ATF_PUBL) - arp_proxies++; /* Count proxy arps so we know if to use it */ - - return(0); + struct arpreq r; + struct arp_table *apt; + struct sockaddr_in *si; + int htype, hlen; + + /* We only understand about IP addresses... */ + memcpy_fromfs(&r, req, sizeof(r)); + if (r.arp_pa.sa_family != AF_INET) + return(-EPFNOSUPPORT); + + /* + * Find out about the hardware type. + * We have to be compatible with BSD UNIX, so we have to + * assume that a "not set" value (i.e. 0) means Ethernet. + */ + si = (struct sockaddr_in *) &r.arp_pa; + switch(r.arp_ha.sa_family) + { + case 0: + case ARPHRD_ETHER: + htype = ARPHRD_ETHER; + hlen = ETH_ALEN; + break; + case ARPHRD_AX25: + htype = ARPHRD_AX25; + hlen = 7; + break; + + default: + return(-EPFNOSUPPORT); + } + + /* Is there an existing entry for this address? */ + if (si->sin_addr.s_addr == 0) + { + printk("ARP: SETARP: requested PA is 0.0.0.0 !\n"); + return(-EINVAL); + } + apt = arp_lookup(si->sin_addr.s_addr); + if (apt == NULL) + { + apt = arp_create(si->sin_addr.s_addr, + (unsigned char *) r.arp_ha.sa_data, hlen, htype); + if (apt == NULL) + return(-ENOMEM); + } + + /* We now have a pointer to an ARP entry. Update it! */ + memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen); + apt->last_used = jiffies; + apt->flags = r.arp_flags; + if(apt->flags&ATF_PUBL) + arp_proxies++; /* Count proxy arps so we know if to use it */ + + return(0); } -/* Get an ARP cache entry. */ -static int -arp_req_get(struct arpreq *req) +/* + * Get an ARP cache entry. + */ + +static int arp_req_get(struct arpreq *req) { - struct arpreq r; - struct arp_table *apt; - struct sockaddr_in *si; - - /* We only understand about IP addresses... */ - memcpy_fromfs(&r, req, sizeof(r)); - if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT); - - /* Is there an existing entry for this address? */ - si = (struct sockaddr_in *) &r.arp_pa; - apt = arp_lookup(si->sin_addr.s_addr); - if (apt == NULL) return(-ENXIO); - - /* We found it; copy into structure. */ - memcpy((char *) r.arp_ha.sa_data, (char *) &apt->ha, apt->hlen); - r.arp_ha.sa_family = apt->htype; - - /* Copy the information back */ - memcpy_tofs(req, &r, sizeof(r)); - return(0); + struct arpreq r; + struct arp_table *apt; + struct sockaddr_in *si; + + /* We only understand about IP addresses... */ + memcpy_fromfs(&r, req, sizeof(r)); + if (r.arp_pa.sa_family != AF_INET) + return(-EPFNOSUPPORT); + + /* Is there an existing entry for this address? */ + si = (struct sockaddr_in *) &r.arp_pa; + apt = arp_lookup(si->sin_addr.s_addr); + if (apt == NULL) + return(-ENXIO); + + /* We found it; copy into structure. */ + memcpy((char *) r.arp_ha.sa_data, (char *) &apt->ha, apt->hlen); + r.arp_ha.sa_family = apt->htype; + + /* Copy the information back */ + memcpy_tofs(req, &r, sizeof(r)); + return(0); } -/* Delete an ARP cache entry. */ -static int -arp_req_del(struct arpreq *req) +/* + * Delete an ARP cache entry. + */ + +static int arp_req_del(struct arpreq *req) { - struct arpreq r; - struct sockaddr_in *si; - - /* We only understand about IP addresses... */ - memcpy_fromfs(&r, req, sizeof(r)); - if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT); - - si = (struct sockaddr_in *) &r.arp_pa; + struct arpreq r; + struct sockaddr_in *si; + + /* We only understand about IP addresses... */ + memcpy_fromfs(&r, req, sizeof(r)); + if (r.arp_pa.sa_family != AF_INET) + return(-EPFNOSUPPORT); + si = (struct sockaddr_in *) &r.arp_pa; - /* The system cope with this but splats up a nasty kernel message - We trap it beforehand and tell the user off */ - if(chk_addr(si->sin_addr.s_addr)==IS_MYADDR) - return -EINVAL; + /* The system cope with this but splats up a nasty kernel message + We trap it beforehand and tell the user off */ + if(chk_addr(si->sin_addr.s_addr)==IS_MYADDR) + return -EINVAL; - arp_destroy(si->sin_addr.s_addr); - - return(0); + arp_destroy(si->sin_addr.s_addr); + + return(0); } -/* Handle an ARP layer I/O control request. */ -int -arp_ioctl(unsigned int cmd, void *arg) +/* + * Handle an ARP layer I/O control request. + */ + +int arp_ioctl(unsigned int cmd, void *arg) { - int err; - switch(cmd) { - case DDIOCSDBG: - return(dbg_ioctl(arg, DBG_ARP)); - case SIOCDARP: - if (!suser()) return(-EPERM); - err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq)); - if(err) - return err; - return(arp_req_del((struct arpreq *)arg)); - case SIOCGARP: - err=verify_area(VERIFY_WRITE,arg,sizeof(struct arpreq)); - if(err) - return err; - return(arp_req_get((struct arpreq *)arg)); - case SIOCSARP: - if (!suser()) return(-EPERM); - err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq)); - if(err) - return err; - return(arp_req_set((struct arpreq *)arg)); - default: - return(-EINVAL); - } - /*NOTREACHED*/ - return(0); + int err; + switch(cmd) + { + case DDIOCSDBG: + return(dbg_ioctl(arg, DBG_ARP)); + case SIOCDARP: + if (!suser()) return(-EPERM); + err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq)); + if(err) + return err; + return(arp_req_del((struct arpreq *)arg)); + case SIOCGARP: + err=verify_area(VERIFY_WRITE,arg,sizeof(struct arpreq)); + if(err) + return err; + return(arp_req_get((struct arpreq *)arg)); + case SIOCSARP: + if (!suser()) return(-EPERM); + err=verify_area(VERIFY_READ,arg,sizeof(struct arpreq)); + if(err) + return err; + return(arp_req_set((struct arpreq *)arg)); + default: + return(-EINVAL); + } + /*NOTREACHED*/ + return(0); } diff --git a/net/inet/arp.h b/net/inet/arp.h index c75c6cf..b18e0ae 100644 --- a/net/inet/arp.h +++ b/net/inet/arp.h @@ -5,7 +5,7 @@ * * Definitions for the ARP protocol module. * - * Version: @(#)arp.h 1.0.6 05/21/93 + * Version: @(#)arp.h 1.28 24/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -26,29 +26,31 @@ #define ARP_QUEUE_MAGIC 0x0432447A /* magic # for queues */ -/* This structure defines the ARP mapping cache. */ -struct arp_table { - struct arp_table *next; - volatile unsigned long last_used; - unsigned int flags; -#if 1 - unsigned long ip; -#else - unsigned char pa[MAX_ADDR_LEN]; - unsigned char plen; - unsigned char ptype; -#endif - unsigned char ha[MAX_ADDR_LEN]; - unsigned char hlen; - unsigned char htype; +/* + * This structure defines the ARP mapping cache. + */ + +struct arp_table +{ + struct arp_table *next; + volatile unsigned long last_used; + unsigned int flags; + unsigned long ip; + unsigned char ha[MAX_ADDR_LEN]; + unsigned char hlen; + unsigned char htype; }; -/* This is also used in "sock.c" and "tcp.c" - YUCK! - FvK */ +/* + * This is also used in "sock.c" and "tcp.c" - YUCK! - FvK + */ + extern struct sk_buff *arp_q; extern void arp_destroy(unsigned long paddr); +extern void arp_destroy_maybe(unsigned long paddr); extern int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt); extern int arp_find(unsigned char *haddr, unsigned long paddr, diff --git a/net/inet/datagram.c b/net/inet/datagram.c index 931d9f3..e69de29 100644 --- a/net/inet/datagram.c +++ b/net/inet/datagram.c @@ -1,200 +0,0 @@ -/* - * SUCS NET2 Debugged. - * - * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top - * of these would make sense. Not tonight however 8-). - * This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly - * identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it. - * - * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code) - * - * Fixes: - * Alan Cox : NULL return from skb_peek_copy() understood - * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff. - * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but - * AX.25 now works right, and SPX is feasible. - */ - -#include <linux/config.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <asm/segment.h> -#include <asm/system.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include "inet.h" -#include "dev.h" -#include "ip.h" -#include "protocol.h" -#include "arp.h" -#include "route.h" -#include "tcp.h" -#include "udp.h" -#include "skbuff.h" -#include "sock.h" - - -/* - * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible - * races. This replaces identical code in packet,raw and udp, as well as the yet to - * be released IPX support. It also finally fixes the long standing peek and read - * race for datagram sockets. If you alter this routine remember it must be - * re-entrant. - */ - -struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) -{ - struct sk_buff *skb; - - /* Socket is inuse - so the timer doesn't attack it */ -restart: - sk->inuse = 1; - while(sk->rqueue == NULL) /* No data */ - { - /* If we are shutdown then no more data is going to appear. We are done */ - if (sk->shutdown & RCV_SHUTDOWN) - { - release_sock(sk); - *err=0; - return NULL; - } - - if(sk->err) - { - release_sock(sk); - *err=-sk->err; - sk->err=0; - return NULL; - } - - /* Sequenced packets can come disconnected. If so we report the problem */ - if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED) - { - release_sock(sk); - *err=-ENOTCONN; - return NULL; - } - - /* User doesn't want to wait */ - if (noblock) - { - release_sock(sk); - *err=-EAGAIN; - return NULL; - } - release_sock(sk); - - /* Interrupts off so that no packet arrives before we begin sleeping. - Otherwise we might miss our wake up */ - cli(); - if (sk->rqueue == NULL) - { - interruptible_sleep_on(sk->sleep); - /* Signals may need a restart of the syscall */ - if (current->signal & ~current->blocked) - { - sti(); - *err=-ERESTARTSYS; - return(NULL); - } - if(sk->err != 0) /* Error while waiting for packet - eg an icmp sent earlier by the - peer has finaly turned up now */ - { - *err = -sk->err; - sti(); - sk->err=0; - return NULL; - } - } - sk->inuse = 1; - sti(); - } - /* Again only user level code calls this function, so nothing interrupt level - will suddenely eat the rqueue */ - if (!(flags & MSG_PEEK)) - { - skb=skb_dequeue(&sk->rqueue); - if(skb!=NULL) - skb->users++; - else - goto restart; /* Avoid race if someone beats us to the data */ - } - else - { - cli(); - skb=skb_peek(&sk->rqueue); - if(skb!=NULL) - skb->users++; - sti(); - if(skb==NULL) /* shouldn't happen but .. */ - *err=-EAGAIN; - } - return skb; -} - -void skb_free_datagram(struct sk_buff *skb) -{ - unsigned long flags; - - save_flags(flags); - cli(); - skb->users--; - if(skb->users>0) - { - restore_flags(flags); - return; - } - /* See if it needs destroying */ - if(skb->list == NULL) /* Been dequeued by someone - ie its read */ - kfree_skb(skb,FREE_READ); - restore_flags(flags); -} - -void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size) -{ - /* We will know all about the fraglist options to allow >4K receives - but not this release */ - memcpy_tofs(to,skb->h.raw+offset,size); -} - -/* - * Datagram select: Again totally generic. Moved from udp.c - * Now does seqpacket. - */ - -int datagram_select(struct sock *sk, int sel_type, select_table *wait) -{ - select_wait(sk->sleep, wait); - switch(sel_type) - { - case SEL_IN: - if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE) - { - /* Connection closed: Wake up */ - return(1); - } - if (sk->rqueue != NULL || sk->err != 0) - { /* This appears to be consistent - with other stacks */ - return(1); - } - return(0); - - case SEL_OUT: - if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE) - { - return(1); - } - return(0); - - case SEL_EX: - if (sk->err) - return(1); /* Socket has gone into error state (eg icmp error) */ - return(0); - } - return(0); -} diff --git a/net/inet/dev.c b/net/inet/dev.c index e867ae3..05c2db4 100644 --- a/net/inet/dev.c +++ b/net/inet/dev.c @@ -790,7 +790,6 @@ static inline int bad_mask(unsigned long mask, unsigned long addr) return 0; } - /* Perform the SIOCxIFxxx calls. */ static int dev_ifsioc(void *arg, unsigned int getset) diff --git a/net/inet/devinet.c b/net/inet/devinet.c new file mode 100644 index 0000000..6b83134 --- /dev/null +++ b/net/inet/devinet.c @@ -0,0 +1,266 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Interface (streams) handling functions. + * + * Version: @(#)dev.c 1.28 20/12/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * + * Fixes: + * Alan Cox: check_addr returns a value for a wrong subnet + * ie not us but don't forward this! + * Alan Cox: block timer if the inet_bh handler is running + * Alan Cox: generic queue code added. A lot neater now + * C.E.Hawkins: SIOCGIFCONF only reports 'upped' interfaces + * C.E.Hawkins: IFF_PROMISC support + * Alan Cox: Supports Donald Beckers new hardware + * multicast layer, but not yet multicast lists. + * Alan Cox: ip_addr_match problems with class A/B nets. + * C.E.Hawkins IP 0.0.0.0 and also same net route fix. [FIXME: Ought to cause ICMP_REDIRECT] + * Alan Cox: Removed bogus subnet check now the subnet code + * a) actually works for all A/B nets + * b) doesn't forward off the same interface. + * Alan Cox: Multiple extra protocols + * Alan Cox: A Couple more escaped verify_area calls die + * Alan Cox: IP_SET_DEV is gone (forever) as per Fred's comment. + * Alan Cox: Grand tidy up ready for the big day. + * Alan Cox: Handles dev_open errors correctly. + * Alan Cox: IP and generic parts split + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/if_ether.h> +#include "inet.h" +#include "devinet.h" +#include "eth.h" +#include "ip.h" +#include "route.h" +#include "protocol.h" +#include "tcp.h" +#include "skbuff.h" + + +/* + * Determine a default network mask, based on the IP address. + */ + +unsigned long ip_get_mask(unsigned long addr) +{ + unsigned long dst; + + if (addr == 0L) + return(0L); /* special case */ + + dst = ntohl(addr); + if (IN_CLASSA(dst)) + return(htonl(IN_CLASSA_NET)); + if (IN_CLASSB(dst)) + return(htonl(IN_CLASSB_NET)); + if (IN_CLASSC(dst)) + return(htonl(IN_CLASSC_NET)); + + /* Something else, probably a subnet. */ + return(0); +} + +/* + * See if a pair of addresses match. + */ + +int ip_addr_match(unsigned long me, unsigned long him) +{ + int i; + unsigned long mask=0xFFFFFFFF; + DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me))); + DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him))); + + /* Fast path for 99.9% of cases */ + if (me == him) + return(1); + + for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8) + { + if ((me & 0xFF) != (him & 0xFF)) + { + /* + * The only way this could be a match is for + * the rest of addr1 to be 0 or 255. + */ + if (me != 0 && me != mask) + return(0); + return(1); + } + } + return(1); +} + + +/* + * Check the address for our address, broadcasts, etc. + * + * This routine is used a lot, and in many time critical + * places. It's already _TOO_ slow so be careful how you + * alter it. + */ + +int chk_addr(unsigned long addr) +{ + struct device *dev; + unsigned long dst; + + DPRINTF((DBG_DEV, "chk_addr(%s) --> ", in_ntoa(addr))); + dst = ntohl(addr); + + /* + * Accept both `all ones' and `all zeros' as BROADCAST. + * All 0's is the old BSD broadcast. + */ + + if (dst == INADDR_ANY || dst == INADDR_BROADCAST) + { + DPRINTF((DBG_DEV, "BROADCAST\n")); + return(IS_BROADCAST); + } + + /* Accept all of the `loopback' class A net. */ + if ((dst & IN_CLASSA_NET) == 0x7F000000L) + { + DPRINTF((DBG_DEV, "LOOPBACK\n")); + + /* + * We force `loopback' to be equal to MY_ADDR. + */ + return(IS_MYADDR); + /* return(IS_LOOPBACK); */ + } + + /* OK, now check the interface addresses. */ + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (!(dev->flags&IFF_UP)) + continue; + if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/) + return(IS_MYADDR); + /* Is it the exact IP address? */ + if (addr == dev->pa_addr) + { + DPRINTF((DBG_DEV, "MYADDR\n")); + return(IS_MYADDR); + } + + /* Nope. Check for a subnetwork broadcast. */ + if ((addr & dev->pa_mask) == (dev->pa_addr & dev->pa_mask)) + { + if ((addr & ~dev->pa_mask) == 0) + { + DPRINTF((DBG_DEV, "SUBBROADCAST-0\n")); + return(IS_BROADCAST); + } + if (((addr & ~dev->pa_mask) | dev->pa_mask) + == INADDR_BROADCAST) + { + DPRINTF((DBG_DEV, "SUBBROADCAST-1\n")); + return(IS_BROADCAST); + } + } + + /* Nope. Check for Network broadcast. */ + if(IN_CLASSA(dst)) + { + if( addr == (dev->pa_addr | 0xffffff00)) + { + DPRINTF((DBG_DEV, "CLASS A BROADCAST-1\n")); + return(IS_BROADCAST); + } + } + else if(IN_CLASSB(dst)) + { + if( addr == (dev->pa_addr | 0xffff0000)) + { + DPRINTF((DBG_DEV, "CLASS B BROADCAST-1\n")); + return(IS_BROADCAST); + } + } + else + { /* IN_CLASSC */ + if( addr == (dev->pa_addr | 0xff000000)) + { + DPRINTF((DBG_DEV, "CLASS C BROADCAST-1\n")); + return(IS_BROADCAST); + } + } + } + + DPRINTF((DBG_DEV, "NONE\n")); + + return(0); /* no match at all */ +} + + +/* + * Retrieve our own address. + * Because the loopback address (127.0.0.1) is already recognized + * automatically, we can use the loopback interface's address as + * our "primary" interface. This is the addressed used by IP et + * al when it doesn't know which address to use (i.e. it does not + * yet know from or to which interface to go...). + */ + +unsigned long my_addr(void) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (dev->flags & IFF_LOOPBACK) + return(dev->pa_addr); + } + return(0); +} + + + +/* + * Find an interface that can handle addresses for a certain address. + */ + +struct device *dev_check(unsigned long addr) +{ + struct device *dev; + + for (dev = dev_base; dev; dev = dev->next) + if ((dev->flags & IFF_UP) && (dev->flags & IFF_POINTOPOINT) && + (addr == dev->pa_dstaddr)) + return dev; + for (dev = dev_base; dev; dev = dev->next) + if ((dev->flags & IFF_UP) && !(dev->flags & IFF_POINTOPOINT) && + (dev->flags & IFF_LOOPBACK ? (addr == dev->pa_addr) : + (dev->pa_mask & addr) == (dev->pa_addr & dev->pa_mask))) + break; + /* no need to check broadcast addresses */ + return dev; +} + diff --git a/net/inet/devinet.h b/net/inet/devinet.h new file mode 100644 index 0000000..c9fc5cc --- /dev/null +++ b/net/inet/devinet.h @@ -0,0 +1,22 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the INET bits of the interfaces handler. + * + */ + +#ifndef _DEVINET_H +#define _DEVINET_H + +#ifndef _DEV_H +#include "dev.h" +#endif + +extern int ip_addr_match(unsigned long addr1, unsigned long addr2); +extern int chk_addr(unsigned long addr); +extern struct device *dev_check(unsigned long daddr); +extern unsigned long my_addr(void); + +#endif /* _DEVINET_H */ diff --git a/net/inet/eth.c b/net/inet/eth.c index 01e2f51..7bf2657 100644 --- a/net/inet/eth.c +++ b/net/inet/eth.c @@ -5,24 +5,27 @@ * * Ethernet-type device handling. * - * Version: @(#)eth.c 1.0.7 05/25/93 + * Version: @(#)eth.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Mark Evans, <evansmp@uhura.aston.ac.uk> * * Fixes: - * Mr Linux : Arp problems - * Alan Cox : Generic queue tidyup (very tiny here) - * Alan Cox : eth_header ntohs should be htons + * Mr Linux : Arp problems. + * Alan Cox : Generic queue tidyup (very tiny here). + * Alan Cox : eth_header ntohs should be htons. * Alan Cox : eth_rebuild_header missing an htons and * minor other things. + * Tegge : Arp bug fixes. + * Alan Cox : Tidy up ready for the big day. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + #include <asm/segment.h> #include <asm/system.h> #include <linux/types.h> @@ -33,18 +36,19 @@ #include <linux/socket.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "eth.h" #include "ip.h" #include "route.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include <linux/errno.h> #include "arp.h" +#ifdef ETH_DEBUG /* Display an Ethernet address in readable format. */ char *eth_print(unsigned char *ptr) { @@ -57,32 +61,16 @@ char *eth_print(unsigned char *ptr) ); return(buff); } +#endif -void eth_setup(char *str, int *ints) -{ - struct device *d = dev_base; - - if (!str || !*str) - return; - while (d) { - if (!strcmp(str,d->name)) { - if (ints[0] > 0) - d->irq=ints[1]; - if (ints[0] > 1) - d->base_addr=ints[2]; - if (ints[0] > 2) - d->mem_start=ints[3]; - if (ints[0] > 3) - d->mem_end=ints[4]; - break; - } - d=d->next; - } -} -/* Display the contents of the Ethernet MAC header. */ -void -eth_dump(struct ethhdr *eth) +#ifdef ETH_DEBUG + +/* + * Display the contents of the Ethernet MAC header. + */ + +void eth_dump(struct ethhdr *eth) { if (inet_debug != DBG_ETH) return; @@ -91,96 +79,128 @@ eth_dump(struct ethhdr *eth) printk("TYPE = %04X\n", ntohs(eth->h_proto)); } +#endif -/* Create the Ethernet MAC header. */ -int -eth_header(unsigned char *buff, struct device *dev, unsigned short type, + +/* + * Create the Ethernet MAC header. + * + * ARP might prevent this from working all in one go. See also + * the rebuild header function. + */ + +int eth_header(unsigned char *buff, struct device *dev, unsigned short type, unsigned long daddr, unsigned long saddr, unsigned len) { - struct ethhdr *eth; - - DPRINTF((DBG_DEV, "ETH: header(%s, ", in_ntoa(saddr))); - DPRINTF((DBG_DEV, "%s, 0x%X)\n", in_ntoa(daddr), type)); - - /* Fill in the basic Ethernet MAC header. */ - eth = (struct ethhdr *) buff; - eth->h_proto = htons(type); - - /* We don't ARP for the LOOPBACK device... */ - if (dev->flags & IFF_LOOPBACK) { - DPRINTF((DBG_DEV, "ETH: No header for loopback\n")); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - memset(eth->h_dest, 0, dev->addr_len); - return(dev->hard_header_len); - } - - /* Check if we can use the MAC BROADCAST address. */ - if (chk_addr(daddr) == IS_BROADCAST) { - DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n")); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - memcpy(eth->h_dest, dev->broadcast, dev->addr_len); - return(dev->hard_header_len); - } - cli(); - memcpy(eth->h_source, &saddr, 4); - /* No. Ask ARP to resolve the Ethernet address. */ - if (arp_find(eth->h_dest, daddr, dev, saddr)) - { - sti(); - if(type!=ETH_P_IP) - printk("Erk: protocol %X got into an arp request state!\n",type); - return(-dev->hard_header_len); - } - else - { - memcpy(eth->h_source,dev->dev_addr,dev->addr_len); /* This was missing causing chaos if the - header built correctly! */ - sti(); - return(dev->hard_header_len); - } + struct ethhdr *eth; + + DPRINTF((DBG_DEV, "ETH: header(%s, ", in_ntoa(saddr))); + DPRINTF((DBG_DEV, "%s, 0x%X)\n", in_ntoa(daddr), type)); + + /* Fill in the basic Ethernet MAC header. */ + eth = (struct ethhdr *) buff; + eth->h_proto = htons(type); + + /* We don't ARP for the LOOPBACK device... */ + if (dev->flags & IFF_LOOPBACK) + { + DPRINTF((DBG_DEV, "ETH: No header for loopback\n")); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + memset(eth->h_dest, 0, dev->addr_len); + return(dev->hard_header_len); + } + + /* Check if we can use the MAC BROADCAST address. */ + if (chk_addr(daddr) == IS_BROADCAST) + { + DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n")); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, dev->broadcast, dev->addr_len); + return(dev->hard_header_len); + } + /* + * We disable interrupts here to avoid a race if the ARP + * reply is too quick. + */ + cli(); + memcpy(eth->h_source, &saddr, 4); + /* No. Ask ARP to resolve the Ethernet address. */ + if (arp_find(eth->h_dest, daddr, dev, dev->pa_addr/* saddr */)) + { + sti(); + if(type!=ETH_P_IP) + printk("Erk: protocol %X got into an arp request state!\n",type); + return(-dev->hard_header_len); + } + else + { + memcpy(eth->h_source,dev->dev_addr,dev->addr_len); /* This was missing causing chaos if the + header built correctly! */ + sti(); + return(dev->hard_header_len); + } } -/* Rebuild the Ethernet MAC header. */ -int -eth_rebuild_header(void *buff, struct device *dev) +/* + * Rebuild the Ethernet MAC header. + * + * We've got a 'stuck' packet that failed to go out before. See if + * the arp is resolved and we can finally shift it. + */ + +int eth_rebuild_header(void *buff, struct device *dev) { - struct ethhdr *eth; - unsigned long src, dst; - - DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n")); - eth = (struct ethhdr *) buff; - src = *(unsigned long *) eth->h_source; - dst = *(unsigned long *) eth->h_dest; - DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src))); - DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst))); - if(eth->h_proto!=htons(ETH_P_ARP)) /* This ntohs kind of helps a bit! */ - if (arp_find(eth->h_dest, dst, dev, src)) return(1); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - return(0); + struct ethhdr *eth; + unsigned long src, dst; + + DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n")); + eth = (struct ethhdr *) buff; + src = *(unsigned long *) eth->h_source; + dst = *(unsigned long *) eth->h_dest; + DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src))); + DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst))); + if(eth->h_proto!=htons(ETH_P_ARP)) /* This ntohs kind of helps a bit! */ + if (arp_find(eth->h_dest, dst, dev, dev->pa_addr /* src */)) + /* Still not known */ + return(1); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + return(0); } -/* Add an ARP entry for a host on this interface. */ -void -eth_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev) +/* + * Add an ARP entry for a host on this interface. + */ + +void eth_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev) { - struct ethhdr *eth; + struct ethhdr *eth; - eth = (struct ethhdr *) (skb + 1); - arp_add(addr, eth->h_source, dev); + eth = (struct ethhdr *) (skb + 1); + arp_add(addr, eth->h_source, dev); } -/* Determine the packet's protocol ID. */ -unsigned short -eth_type_trans(struct sk_buff *skb, struct device *dev) +/* + * Determine the packet's protocol ID. + * + * Ethernet comes in two 'species' DIX (Digitial Intel Xerox) and IEE802.3 + * needless to say they are different. Fortunately there is a way of telling + * them apart. All 'normal' modern DIX service ID's are >1536. + * All IEE802.3 frames have a length at this position and that cannot be + * >=1536. Note IEE802.3 frames have a second 802.2 header normally. We don't + * deal with this bit in the current kernel, but a user using SOCK_PACKET + * for 802.3 frames can do so. + */ + +unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev) { - struct ethhdr *eth; + struct ethhdr *eth; - eth = (struct ethhdr *) (skb + 1); + eth = (struct ethhdr *) (skb + 1); - if(ntohs(eth->h_proto)<1536) - return(htons(ETH_P_802_3)); - return(eth->h_proto); + if(ntohs(eth->h_proto)<1536) + return(htons(ETH_P_802_3)); + return(eth->h_proto); } diff --git a/net/inet/icmp.c b/net/inet/icmp.c index 7b953d4..467a119 100644 --- a/net/inet/icmp.c +++ b/net/inet/icmp.c @@ -5,7 +5,7 @@ * * Internet Control Message Protocol (ICMP) * - * Version: @(#)icmp.c 1.0.11 06/02/93 + * Version: @(#)icmp.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -14,6 +14,7 @@ * Fixes: * Alan Cox : Generic queue usage. * Gerhard Koerting: ICMP addressing corrected + * Tegge : Subnet problems * * * This program is free software; you can redistribute it and/or @@ -21,6 +22,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + #include <linux/types.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -28,14 +30,14 @@ #include <linux/socket.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "route.h" #include "protocol.h" #include "icmp.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include <linux/errno.h> #include <linux/timer.h> #include <asm/system.h> @@ -63,379 +65,413 @@ struct icmp_err icmp_err_convert[] = { }; +#ifdef ICMP_DEBUG + /* Display the contents of an ICMP header. */ static void print_icmp(struct icmphdr *icmph) { - if (inet_debug != DBG_ICMP) return; + if (inet_debug != DBG_ICMP) + return; - printk("ICMP: type = %d, code = %d, checksum = %X\n", + printk("ICMP: type = %d, code = %d, checksum = %X\n", icmph->type, icmph->code, icmph->checksum); - printk(" gateway = %s\n", in_ntoa(icmph->un.gateway)); + printk(" gateway = %s\n", in_ntoa(icmph->un.gateway)); } +#endif + +/* + * Send an ICMP message. + * + * ICMP is the control message protocol for error reporting in IP. + * A good document to start with for this stuff is RFC 791. + */ -/* Send an ICMP message. */ -void -icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev) +void icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev) { - struct sk_buff *skb; - struct iphdr *iph; - int offset; - struct icmphdr *icmph; - int len; + struct sk_buff *skb; + struct iphdr *iph; + int offset; + struct icmphdr *icmph; + int len; - DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n", + DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n", skb_in, type, code, dev)); - /* Get some memory for the reply. */ - len = sizeof(struct sk_buff) + dev->hard_header_len + - sizeof(struct iphdr) + sizeof(struct icmphdr) + - sizeof(struct iphdr) + 8; /* amount of header to return */ + /* Get some memory for the reply. */ + len = sizeof(struct sk_buff) + dev->hard_header_len + + sizeof(struct iphdr) + sizeof(struct icmphdr) + + sizeof(struct iphdr) + 8; /* amount of header to return */ - skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC); - if (skb == NULL) - return; - - skb->sk = NULL; - skb->mem_addr = skb; - skb->mem_len = len; - len -= sizeof(struct sk_buff); - - /* Find the IP header. */ - iph = (struct iphdr *) (skb_in + 1); - iph = (struct iphdr *) ((unsigned char *) iph + dev->hard_header_len); - - /* Build Layer 2-3 headers for message back to source. */ - offset = ip_build_header(skb, dev->pa_addr, iph->saddr, - &dev, IPPROTO_ICMP, NULL, len); - if (offset < 0) { - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return; - } - - /* Re-adjust length according to actual IP header size. */ - skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8; - icmph = (struct icmphdr *) ((unsigned char *) (skb + 1) + offset); - icmph->type = type; - icmph->code = code; - icmph->checksum = 0; - icmph->un.gateway = 0; - memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8); - - icmph->checksum = ip_compute_csum((unsigned char *)icmph, - sizeof(struct icmphdr) + sizeof(struct iphdr) + 8); - - DPRINTF((DBG_ICMP, ">>\n")); - print_icmp(icmph); - - /* Send it and free it. */ - ip_queue_xmit(NULL, dev, skb, 1); + skb = (struct sk_buff *) alloc_skb(len, GFP_ATOMIC); + /* We just forget about failed ICMP messages. ICMP is unreliable anyway and + things will sort out in time */ + + if (skb == NULL) + return; + + skb->sk = NULL; + len -= sizeof(struct sk_buff); + + /* Find the IP header. */ + iph = (struct iphdr *) (skb_in + 1); + iph = (struct iphdr *) ((unsigned char *) iph + dev->hard_header_len); + + /* Build Layer 2-3 headers for message back to source. */ + offset = ip_build_header(skb, dev->pa_addr, iph->saddr, + &dev, IPPROTO_ICMP, NULL, len, 25, IPTOS_RELIABILITY); + if (offset < 0) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return; + } + + /* Re-adjust length according to actual IP header size. */ + skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8; + icmph = (struct icmphdr *) ((unsigned char *) (skb + 1) + offset); + icmph->type = type; + icmph->code = code; + icmph->checksum = 0; + icmph->un.gateway = 0; + memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8); + + icmph->checksum = ip_compute_csum((unsigned char *)icmph, + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8); + +#ifdef ICMP_DEBUG + DPRINTF((DBG_ICMP, ">>\n")); + print_icmp(icmph); +#endif + /* Send it and free it. */ + ip_queue_xmit(NULL, dev, skb, 1); } -/* Handle ICMP_UNREACH and ICMP_QUENCH. */ -static void -icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb) +/* + * Handle ICMP_UNREACH and ICMP_QUENCH. + */ + +static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb) { - struct inet_protocol *ipprot; - struct iphdr *iph; - unsigned char hash; - int err; - - err = (icmph->type << 8) | icmph->code; - iph = (struct iphdr *) (icmph + 1); - switch(icmph->code & 7) { - case ICMP_NET_UNREACH: - DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n", + struct inet_protocol *ipprot; + struct iphdr *iph; + unsigned char hash; + int err; + + err = (icmph->type << 8) | icmph->code; + iph = (struct iphdr *) (icmph + 1); + switch(icmph->code & 7) + { + case ICMP_NET_UNREACH: + DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n", in_ntoa(iph->daddr))); - break; - case ICMP_HOST_UNREACH: - DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n", - in_ntoa(iph->daddr))); - break; - case ICMP_PROT_UNREACH: - printk("ICMP: %s:%d: protocol unreachable.\n", - in_ntoa(iph->daddr), ntohs(iph->protocol)); - break; - case ICMP_PORT_UNREACH: - DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n", - in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */)); - break; - case ICMP_FRAG_NEEDED: - printk("ICMP: %s: fragmentation needed and DF set.\n", - in_ntoa(iph->daddr)); - break; - case ICMP_SR_FAILED: - printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr)); - break; - default: - DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n", - (icmph->code & 7), in_ntoa(iph->daddr))); - break; - } - - /* Get the protocol(s). */ - hash = iph->protocol & (MAX_INET_PROTOS -1); - - /* This can change while we are doing it. */ - ipprot = (struct inet_protocol *) inet_protos[hash]; - while(ipprot != NULL) { - struct inet_protocol *nextip; - - nextip = (struct inet_protocol *) ipprot->next; - - /* Pass it off to everyone who wants it. */ - if (iph->protocol == ipprot->protocol && ipprot->err_handler) { - ipprot->err_handler(err, (unsigned char *)(icmph + 1), - iph->daddr, iph->saddr, ipprot); + break; + case ICMP_HOST_UNREACH: + DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n", + in_ntoa(iph->daddr))); + break; + case ICMP_PROT_UNREACH: + printk("ICMP: %s:%d: protocol unreachable.\n", + in_ntoa(iph->daddr), ntohs(iph->protocol)); + break; + case ICMP_PORT_UNREACH: + DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n", + in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */)); + break; + case ICMP_FRAG_NEEDED: + printk("ICMP: %s: fragmentation needed and DF set.\n", + in_ntoa(iph->daddr)); + break; + case ICMP_SR_FAILED: + printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr)); + break; + default: + DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n", + (icmph->code & 7), in_ntoa(iph->daddr))); + break; } + + /* Get the protocol(s). */ + hash = iph->protocol & (MAX_INET_PROTOS -1); + + /* This can change while we are doing it. */ + ipprot = (struct inet_protocol *) inet_protos[hash]; + while(ipprot != NULL) + { + struct inet_protocol *nextip; + + nextip = (struct inet_protocol *) ipprot->next; + + /* Pass it off to everyone who wants it. */ + if (iph->protocol == ipprot->protocol && ipprot->err_handler) + { + ipprot->err_handler(err, (unsigned char *)(icmph + 1), + iph->daddr, iph->saddr, ipprot); + } - ipprot = nextip; - } - skb->sk = NULL; - kfree_skb(skb, FREE_READ); + ipprot = nextip; + } + skb->sk = NULL; + kfree_skb(skb, FREE_READ); } -/* Handle ICMP_REDIRECT. */ -static void -icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev) +/* + * Handle ICMP_REDIRECT. + */ + +static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev) { - struct iphdr *iph; - unsigned long ip; - - iph = (struct iphdr *) (icmph + 1); - ip = iph->daddr; - switch(icmph->code & 7) { - case ICMP_REDIR_NET: - rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY), - ip, 0, icmph->un.gateway, dev); - break; - case ICMP_REDIR_HOST: - rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY), - ip, 0, icmph->un.gateway, dev); - break; - case ICMP_REDIR_NETTOS: - case ICMP_REDIR_HOSTTOS: - printk("ICMP: cannot handle TOS redirects yet!\n"); + struct iphdr *iph; + unsigned long ip; + + iph = (struct iphdr *) (icmph + 1); + ip = iph->daddr; + switch(icmph->code & 7) + { + case ICMP_REDIR_NET: + rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY), + ip, 0, icmph->un.gateway, dev); + break; + case ICMP_REDIR_HOST: + rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY), + ip, 0, icmph->un.gateway, dev); + break; + case ICMP_REDIR_NETTOS: + case ICMP_REDIR_HOSTTOS: + printk("ICMP: cannot handle TOS redirects yet!\n"); + break; + default: + DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n", + (icmph->code & 7))); break; - default: - DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n", - (icmph->code & 7))); - break; - } - skb->sk = NULL; - kfree_skb(skb, FREE_READ); + } + skb->sk = NULL; + kfree_skb(skb, FREE_READ); } /* Handle ICMP_ECHO ("ping") requests. */ -static void -icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, +static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, unsigned long saddr, unsigned long daddr, int len, struct options *opt) { - struct icmphdr *icmphr; - struct sk_buff *skb2; - int size, offset; - - size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len; - skb2 = alloc_skb(size, GFP_ATOMIC); - if (skb2 == NULL) { - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return; - } - skb2->sk = NULL; - skb2->mem_addr = skb2; - skb2->mem_len = size; - skb2->free = 1; - - /* Build Layer 2-3 headers for message back to source */ - offset = ip_build_header(skb2, daddr, saddr, &dev, - IPPROTO_ICMP, opt, len); - if (offset < 0) { - printk("ICMP: Could not build IP Header for ICMP ECHO Response\n"); - kfree_skb(skb2,FREE_WRITE); - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return; - } - - /* Re-adjust length according to actual IP header size. */ - skb2->len = offset + len; - - /* Build ICMP_ECHO Response message. */ - icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset); - memcpy((char *) icmphr, (char *) icmph, len); - icmphr->type = ICMP_ECHOREPLY; - icmphr->code = 0; - icmphr->checksum = 0; - icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len); - - /* Ship it out - free it when done */ - ip_queue_xmit((struct sock *)NULL, dev, skb2, 1); - - skb->sk = NULL; - kfree_skb(skb, FREE_READ); + struct icmphdr *icmphr; + struct sk_buff *skb2; + int size, offset; + + size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len; + skb2 = alloc_skb(size, GFP_ATOMIC); + if (skb2 == NULL) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return; + } + skb2->sk = NULL; + skb2->free = 1; + + /* Build Layer 2-3 headers for message back to source */ + offset = ip_build_header(skb2, daddr, saddr, &dev, + IPPROTO_ICMP, opt, len, 255, IPTOS_RELIABILITY); + if (offset < 0) + { + printk("ICMP: Could not build IP Header for ICMP ECHO Response\n"); + kfree_skb(skb2,FREE_WRITE); + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return; + } + + /* Re-adjust length according to actual IP header size. */ + skb2->len = offset + len; + + /* Build ICMP_ECHO Response message. */ + icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset); + memcpy((char *) icmphr, (char *) icmph, len); + icmphr->type = ICMP_ECHOREPLY; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len); + + /* Ship it out - free it when done */ + ip_queue_xmit((struct sock *)NULL, dev, skb2, 1); + + skb->sk = NULL; + kfree_skb(skb, FREE_READ); } -/* Handle the ICMP INFORMATION REQUEST. */ -static void -icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, +/* + * Handle the ICMP INFORMATION REQUEST. + */ + +static void icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, unsigned long saddr, unsigned long daddr, int len, struct options *opt) { - /* NOT YET */ - skb->sk = NULL; - kfree_skb(skb, FREE_READ); + /* NOT YET */ + skb->sk = NULL; + kfree_skb(skb, FREE_READ); } -/* Handle ICMP_ADRESS_MASK requests. */ -static void -icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, +/* + * Handle ICMP_ADRESS_MASK requests. + */ + +static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, unsigned long saddr, unsigned long daddr, int len, struct options *opt) { - struct icmphdr *icmphr; - struct sk_buff *skb2; - int size, offset; - - size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len; - skb2 = alloc_skb(size, GFP_ATOMIC); - if (skb2 == NULL) { - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return; - } - skb2->sk = NULL; - skb2->mem_addr = skb2; - skb2->mem_len = size; - skb2->free = 1; - - /* Build Layer 2-3 headers for message back to source */ - offset = ip_build_header(skb2, daddr, saddr, &dev, - IPPROTO_ICMP, opt, len); - if (offset < 0) { - printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n"); - kfree_skb(skb2,FREE_WRITE); - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return; - } - - /* Re-adjust length according to actual IP header size. */ - skb2->len = offset + len; - - /* Build ICMP ADDRESS MASK Response message. */ - icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset); - icmphr->type = ICMP_ADDRESSREPLY; - icmphr->code = 0; - icmphr->checksum = 0; - icmphr->un.echo.id = icmph->un.echo.id; - icmphr->un.echo.sequence = icmph->un.echo.sequence; - memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask)); - - icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len); - - /* Ship it out - free it when done */ - ip_queue_xmit((struct sock *)NULL, dev, skb2, 1); - - skb->sk = NULL; - kfree_skb(skb, FREE_READ); + struct icmphdr *icmphr; + struct sk_buff *skb2; + int size, offset; + + size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len; + skb2 = alloc_skb(size, GFP_ATOMIC); + if (skb2 == NULL) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return; + } + skb2->sk = NULL; + skb2->free = 1; + + /* Build Layer 2-3 headers for message back to source */ + offset = ip_build_header(skb2, daddr, saddr, &dev, + IPPROTO_ICMP, opt, len, 255, IPTOS_RELIABILITY); + if (offset < 0) + { + printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n"); + kfree_skb(skb2,FREE_WRITE); + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return; + } + + /* Re-adjust length according to actual IP header size. */ + skb2->len = offset + len; + + /* Build ICMP ADDRESS MASK Response message. */ + icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset); + icmphr->type = ICMP_ADDRESSREPLY; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->un.echo.id = icmph->un.echo.id; + icmphr->un.echo.sequence = icmph->un.echo.sequence; + memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask)); + + icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len); + + /* Ship it out - free it when done */ + ip_queue_xmit((struct sock *)NULL, dev, skb2, 1); + + skb->sk = NULL; + kfree_skb(skb, FREE_READ); } -/* Deal with incoming ICMP packets. */ -int -icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt, +/* + * Deal with incoming ICMP packets. + */ + +int icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol) { - struct icmphdr *icmph; - unsigned char *buff; + struct icmphdr *icmph; + unsigned char *buff; - /* Drop broadcast packets. */ - if (chk_addr(daddr) == IS_BROADCAST) { - DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n", + /* Drop broadcast packets. */ + if (chk_addr(daddr) == IS_BROADCAST) + { + DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n", in_ntoa(saddr))); - skb1->sk = NULL; - kfree_skb(skb1, FREE_READ); - return(0); - } - - buff = skb1->h.raw; - icmph = (struct icmphdr *) buff; - - /* Validate the packet first */ - if (ip_compute_csum((unsigned char *) icmph, len)) { - /* Failed checksum! */ - printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr)); - skb1->sk = NULL; - kfree_skb(skb1, FREE_READ); - return(0); - } - print_icmp(icmph); - - /* Parse the ICMP message */ - switch(icmph->type) { - case ICMP_TIME_EXCEEDED: - case ICMP_DEST_UNREACH: - case ICMP_SOURCE_QUENCH: - icmp_unreach(icmph, skb1); - return(0); - case ICMP_REDIRECT: - icmp_redirect(icmph, skb1, dev); - return(0); - case ICMP_ECHO: - icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt); - return 0; - case ICMP_ECHOREPLY: - skb1->sk = NULL; - kfree_skb(skb1, FREE_READ); - return(0); - case ICMP_INFO_REQUEST: - icmp_info(icmph, skb1, dev, saddr, daddr, len, opt); - return 0; - case ICMP_INFO_REPLY: - skb1->sk = NULL; - kfree_skb(skb1, FREE_READ); - return(0); - case ICMP_ADDRESS: - icmp_address(icmph, skb1, dev, saddr, daddr, len, opt); - return 0; - case ICMP_ADDRESSREPLY: skb1->sk = NULL; kfree_skb(skb1, FREE_READ); return(0); - default: - DPRINTF((DBG_ICMP, - "ICMP: Unsupported ICMP from %s, type = 0x%X\n", - in_ntoa(saddr), icmph->type)); + } + + buff = skb1->h.raw; + icmph = (struct icmphdr *) buff; + + /* Validate the packet first */ + if (ip_compute_csum((unsigned char *) icmph, len)) + { + /* Failed checksum! */ + printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr)); skb1->sk = NULL; kfree_skb(skb1, FREE_READ); return(0); - } - /*NOTREACHED*/ - skb1->sk = NULL; - kfree_skb(skb1, FREE_READ); - return(-1); + } +#ifdef ICMP_DEBUG + print_icmp(icmph); +#endif + /* Parse the ICMP message */ + switch(icmph->type) + { + case ICMP_TIME_EXCEEDED: + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + icmp_unreach(icmph, skb1); + return(0); + case ICMP_REDIRECT: + icmp_redirect(icmph, skb1, dev); + return(0); + case ICMP_ECHO: + icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_ECHOREPLY: + skb1->sk = NULL; + kfree_skb(skb1, FREE_READ); + return(0); + case ICMP_INFO_REQUEST: + icmp_info(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_INFO_REPLY: + skb1->sk = NULL; + kfree_skb(skb1, FREE_READ); + return(0); + case ICMP_ADDRESS: + icmp_address(icmph, skb1, dev, saddr, daddr, len, opt); + return 0; + case ICMP_ADDRESSREPLY: + skb1->sk = NULL; + kfree_skb(skb1, FREE_READ); + return(0); + default: + DPRINTF((DBG_ICMP, + "ICMP: Unsupported ICMP from %s, type = 0x%X\n", + in_ntoa(saddr), icmph->type)); + skb1->sk = NULL; + kfree_skb(skb1, FREE_READ); + return(0); + } + /*NOTREACHED*/ + skb1->sk = NULL; + kfree_skb(skb1, FREE_READ); + return(-1); } -/* Perform any ICMP-related I/O control requests. */ -int -icmp_ioctl(struct sock *sk, int cmd, unsigned long arg) +/* + * Perform any ICMP-related I/O control requests. + * + * In the case of ICMP all the user can do is play with the debygging + */ + +int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg) { - switch(cmd) { - case DDIOCSDBG: - return(dbg_ioctl((void *) arg, DBG_ICMP)); - default: - return(-EINVAL); - } - return(0); + switch(cmd) + { + case DDIOCSDBG: + return(dbg_ioctl((void *) arg, DBG_ICMP)); + default: + return(-EINVAL); + } + return(0); } diff --git a/net/inet/ip.c b/net/inet/ip.c index 4c9f924..a3cc4ec 100644 --- a/net/inet/ip.c +++ b/net/inet/ip.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) module. * - * Version: @(#)ip.c 1.0.16b 9/1/93 + * Version: @(#)ip.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -31,10 +31,14 @@ * Gerhard Koerting: IP interface addressing fix. * Linus Torvalds : More robustness checks * Alan Cox : Even more checks: Still not as robust as it ought to be + * Alan Cox : Reformatted for neatness and final release. + * Alan Cox : Tags ip header for RAW sockets, and for accept(). Old + * method wasn't suitable for AX.25 + * Alan Cox : Most of the ip_options processing logic added. * * To Fix: - * IP option processing is mostly not needed. ip_forward needs to know about routing rules - * and time stamp but that's about all. + * RFC791 states that options are a 'required' feature of an + * IP implementation. We don't do options at all. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -52,141 +56,176 @@ #include <linux/sockios.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "eth.h" #include "ip.h" #include "protocol.h" #include "route.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "arp.h" #include "icmp.h" -#define CONFIG_IP_FORWARD -#define CONFIG_IP_DEFRAG +/* + * These two can normally be left. In olden times the numerous bugs used to + * make forwarding go crazy on some nets and fragmentation fragment your + * computer 8-) + */ + +#define CONFIG_IP_FORWARD /* Forwarding ? */ +#define CONFIG_IP_DEFRAG /* Fragmentation ? */ + extern int last_retran; extern void sort_send(struct sock *sk); -void -ip_print(struct iphdr *ip) +#ifdef IP_DEBUG + +void ip_print(struct iphdr *ip) { - unsigned char buff[32]; - unsigned char *ptr; - int addr, len, i; - - if (inet_debug != DBG_IP) return; - - /* Dump the IP header. */ - printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n", - ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len)); - printk(" id=%X, ttl=%d, prot=%d, check=%X\n", - ip->id, ip->ttl, ip->protocol, ip->check); - printk(" frag_off=%d\n", ip->frag_off); - printk(" soucre=%s ", in_ntoa(ip->saddr)); - printk("dest=%s\n", in_ntoa(ip->daddr)); - printk(" ----\n"); - - /* Dump the data. */ - ptr = (unsigned char *)(ip + 1); - addr = 0; - len = ntohs(ip->tot_len) - (4 * ip->ihl); - while (len > 0) { - printk(" %04X: ", addr); - for(i = 0; i < 16; i++) { - if (len > 0) { - printk("%02X ", (*ptr & 0xFF)); - buff[i] = *ptr++; - if (buff[i] < 32 || buff[i] > 126) buff[i] = '.'; - } else { - printk(" "); - buff[i] = ' '; - } - addr++; - len--; - }; - buff[i] = '\0'; - printk(" \"%s\"\n", buff); - } - printk(" ----\n\n"); + unsigned char buff[32]; + unsigned char *ptr; + int addr, len, i; + + if (inet_debug != DBG_IP) + return; + + /* Dump the IP header. */ + printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n", + ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len)); + printk(" id=%X, ttl=%d, prot=%d, check=%X\n", + ip->id, ip->ttl, ip->protocol, ip->check); + printk(" frag_off=%d\n", ip->frag_off); + printk(" soucre=%s ", in_ntoa(ip->saddr)); + printk("dest=%s\n", in_ntoa(ip->daddr)); + printk(" ----\n"); + + /* Dump the data. */ + ptr = (unsigned char *)(ip + 1); + addr = 0; + len = ntohs(ip->tot_len) - (4 * ip->ihl); + while (len > 0) + { + printk(" %04X: ", addr); + for(i = 0; i < 16; i++) + { + if (len > 0) + { + printk("%02X ", (*ptr & 0xFF)); + buff[i] = *ptr++; + if (buff[i] < 32 || buff[i] > 126) buff[i] = '.'; + } + else + { + printk(" "); + buff[i] = ' '; + } + addr++; + len--; + }; + buff[i] = '\0'; + printk(" \"%s\"\n", buff); + } + printk(" ----\n\n"); } +#endif -int -ip_ioctl(struct sock *sk, int cmd, unsigned long arg) +/* + * Low level user requests to the IP device. NOT that same as IP layer + * socket requests (which also do nothing useful at the moment) + */ + +int ip_ioctl(struct sock *sk, int cmd, unsigned long arg) { - switch(cmd) { - case DDIOCSDBG: - return(dbg_ioctl((void *) arg, DBG_IP)); - default: - return(-EINVAL); - } + switch(cmd) + { + case DDIOCSDBG: + return(dbg_ioctl((void *) arg, DBG_IP)); + default: + return(-EINVAL); + } } -/* these two routines will do routining. */ -static void -strict_route(struct iphdr *iph, struct options *opt) +/* + * These two routines will do routing when we have ip options support + * (RFC 791 page 18,19) + */ + +static void strict_route(struct iphdr *iph, struct options *opt) { } -static void -loose_route(struct iphdr *iph, struct options *opt) +static void loose_route(struct iphdr *iph, struct options *opt) { } -static void -print_ipprot(struct inet_protocol *ipprot) +static void print_ipprot(struct inet_protocol *ipprot) { - DPRINTF((DBG_IP, "handler = %X, protocol = %d, copy=%d \n", - ipprot->handler, ipprot->protocol, ipprot->copy)); + DPRINTF((DBG_IP, "handler = %X, protocol = %d, copy=%d \n", + ipprot->handler, ipprot->protocol, ipprot->copy)); } -/* This routine will check to see if we have lost a gateway. */ -void -ip_route_check(unsigned long daddr) +/* + * This routine will check to see if we have lost a gateway. + */ + +void ip_route_check(unsigned long daddr) { } -#if 0 -/* this routine puts the options at the end of an ip header. */ -static int -build_options(struct iphdr *iph, struct options *opt) +/* + * This routine puts the options at the end of an ip header. + */ + +static int build_options(struct iphdr *iph, struct options *opt) { - unsigned char *ptr; - /* currently we don't support any options. */ - ptr = (unsigned char *)(iph+1); - *ptr = 0; - return (4); + unsigned char *ptr; + ptr = (unsigned char *)(iph+1); + /* currently we don't support any options. */ + if(opt==NULL) + { + *ptr = 0; + return (1); + } + else + { + memcpy(ptr,opt->option_data,opt->option_length); + return((opt->option_length+3)/4); + } } -#endif -/* Take an skb, and fill in the MAC header. */ -static int -ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, +/* + * Take an skb, and fill in the MAC header. + */ + +static int ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, unsigned long saddr) { - unsigned char *ptr; - int mac; - - ptr = (unsigned char *)(skb + 1); - mac = 0; - skb->arp = 1; - if (dev->hard_header) { - mac = dev->hard_header(ptr, dev, ETH_P_IP, daddr, saddr, len); - } - if (mac < 0) { - mac = -mac; - skb->arp = 0; - } - skb->dev = dev; - return(mac); + unsigned char *ptr; + int mac; + + ptr = (unsigned char *)(skb + 1); + mac = 0; + skb->arp = 1; + if (dev->hard_header) + { + mac = dev->hard_header(ptr, dev, ETH_P_IP, daddr, saddr, len); + } + if (mac < 0) + { + mac = -mac; + skb->arp = 0; + } + skb->dev = dev; + return(mac); } @@ -196,333 +235,419 @@ ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, * protocol knows what it's doing, otherwise it uses the * routing/ARP tables to select a device struct. */ -int -ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, - struct device **dev, int type, struct options *opt, int len) +int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, + struct device **dev, int type, struct options *opt, int len, int ttl,int tos) { - static struct options optmem; - struct iphdr *iph; - struct rtable *rt; - unsigned char *buff; - unsigned long raddr; - static int count = 0; - int tmp; - - if (saddr == 0) - saddr = my_addr(); - - DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n" - " type=%d, opt=%X, len = %d)\n", - skb, saddr, daddr, *dev, type, opt, len)); + static struct options optmem; + struct iphdr *iph; + struct rtable *rt; + unsigned char *buff; + unsigned long raddr; + static int count = 0; + int tmp; + int optlen; + + if (saddr == 0) + saddr = my_addr(); + + DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n" + " type=%d, opt=%X, len = %d)\n", + skb, saddr, daddr, *dev, type, opt, len)); - buff = (unsigned char *)(skb + 1); + buff = (unsigned char *)(skb + 1); - /* See if we need to look up the device. */ - if (*dev == NULL) { - rt = rt_route(daddr, &optmem); - if (rt == NULL) - return(-ENETUNREACH); + /* See if we need to look up the device. */ + if (*dev == NULL) + { + rt = rt_route(daddr, &optmem); + if (rt == NULL) + return(-ENETUNREACH); - *dev = rt->rt_dev; - if (saddr == 0x0100007FL && daddr != 0x0100007FL) - saddr = rt->rt_dev->pa_addr; - raddr = rt->rt_gateway; + *dev = rt->rt_dev; + if (saddr == 0x0100007FL && daddr != 0x0100007FL) + saddr = rt->rt_dev->pa_addr; + raddr = rt->rt_gateway; - DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr))); - opt = &optmem; - } else { - /* We still need the address of the first hop. */ - rt = rt_route(daddr, &optmem); - raddr = (rt == NULL) ? 0 : rt->rt_gateway; - } - if (raddr == 0) - raddr = daddr; - - /* Now build the MAC header. */ - tmp = ip_send(skb, raddr, len, *dev, saddr); - buff += tmp; - len -= tmp; - - skb->dev = *dev; - skb->saddr = saddr; - if (skb->sk) skb->sk->saddr = saddr; - - /* Now build the IP header. */ - - /* If we are using IPPROTO_RAW, then we don't need an IP header, since - one is being supplied to us by the user */ - - if(type == IPPROTO_RAW) return (tmp); - - iph = (struct iphdr *)buff; - iph->version = 4; - iph->tos = 0; - iph->frag_off = 0; - iph->ttl = 32; - iph->daddr = daddr; - iph->saddr = saddr; - iph->protocol = type; - iph->ihl = 5; - iph->id = htons(count++); - - /* Setup the IP options. */ -#ifdef Not_Yet_Avail - build_options(iph, opt); -#endif + DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr))); + opt = &optmem; + } + else + { + /* We still need the address of the first hop. */ + rt = rt_route(daddr, &optmem); + raddr = (rt == NULL) ? 0 : rt->rt_gateway; + } + if (raddr == 0) + raddr = daddr; + + /* Now build the MAC header. */ + tmp = ip_send(skb, raddr, len, *dev, saddr); + buff += tmp; + len -= tmp; + + skb->dev = *dev; + skb->saddr = saddr; + if (skb->sk) + skb->sk->saddr = saddr; + + /* Now build the IP header. */ + + /* If we are using IPPROTO_RAW, then we don't need an IP header, since + one is being supplied to us by the user */ + + if(type == IPPROTO_RAW) + return (tmp); + + iph = (struct iphdr *)buff; + iph->version = 4; + iph->tos = tos; + iph->frag_off = 0; + iph->ttl = ttl; + iph->daddr = daddr; + iph->saddr = saddr; + iph->protocol = type; + iph->ihl = 5; + iph->id = htons(count++); + + /* Setup the IP options. Length is in longs.*/ - return(20 + tmp); /* IP header plus MAC header size */ + optlen=build_options(iph, opt); + iph->ihl+=optlen; + + return(20 + tmp + 4*optlen); /* IP header plus MAC header size */ } -static int -do_options(struct iphdr *iph, struct options *opt) +/* + * Interpret the incoming options + */ + +static int do_options(struct iphdr *iph, struct options **opt_ptr, struct device *dev) { - unsigned char *buff; - int done = 0; - int i, len = sizeof(struct iphdr); - - /* Zero out the options. */ - opt->record_route.route_size = 0; - opt->loose_route.route_size = 0; - opt->strict_route.route_size = 0; - opt->tstamp.ptr = 0; - opt->security = 0; - opt->compartment = 0; - opt->handling = 0; - opt->stream = 0; - opt->tcc = 0; - return(0); - - /* Advance the pointer to start at the options. */ - buff = (unsigned char *)(iph + 1); - - /* Now start the processing. */ - while (!done && len < iph->ihl*4) switch(*buff) { - case IPOPT_END: - done = 1; - break; - case IPOPT_NOOP: - buff++; - len++; - break; - case IPOPT_SEC: - buff++; - if (*buff != 11) return(1); - buff++; - opt->security = ntohs(*(unsigned short *)buff); - buff += 2; - opt->compartment = ntohs(*(unsigned short *)buff); - buff += 2; - opt->handling = ntohs(*(unsigned short *)buff); - buff += 2; - opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1)); - buff += 3; - len += 11; - break; - case IPOPT_LSRR: - buff++; - if ((*buff - 3)% 4 != 0) return(1); - len += *buff; - opt->loose_route.route_size = (*buff -3)/4; - buff++; - if (*buff % 4 != 0) return(1); - opt->loose_route.pointer = *buff/4 - 1; - buff++; - buff++; - for (i = 0; i < opt->loose_route.route_size; i++) { - if(i>=MAX_ROUTE) + unsigned char *buff; + int done = 0; + int i, len = sizeof(struct iphdr); + unsigned char *outbuf; + struct options *opt; + int ol; + int optsiz; + + if(iph->ihl==5) + { + *opt_ptr=NULL; + return(0); + } + + /* Allocate a buffer to stuff the options (decoded) and the raw option data into */ + + ol=(iph->ihl*4)-sizeof(struct iphdr); + + opt=(struct options *)kmalloc(ol+sizeof(*opt),GFP_ATOMIC); + *opt_ptr=opt; + + if(opt==NULL) + return(1); + + opt->option_length=ol; + outbuf=(unsigned char *)(opt+1); + opt->option_data=outbuf; + + /* Zero out the options. */ + opt->record_route.route_size = 0; + opt->loose_route.route_size = 0; + opt->strict_route.route_size = 0; + opt->tstamp.ptr = 0; + opt->security = 0; + opt->compartment = 0; + opt->handling = 0; + opt->stream = 0; + opt->tcc = 0; + + + /* Advance the pointer to start at the options. */ + buff = (unsigned char *)(iph + 1); + /* Copy the data */ + memcpy(outbuf,buff,opt->option_length); + buff = outbuf; + + /* Now start the processing. */ + while (!done && len < iph->ihl*4) switch(*buff) + { + case IPOPT_END: + done = 1; + break; + case IPOPT_NOOP: + buff++; + len++; + break; + case IPOPT_SEC: + buff++; + if (*buff != 11) return(1); - opt->loose_route.route[i] = *(unsigned long *)buff; - buff += 4; - } - break; - case IPOPT_SSRR: - buff++; - if ((*buff - 3)% 4 != 0) return(1); - len += *buff; - opt->strict_route.route_size = (*buff -3)/4; - buff++; - if (*buff % 4 != 0) return(1); - opt->strict_route.pointer = *buff/4 - 1; - buff++; - buff++; - for (i = 0; i < opt->strict_route.route_size; i++) { - if(i>=MAX_ROUTE) + buff++; + opt->security = ntohs(*(unsigned short *)buff); + buff += 2; + opt->compartment = ntohs(*(unsigned short *)buff); + buff += 2; + opt->handling = ntohs(*(unsigned short *)buff); + buff += 2; + opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1)); + buff += 3; + len += 11; + break; + case IPOPT_LSRR: + buff++; + if ((*buff - 3)% 4 != 0) return(1); - opt->strict_route.route[i] = *(unsigned long *)buff; - buff += 4; - } - break; - case IPOPT_RR: - buff++; - if ((*buff - 3)% 4 != 0) return(1); - len += *buff; - opt->record_route.route_size = (*buff -3)/4; - buff++; - if (*buff % 4 != 0) return(1); - opt->record_route.pointer = *buff/4 - 1; - buff++; - buff++; - for (i = 0; i < opt->record_route.route_size; i++) { - if(i>=MAX_ROUTE) + if(*buff<2) + return(1); + len += (optsiz= *buff); + opt->loose_route.route_size = (*buff -3)/4; + buff++; + if (*buff % 4 != 0) + return(1); + opt->loose_route.pointer = *buff/4 - 1; + if(*buff<=optsiz) + *buff+=4; /* Move on a route */ + buff++; + buff++; + for (i = 0; i < opt->loose_route.route_size; i++) + { + if(i>=MAX_ROUTE) + return(1); + if(i==opt->strict_route.pointer) + *(unsigned long *)buff=dev->pa_addr; + opt->loose_route.route[i] = *(unsigned long *)buff; + buff += 4; + } + break; + case IPOPT_SSRR: + buff++; + if ((*buff - 3)% 4 != 0) + return(1); + if(*buff<2) + return(1); + len += (optsiz= *buff); + opt->strict_route.route_size = (*buff -3)/4; + buff++; + if (*buff % 4 != 0) + return(1); + opt->strict_route.pointer = *buff/4 - 1; + if(*buff<=optsiz) + *buff+=4; + buff++; + buff++; + for (i = 0; i < opt->strict_route.route_size; i++) + { + if(i>=MAX_ROUTE) + return(1); + if(i==opt->strict_route.pointer) + *(unsigned long *)buff=dev->pa_addr; + opt->strict_route.route[i] = *(unsigned long *)buff; + buff += 4; + } + break; + case IPOPT_RR: + buff++; + if ((*buff - 3)% 4 != 0) + return(1); + if(*buff<2) + return(1); + len += (optsiz= *buff); + opt->record_route.route_size = (*buff -3)/4; + buff++; + if (*buff % 4 != 0) + return(1); + opt->record_route.pointer = *buff/4 - 1; + if(*buff+4<=optsiz) + *buff+=4; + buff++; + buff++; + for (i = 0; i < opt->record_route.route_size; i++) + { + if(i>=MAX_ROUTE) + return 1; + if(i==opt->record_route.pointer) + *(unsigned long *)buff=dev->pa_addr; + opt->record_route.route[i] = *(unsigned long *)buff; + buff += 4; + } + break; + case IPOPT_SID: + len += 4; + buff +=2; + opt->stream = *(unsigned short *)buff; + buff += 2; + break; + case IPOPT_TIMESTAMP: + /* FIXME: This one isn't altered correctly yet */ + buff++; + if(*buff<2) return 1; - opt->record_route.route[i] = *(unsigned long *)buff; - buff += 4; - } - break; - case IPOPT_SID: - len += 4; - buff +=2; - opt->stream = *(unsigned short *)buff; - buff += 2; - break; - case IPOPT_TIMESTAMP: - buff++; - len += *buff; - if (*buff % 4 != 0) return(1); - opt->tstamp.len = *buff / 4 - 1; - buff++; - if ((*buff - 1) % 4 != 0) return(1); - opt->tstamp.ptr = (*buff-1)/4; - buff++; - opt->tstamp.x.full_char = *buff; - buff++; - for (i = 0; i < opt->tstamp.len; i++) { - opt->tstamp.data[i] = *(unsigned long *)buff; - buff += 4; - } - break; - default: - return(1); - } + len += *buff; + if (*buff % 4 != 0) + return(1); + opt->tstamp.len = *buff / 4 - 1; + buff++; + if ((*buff - 1) % 4 != 0) + return(1); + opt->tstamp.ptr = (*buff-1)/4; + buff++; + opt->tstamp.x.full_char = *buff; + buff++; + for (i = 0; i < opt->tstamp.len; i++) + { + opt->tstamp.data[i] = *(unsigned long *)buff; + buff += 4; + } + break; + default: + return(1); + } - if (opt->record_route.route_size == 0) { - if (opt->strict_route.route_size != 0) { - memcpy(&(opt->record_route), &(opt->strict_route), + if (opt->record_route.route_size == 0) + { + if (opt->strict_route.route_size != 0) + { + memcpy(&(opt->record_route), &(opt->strict_route), sizeof(opt->record_route)); - } else if (opt->loose_route.route_size != 0) { - memcpy(&(opt->record_route), &(opt->loose_route), + } + else if (opt->loose_route.route_size != 0) + { + memcpy(&(opt->record_route), &(opt->loose_route), sizeof(opt->record_route)); + } } - } - if (opt->strict_route.route_size != 0 && - opt->strict_route.route_size != opt->strict_route.pointer) { - strict_route(iph, opt); - return(0); - } + if (opt->strict_route.route_size != 0 && + opt->strict_route.route_size != opt->strict_route.pointer) + { + strict_route(iph, opt); + return(0); + } - if (opt->loose_route.route_size != 0 && - opt->loose_route.route_size != opt->loose_route.pointer) { - loose_route(iph, opt); - return(0); - } + if (opt->loose_route.route_size != 0 && + opt->loose_route.route_size != opt->loose_route.pointer) + { + loose_route(iph, opt); + return(0); + } - return(0); + return(0); } -/* This is a version of ip_compute_csum() optimized for IP headers, which - always checksum on 4 octet boundaries. */ -static inline unsigned short -ip_fast_csum(unsigned char * buff, int wlen) +/* + * This is a version of ip_compute_csum() optimized for IP headers, which + * always checksum on 4 octet boundaries. + */ + +static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen) { - unsigned long sum = 0; - - if (wlen) { - unsigned long bogus; - __asm__("clc\n" - "1:\t" - "lodsl\n\t" - "adcl %3, %0\n\t" - "decl %2\n\t" - "jne 1b\n\t" - "adcl $0, %0\n\t" - "movl %0, %3\n\t" - "shrl $16, %3\n\t" - "addw %w3, %w0\n\t" - "adcw $0, %w0" - : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus) - : "0" (sum), "1" (buff), "2" (wlen)); - } - return (~sum) & 0xffff; + unsigned long sum = 0; + + if (wlen) { + unsigned long bogus; + __asm__("clc\n" + "1:\t" + "lodsl\n\t" + "adcl %3, %0\n\t" + "decl %2\n\t" + "jne 1b\n\t" + "adcl $0, %0\n\t" + "movl %0, %3\n\t" + "shrl $16, %3\n\t" + "addw %w3, %w0\n\t" + "adcw $0, %w0" + : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus) + : "0" (sum), "1" (buff), "2" (wlen)); + } + return (~sum) & 0xffff; } /* * This routine does all the checksum computations that don't * require anything special (like copying or special headers). */ -unsigned short -ip_compute_csum(unsigned char * buff, int len) + +unsigned short ip_compute_csum(unsigned char * buff, int len) { - unsigned long sum = 0; - - /* Do the first multiple of 4 bytes and convert to 16 bits. */ - if (len > 3) { - __asm__("clc\n" - "1:\t" - "lodsl\n\t" - "adcl %%eax, %%ebx\n\t" - "loop 1b\n\t" - "adcl $0, %%ebx\n\t" - "movl %%ebx, %%eax\n\t" - "shrl $16, %%eax\n\t" - "addw %%ax, %%bx\n\t" - "adcw $0, %%bx" - : "=b" (sum) , "=S" (buff) - : "0" (sum), "c" (len >> 2) ,"1" (buff) - : "ax", "cx", "si", "bx" ); - } - if (len & 2) { - __asm__("lodsw\n\t" - "addw %%ax, %%bx\n\t" - "adcw $0, %%bx" - : "=b" (sum), "=S" (buff) - : "0" (sum), "1" (buff) - : "bx", "ax", "si"); - } - if (len & 1) { - __asm__("lodsb\n\t" - "movb $0, %%ah\n\t" - "addw %%ax, %%bx\n\t" - "adcw $0, %%bx" - : "=b" (sum), "=S" (buff) - : "0" (sum), "1" (buff) - : "bx", "ax", "si"); - } - sum =~sum; - return(sum & 0xffff); + unsigned long sum = 0; + + /* Do the first multiple of 4 bytes and convert to 16 bits. */ + if (len > 3) { + __asm__("clc\n" + "1:\t" + "lodsl\n\t" + "adcl %%eax, %%ebx\n\t" + "loop 1b\n\t" + "adcl $0, %%ebx\n\t" + "movl %%ebx, %%eax\n\t" + "shrl $16, %%eax\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum) , "=S" (buff) + : "0" (sum), "c" (len >> 2) ,"1" (buff) + : "ax", "cx", "si", "bx" ); + } + if (len & 2) { + __asm__("lodsw\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum), "=S" (buff) + : "0" (sum), "1" (buff) + : "bx", "ax", "si"); + } + if (len & 1) { + __asm__("lodsb\n\t" + "movb $0, %%ah\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum), "=S" (buff) + : "0" (sum), "1" (buff) + : "bx", "ax", "si"); + } + sum =~sum; + return(sum & 0xffff); } -/* Check the header of an incoming IP datagram. This version is still used in slhc.c. */ -int -ip_csum(struct iphdr *iph) +/* + * Check the header of an incoming IP datagram. This version is still used in slhc.c. + */ + +int ip_csum(struct iphdr *iph) { - return ip_fast_csum((unsigned char *)iph, iph->ihl); + return ip_fast_csum((unsigned char *)iph, iph->ihl); } -/* Generate a checksym for an outgoing IP datagram. */ -static void -ip_send_check(struct iphdr *iph) +/* + * Generate a checksym for an outgoing IP datagram. (RFC791, Page 14) + */ + +static void ip_send_check(struct iphdr *iph) { - iph->check = 0; - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + iph->check = 0; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } /************************ Fragment Handlers From NET2E not yet with tweaks to beat 4K **********************************/ static struct ipq *ipqueue = NULL; /* IP fragment queue */ - /* Create a new fragment entry. */ + +/* + * Create a new fragment entry. + */ + static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr) { - struct ipfrag *fp; + struct ipfrag *fp; - fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC); - if (fp == NULL) - { + fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC); + if (fp == NULL) + { printk("IP: frag_create: no memory left !\n"); return(NULL); - } - memset(fp, 0, sizeof(struct ipfrag)); + } + memset(fp, 0, sizeof(struct ipfrag)); /* Fill in the structure. */ fp->offset = offset; @@ -539,6 +664,7 @@ static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, u * Find the correct entry in the "incomplete datagrams" queue for * this IP datagram, and return the queue entry address if found. */ + static struct ipq *ip_find(struct iphdr *iph) { struct ipq *qp; @@ -548,14 +674,14 @@ static struct ipq *ip_find(struct iphdr *iph) qplast = NULL; for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next) { - if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr && + if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr && iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol) { del_timer(&qp->timer); /* So it doesnt vanish on us. The timer will be reset anyway */ - sti(); - return(qp); - } - } + sti(); + return(qp); + } + } sti(); return(NULL); } @@ -583,60 +709,62 @@ static void ip_free(struct ipq *qp) ipqueue = qp->next; if (ipqueue != NULL) ipqueue->prev = NULL; - } - else - { - qp->prev->next = qp->next; - if (qp->next != NULL) - qp->next->prev = qp->prev; - } - - /* Release all fragment data. */ + } + else + { + qp->prev->next = qp->next; + if (qp->next != NULL) + qp->next->prev = qp->prev; + } + + /* Release all fragment data. */ /* printk("ip_free: kill frag data\n");*/ - fp = qp->fragments; - while (fp != NULL) - { - xp = fp->next; - IS_SKB(fp->skb); - kfree_skb(fp->skb,FREE_READ); - kfree_s(fp, sizeof(struct ipfrag)); - fp = xp; - } - + fp = qp->fragments; + while (fp != NULL) + { + xp = fp->next; + IS_SKB(fp->skb); + kfree_skb(fp->skb,FREE_READ); + kfree_s(fp, sizeof(struct ipfrag)); + fp = xp; + } + /* printk("ip_free: cleanup\n");*/ - /* Release the MAC header. */ - kfree_s(qp->mac, qp->maclen); + /* Release the MAC header. */ + kfree_s(qp->mac, qp->maclen); - /* Release the IP header. */ - kfree_s(qp->iph, qp->ihlen + 8); + /* Release the IP header. */ + kfree_s(qp->iph, qp->ihlen + 8); - /* Finally, release the queue descriptor itself. */ - kfree_s(qp, sizeof(struct ipq)); + /* Finally, release the queue descriptor itself. */ + kfree_s(qp, sizeof(struct ipq)); /* printk("ip_free:done\n");*/ - sti(); + sti(); } - /* Oops- a fragment queue timed out. Kill it and send an ICMP reply. */ +/* + * Oops- a fragment queue timed out. Kill it and send an ICMP reply. + */ static void ip_expire(unsigned long arg) { - struct ipq *qp; + struct ipq *qp; - qp = (struct ipq *)arg; - DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp)); + qp = (struct ipq *)arg; + DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp)); - /* Send an ICMP "Fragment Reassembly Timeout" message. */ + /* Send an ICMP "Fragment Reassembly Timeout" message. */ #if 0 - icmp_send(qp->iph->ip_src.s_addr, ICMP_TIME_EXCEEDED, - ICMP_EXC_FRAGTIME, qp->iph); + icmp_send(qp->iph->ip_src.s_addr, ICMP_TIME_EXCEEDED, + ICMP_EXC_FRAGTIME, qp->iph); #endif - if(qp->fragments!=NULL) - icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED, - ICMP_EXC_FRAGTIME, qp->dev); + if(qp->fragments!=NULL) + icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED, + ICMP_EXC_FRAGTIME, qp->dev); - /* Nuke the fragment queue. */ + /* Nuke the fragment queue. */ ip_free(qp); } @@ -650,158 +778,168 @@ static void ip_expire(unsigned long arg) static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev) { - struct ipq *qp; - int maclen; - int ihlen; + struct ipq *qp; + int maclen; + int ihlen; - qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC); - if (qp == NULL) - { + qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC); + if (qp == NULL) + { printk("IP: create: no memory left !\n"); return(NULL); - } - memset(qp, 0, sizeof(struct ipq)); - - /* Allocate memory for the MAC header. */ - maclen = ((unsigned long) iph) - ((unsigned long) (skb + 1)); - qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC); - if (qp->mac == NULL) - { + } + memset(qp, 0, sizeof(struct ipq)); + + /* Allocate memory for the MAC header. */ + maclen = ((unsigned long) iph) - ((unsigned long) (skb + 1)); + qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC); + if (qp->mac == NULL) + { printk("IP: create: no memory left !\n"); kfree_s(qp, sizeof(struct ipq)); return(NULL); - } + } - /* Allocate memory for the IP header (plus 8 octects for ICMP). */ - ihlen = (iph->ihl * sizeof(unsigned long)); - qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC); - if (qp->iph == NULL) - { + /* Allocate memory for the IP header (plus 8 octects for ICMP). */ + ihlen = (iph->ihl * sizeof(unsigned long)); + qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC); + if (qp->iph == NULL) + { printk("IP: create: no memory left !\n"); kfree_s(qp->mac, maclen); kfree_s(qp, sizeof(struct ipq)); return(NULL); - } - - /* Fill in the structure. */ - memcpy(qp->mac, (skb + 1), maclen); - memcpy(qp->iph, iph, ihlen + 8); - qp->len = 0; - qp->ihlen = ihlen; - qp->maclen = maclen; - qp->fragments = NULL; - qp->dev = dev; + } + + /* Fill in the structure. */ + memcpy(qp->mac, (skb + 1), maclen); + memcpy(qp->iph, iph, ihlen + 8); + qp->len = 0; + qp->ihlen = ihlen; + qp->maclen = maclen; + qp->fragments = NULL; + qp->dev = dev; /* printk("Protocol = %d\n",qp->iph->protocol);*/ - /* Start a timer for this entry. */ - qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ - qp->timer.data = (unsigned long) qp; /* pointer to queue */ - qp->timer.function = ip_expire; /* expire function */ - add_timer(&qp->timer); - - /* Add this entry to the queue. */ - qp->prev = NULL; - cli(); - qp->next = ipqueue; - if (qp->next != NULL) - qp->next->prev = qp; - ipqueue = qp; - sti(); - return(qp); + /* Start a timer for this entry. */ + qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ + qp->timer.data = (unsigned long) qp; /* pointer to queue */ + qp->timer.function = ip_expire; /* expire function */ + add_timer(&qp->timer); + + /* Add this entry to the queue. */ + qp->prev = NULL; + cli(); + qp->next = ipqueue; + if (qp->next != NULL) + qp->next->prev = qp; + ipqueue = qp; + sti(); + return(qp); } - /* See if a fragment queue is complete. */ +/* + * See if a fragment queue is complete. + */ + static int ip_done(struct ipq *qp) { struct ipfrag *fp; int offset; - /* Only possible if we received the final fragment. */ - if (qp->len == 0) - return(0); - - /* Check all fragment offsets to see if they connect. */ - fp = qp->fragments; - offset = 0; - while (fp != NULL) - { - if (fp->offset > offset) - return(0); /* fragment(s) missing */ - offset = fp->end; - fp = fp->next; - } - - /* All fragments are present. */ - return(1); + /* Only possible if we received the final fragment. */ + if (qp->len == 0) + return(0); + + /* Check all fragment offsets to see if they connect. */ + fp = qp->fragments; + offset = 0; + while (fp != NULL) + { + if (fp->offset > offset) + return(0); /* fragment(s) missing */ + offset = fp->end; + fp = fp->next; + } + + /* All fragments are present. */ + return(1); } -/* Build a new IP datagram from all its fragments. */ +/* + * Build a new IP datagram from all its fragments. + */ + static struct sk_buff *ip_glue(struct ipq *qp) { struct sk_buff *skb; - struct iphdr *iph; - struct ipfrag *fp; - unsigned char *ptr; - int count, len; - - /* Allocate a new buffer for the datagram. */ - len = sizeof(struct sk_buff)+qp->maclen + qp->ihlen + qp->len; - if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) - { - printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp); - ip_free(qp); - return(NULL); - } - - /* Fill in the basic details. */ - skb->len = (len - qp->maclen); - skb->h.raw = (unsigned char *) (skb + 1); - skb->free = 1; - skb->lock = 1; - - /* Copy the original MAC and IP headers into the new buffer. */ - ptr = (unsigned char *) skb->h.raw; - memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen); + struct iphdr *iph; + struct ipfrag *fp; + unsigned char *ptr; + int count, len; + + /* Allocate a new buffer for the datagram. */ + len = sizeof(struct sk_buff)+qp->maclen + qp->ihlen + qp->len; + if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) + { + printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp); + ip_free(qp); + return(NULL); + } + + /* Fill in the basic details. */ + skb->len = (len - qp->maclen); + skb->h.raw = (unsigned char *) (skb + 1); + skb->free = 1; + skb->lock = 1; + + /* Copy the original MAC and IP headers into the new buffer. */ + ptr = (unsigned char *) skb->h.raw; + memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen); /* printk("Copied %d bytes of mac header.\n",qp->maclen);*/ - ptr += qp->maclen; - memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen); + ptr += qp->maclen; + memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen); /* printk("Copied %d byte of ip header.\n",qp->ihlen);*/ - ptr += qp->ihlen; - skb->h.raw += qp->maclen; - + ptr += qp->ihlen; + skb->h.raw += qp->maclen; + /* printk("Protocol = %d\n",skb->h.iph->protocol);*/ - count = 0; - - /* Copy the data portions of all fragments into the new buffer. */ - fp = qp->fragments; - while(fp != NULL) - { - if(count+fp->len>skb->len) - { - printk("Invalid fragment list: Fragment over size.\n"); - kfree_skb(skb,FREE_WRITE); - return NULL; - } + count = 0; + + /* Copy the data portions of all fragments into the new buffer. */ + fp = qp->fragments; + while(fp != NULL) + { + if(count+fp->len>skb->len) + { + /* In case some fool sends us a silly fragment. */ + printk("Invalid fragment list: Fragment over size.\n"); + kfree_skb(skb,FREE_WRITE); + return NULL; + } /* printk("Fragment %d size %d\n",fp->offset,fp->len);*/ - memcpy((ptr + fp->offset), fp->ptr, fp->len); - count += fp->len; - fp = fp->next; - } - - /* We glued together all fragments, so remove the queue entry. */ - ip_free(qp); - - /* Done with all fragments. Fixup the new IP header. */ - iph = skb->h.iph; - iph->frag_off = 0; - iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count); - return(skb); + memcpy((ptr + fp->offset), fp->ptr, fp->len); + count += fp->len; + fp = fp->next; + } + + /* We glued together all fragments, so remove the queue entry. */ + ip_free(qp); + + /* Done with all fragments. Fixup the new IP header. */ + iph = skb->h.iph; + iph->frag_off = 0; + iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count); + return(skb); } -/* Process an incoming IP datagram fragment. */ +/* + * Process an incoming IP datagram fragment. + */ + static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev) { struct ipfrag *prev, *next; @@ -813,135 +951,135 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct int i, ihl, end; /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */ - qp = ip_find(iph); - - /* Is this a non-fragmented datagram? */ - offset = ntohs(iph->frag_off); - flags = offset & ~IP_OFFSET; - offset &= IP_OFFSET; - if (((flags & IP_MF) == 0) && (offset == 0)) - { - if (qp != NULL) - ip_free(qp); /* Huh? How could this exist?? */ - return(skb); - } - offset <<= 3; /* offset is in 8-byte chunks */ - - /* - * If the queue already existed, keep restarting its timer as long - * as we still are receiving fragments. Otherwise, create a fresh - * queue entry. - */ - if (qp != NULL) - { - del_timer(&qp->timer); - qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ - qp->timer.data = (unsigned long) qp; /* pointer to queue */ - qp->timer.function = ip_expire; /* expire function */ - add_timer(&qp->timer); - } - else - { - if ((qp = ip_create(skb, iph, dev)) == NULL) - return(NULL); - } - - /* Determine the position of this fragment. */ - ihl = (iph->ihl * sizeof(unsigned long)); - end = offset + ntohs(iph->tot_len) - ihl; - - /* Point into the IP datagram 'data' part. */ - ptr = ((unsigned char *) (skb + 1)) + dev->hard_header_len + ihl; - - /* Is this the final fragment? */ - if ((flags & IP_MF) == 0) - qp->len = end; - - /* - * Find out which fragments are in front and at the back of us - * in the chain of fragments so far. We must know where to put - * this fragment, right? - */ - prev = NULL; - for(next = qp->fragments; next != NULL; next = next->next) - { - if (next->offset > offset) - break; /* bingo! */ - prev = next; - } - - /* - * We found where to put this one. - * Check for overlap with preceeding fragment, and, if needed, - * align things so that any overlaps are eliminated. - */ - if (prev != NULL && offset < prev->end) - { - i = prev->end - offset; - offset += i; /* ptr into datagram */ - ptr += i; /* ptr into fragment data */ - DPRINTF((DBG_IP, "IP: defrag: fixed low overlap %d bytes\n", i)); - } - - /* - * Look for overlap with succeeding segments. - * If we can merge fragments, do it. + qp = ip_find(iph); + + /* Is this a non-fragmented datagram? */ + offset = ntohs(iph->frag_off); + flags = offset & ~IP_OFFSET; + offset &= IP_OFFSET; + if (((flags & IP_MF) == 0) && (offset == 0)) + { + if (qp != NULL) + ip_free(qp); /* Huh? How could this exist?? */ + return(skb); + } + offset <<= 3; /* offset is in 8-byte chunks */ + + /* + * If the queue already existed, keep restarting its timer as long + * as we still are receiving fragments. Otherwise, create a fresh + * queue entry. + */ + if (qp != NULL) + { + del_timer(&qp->timer); + qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ + qp->timer.data = (unsigned long) qp; /* pointer to queue */ + qp->timer.function = ip_expire; /* expire function */ + add_timer(&qp->timer); + } + else + { + if ((qp = ip_create(skb, iph, dev)) == NULL) + return(NULL); + } + + /* Determine the position of this fragment. */ + ihl = (iph->ihl * sizeof(unsigned long)); + end = offset + ntohs(iph->tot_len) - ihl; + + /* Point into the IP datagram 'data' part. */ + ptr = ((unsigned char *) (skb + 1)) + dev->hard_header_len + ihl; + + /* Is this the final fragment? */ + if ((flags & IP_MF) == 0) + qp->len = end; + + /* + * Find out which fragments are in front and at the back of us + * in the chain of fragments so far. We must know where to put + * this fragment, right? + */ + prev = NULL; + for(next = qp->fragments; next != NULL; next = next->next) + { + if (next->offset > offset) + break; /* bingo! */ + prev = next; + } + + /* + * We found where to put this one. + * Check for overlap with preceeding fragment, and, if needed, + * align things so that any overlaps are eliminated. + */ + if (prev != NULL && offset < prev->end) + { + i = prev->end - offset; + offset += i; /* ptr into datagram */ + ptr += i; /* ptr into fragment data */ + DPRINTF((DBG_IP, "IP: defrag: fixed low overlap %d bytes\n", i)); + } + + /* + * Look for overlap with succeeding segments. + * If we can merge fragments, do it. */ - for(; next != NULL; next = tfp) - { - tfp = next->next; - if (next->offset >= end) - break; /* no overlaps at all */ - - i = end - next->offset; /* overlap is 'i' bytes */ - next->len -= i; /* so reduce size of */ - next->offset += i; /* next fragment */ - next->ptr += i; - - /* If we get a frag size of <= 0, remove it. */ - if (next->len <= 0) - { - DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n", - next, next->len)); - if (next->prev != NULL) - next->prev->next = next->next; - else - qp->fragments = next->next; - - if (tfp->next != NULL) - next->next->prev = next->prev; - - kfree_s(next, sizeof(struct ipfrag)); - } - DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i)); - } - - /* Insert this fragment in the chain of fragments. */ - tfp = NULL; - tfp = ip_frag_create(offset, end, skb, ptr); - tfp->prev = prev; - tfp->next = next; - if (prev != NULL) - prev->next = tfp; + for(; next != NULL; next = tfp) + { + tfp = next->next; + if (next->offset >= end) + break; /* no overlaps at all */ + + i = end - next->offset; /* overlap is 'i' bytes */ + next->len -= i; /* so reduce size of */ + next->offset += i; /* next fragment */ + next->ptr += i; + + /* If we get a frag size of <= 0, remove it. */ + if (next->len <= 0) + { + DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n", + next, next->len)); + if (next->prev != NULL) + next->prev->next = next->next; + else + qp->fragments = next->next; + + if (tfp->next != NULL) + next->next->prev = next->prev; + + kfree_s(next, sizeof(struct ipfrag)); + } + DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i)); + } + + /* Insert this fragment in the chain of fragments. */ + tfp = NULL; + tfp = ip_frag_create(offset, end, skb, ptr); + tfp->prev = prev; + tfp->next = next; + if (prev != NULL) + prev->next = tfp; else qp->fragments = tfp; - if (next != NULL) - next->prev = tfp; - - /* - * OK, so we inserted this new fragment into the chain. - * Check if we now have a full IP datagram which we can - * bump up to the IP layer... - */ + if (next != NULL) + next->prev = tfp; + + /* + * OK, so we inserted this new fragment into the chain. + * Check if we now have a full IP datagram which we can + * bump up to the IP layer... + */ - if (ip_done(qp)) - { - skb2 = ip_glue(qp); /* glue together the fragments */ - return(skb2); - } - return(NULL); + if (ip_done(qp)) + { + skb2 = ip_glue(qp); /* glue together the fragments */ + return(skb2); + } + return(NULL); } @@ -953,363 +1091,398 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct * ip_queue_xmit(). Note that this is recursion, and bad things will happen * if this function causes a loop... */ + void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag) { - struct iphdr *iph; - unsigned char *raw; - unsigned char *ptr; - struct sk_buff *skb2; - int left, mtu, hlen, len; - int offset; - - /* Point into the IP datagram header. */ - raw = (unsigned char *) (skb + 1); - iph = (struct iphdr *) (raw + dev->hard_header_len); - - /* Setup starting values. */ - hlen = (iph->ihl * sizeof(unsigned long)); - left = ntohs(iph->tot_len) - hlen; - hlen += dev->hard_header_len; - mtu = (dev->mtu - hlen); - ptr = (raw + hlen); - - DPRINTF((DBG_IP, "IP: Fragmentation Desired\n")); - DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s", - dev->name, dev->mtu, left, in_ntoa(iph->saddr))); - DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr))); - - /* Check for any "DF" flag. */ - if (ntohs(iph->frag_off) & IP_DF) - { - DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n")); - DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s", - dev->name, dev->mtu, left, in_ntoa(iph->saddr))); - DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr))); - - /* - * FIXME: - * We should send an ICMP warning message here! - */ - - icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); - return; - } - - /* Fragment the datagram. */ + struct iphdr *iph; + unsigned char *raw; + unsigned char *ptr; + struct sk_buff *skb2; + int left, mtu, hlen, len; + int offset; + + /* Point into the IP datagram header. */ + raw = (unsigned char *) (skb + 1); + iph = (struct iphdr *) (raw + dev->hard_header_len); + + /* Setup starting values. */ + hlen = (iph->ihl * sizeof(unsigned long)); + left = ntohs(iph->tot_len) - hlen; + hlen += dev->hard_header_len; + mtu = (dev->mtu - hlen); + ptr = (raw + hlen); + + DPRINTF((DBG_IP, "IP: Fragmentation Desired\n")); + DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s", + dev->name, dev->mtu, left, in_ntoa(iph->saddr))); + DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr))); + + /* Check for any "DF" flag. */ + if (ntohs(iph->frag_off) & IP_DF) + { + DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n")); + DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s", + dev->name, dev->mtu, left, in_ntoa(iph->saddr))); + DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr))); + + /* + * FIXME: + * We should send an ICMP warning message here! + */ + + icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); + return; + } + + /* + * If it won't fit then error it. + * NOTE: We don't send a ICMP here. Suppose the ICMP didn't fit.... + */ + + if(mtu<8) + { + return; + } + + /* Fragment the datagram. */ if (is_frag & 2) offset = (ntohs(iph->frag_off) & 0x1fff) << 3; else - offset = 0; - while(left > 0) - { - len = left; - if (len+8 > mtu) - len = (dev->mtu - hlen - 8); - if ((left - len) >= 8) - { - len /= 8; - len *= 8; - } - DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n", - len, len + hlen)); - - /* Allocate buffer. */ - if ((skb2 = alloc_skb(sizeof(struct sk_buff) + len + hlen,GFP_KERNEL)) == NULL) - { - printk("IP: frag: no memory for new fragment!\n"); - return; - } - skb2->arp = skb->arp; - skb2->free = skb->free; - skb2->len = len + hlen; - skb2->h.raw=(char *)(skb2+1); - - if (sk) - sk->wmem_alloc += skb2->mem_len; - - /* Copy the packet header into the new buffer. */ - memcpy(skb2->h.raw, raw, hlen); - - /* Copy a block of the IP datagram. */ - memcpy(skb2->h.raw + hlen, ptr, len); - left -= len; + offset = 0; + while(left > 0) + { + len = left; + if (len+8 > mtu) + len = (dev->mtu - hlen - 8); + if ((left - len) >= 8) + { + len /= 8; + len *= 8; + } + DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n", + len, len + hlen)); + + /* Allocate buffer. */ + if ((skb2 = alloc_skb(sizeof(struct sk_buff) + len + hlen,GFP_KERNEL)) == NULL) + { + printk("IP: frag: no memory for new fragment!\n"); + return; + } + skb2->arp = skb->arp; + skb2->free = skb->free; + skb2->len = len + hlen; + skb2->h.raw=(char *)(skb2+1); + + if (sk) + sk->wmem_alloc += skb2->mem_len; + + /* Copy the packet header into the new buffer. */ + memcpy(skb2->h.raw, raw, hlen); + + /* Copy a block of the IP datagram. */ + memcpy(skb2->h.raw + hlen, ptr, len); + left -= len; skb2->h.raw+=dev->hard_header_len; - /* Fill in the new header fields. */ - iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/); - iph->frag_off = htons((offset >> 3)); - /* Added AC : If we are fragmenting a fragment thats not the - last fragment then keep MF on each bit */ - if (left > 0 || (is_frag & 1)) - iph->frag_off |= htons(IP_MF); - ptr += len; - offset += len; + /* Fill in the new header fields. */ + iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/); + iph->frag_off = htons((offset >> 3)); + /* Added AC : If we are fragmenting a fragment thats not the + last fragment then keep MF on each bit */ + if (left > 0 || (is_frag & 1)) + iph->frag_off |= htons(IP_MF); + ptr += len; + offset += len; /* printk("Queue frag\n");*/ - /* Put this fragment into the sending queue. */ - ip_queue_xmit(sk, dev, skb2, 1); + /* Put this fragment into the sending queue. */ + ip_queue_xmit(sk, dev, skb2, 1); /* printk("Queued\n");*/ - } + } } #ifdef CONFIG_IP_FORWARD -/* Forward an IP datagram to its next destination. */ -static void -ip_forward(struct sk_buff *skb, struct device *dev, int is_frag) +/* + * Forward an IP datagram to its next destination. + */ + +static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag) { - struct device *dev2; - struct iphdr *iph; - struct sk_buff *skb2; - struct rtable *rt; - unsigned char *ptr; - unsigned long raddr; + struct device *dev2; + struct iphdr *iph; + struct sk_buff *skb2; + struct rtable *rt; + unsigned char *ptr; + unsigned long raddr; - /* - * Only forward packets that were fired at us when we are in promiscuous - * mode. In standard mode we rely on the driver to filter for us. - */ + /* + * Only forward packets that were fired at us when we are in promiscuous + * mode. In standard mode we rely on the driver to filter for us. + */ - if(dev->flags&IFF_PROMISC) - { - if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len)) - return; - } + if(dev->flags&IFF_PROMISC) + { + if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len)) + return; + } - /* - * According to the RFC, we must first decrease the TTL field. If - * that reaches zero, we must reply an ICMP control message telling - * that the packet's lifetime expired. - */ - iph = skb->h.iph; - iph->ttl--; - if (iph->ttl <= 0) { - DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n")); - DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr))); - DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr))); - - /* Tell the sender its packet died... */ - icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev); - return; - } - - /* Re-compute the IP header checksum. */ - ip_send_check(iph); - - /* - * OK, the packet is still valid. Fetch its destination address, - * and give it to the IP sender for further processing. - */ - rt = rt_route(iph->daddr, NULL); - if (rt == NULL) { - DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n")); - - /* Tell the sender its packet cannot be delivered... */ - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev); - return; - } + /* + * According to the RFC, we must first decrease the TTL field. If + * that reaches zero, we must reply an ICMP control message telling + * that the packet's lifetime expired. RFC791 page 30. + */ + iph = skb->h.iph; + iph->ttl--; + if (iph->ttl <= 0) + { + DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n")); + DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr))); + DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr))); + + /* Tell the sender its packet died... */ + icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev); + return; + } + /* Re-compute the IP header checksum. */ + ip_send_check(iph); - /* - * Gosh. Not only is the packet valid; we even know how to - * forward it onto its final destination. Can we say this - * is being plain lucky? - * If the router told us that there is no GW, use the dest. - * IP address itself- we seem to be connected directly... - */ - raddr = rt->rt_gateway; - if (raddr != 0) { - rt = rt_route(raddr, NULL); - if (rt == NULL) { - DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n")); + /* + * OK, the packet is still valid. Fetch its destination address, + * and give it to the IP sender for further processing. + */ + rt = rt_route(iph->daddr, NULL); + if (rt == NULL) + { + DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n")); /* Tell the sender its packet cannot be delivered... */ - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev); return; } - if (rt->rt_gateway != 0) raddr = rt->rt_gateway; - } else raddr = iph->daddr; - dev2 = rt->rt_dev; - if (dev == dev2) - return; - /* - * We now allocate a new buffer, and copy the datagram into it. - * If the indicated interface is up and running, kick it. - */ - DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr))); - DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n", + /* + * Gosh. Not only is the packet valid; we even know how to + * forward it onto its final destination. Can we say this + * is being plain lucky? + * If the router told us that there is no GW, use the dest. + * IP address itself- we seem to be connected directly... + */ + raddr = rt->rt_gateway; + if (raddr != 0) + { + rt = rt_route(raddr, NULL); + if (rt == NULL) + { + DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n")); + + /* Tell the sender its packet cannot be delivered... */ + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev); + return; + } + if (rt->rt_gateway != 0) + raddr = rt->rt_gateway; + } + else + raddr = iph->daddr; + dev2 = rt->rt_dev; + + /* + * Never forward out on the same interface, its not allowed, its often not pretty either (except for on + * source routing) + */ + if (dev == dev2) + return; + /* + * We now allocate a new buffer, and copy the datagram into it. + * If the indicated interface is up and running, kick it. + */ + DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr))); + DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n", in_ntoa(raddr), dev2->name, skb->len)); - if (dev2->flags & IFF_UP) { - skb2 = (struct sk_buff *) alloc_skb(sizeof(struct sk_buff) + + if (dev2->flags & IFF_UP) + { + skb2 = (struct sk_buff *) alloc_skb(sizeof(struct sk_buff) + dev2->hard_header_len + skb->len, GFP_ATOMIC); - if (skb2 == NULL) { - printk("\nIP: No memory available for IP forward\n"); - return; - } - ptr = (unsigned char *)(skb2 + 1); - skb2->sk = NULL; - skb2->free = 1; - skb2->len = skb->len + dev2->hard_header_len; - skb2->mem_addr = skb2; - skb2->mem_len = sizeof(struct sk_buff) + skb2->len; - skb2->next = NULL; - skb2->h.raw = ptr; - - /* Copy the packet data into the new buffer. */ - memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len); + if (skb2 == NULL) + { + printk("\nIP: No memory available for IP forward\n"); + return; + } + ptr = (unsigned char *)(skb2 + 1); + skb2->sk = NULL; + skb2->free = 1; + skb2->len = skb->len + dev2->hard_header_len; + skb2->next = NULL; + skb2->h.raw = ptr; + + /* Copy the packet data into the new buffer. */ + memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len); - /* Now build the MAC header. */ - (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr); + /* Now build the MAC header. */ + (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr); - if(skb2->len > dev2->mtu) - { - ip_fragment(NULL,skb2,dev2, is_frag); - kfree_skb(skb2,FREE_WRITE); + if(skb2->len > dev2->mtu) + { + ip_fragment(NULL,skb2,dev2, is_frag); + kfree_skb(skb2,FREE_WRITE); + } + else + dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL); } - else - dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL); - } } #endif -/* This function receives all incoming IP datagrams. */ -int -ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) + +/* + * This function receives all incoming IP datagrams. + */ + +int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { - struct iphdr *iph = skb->h.iph; - unsigned char hash; - unsigned char flag = 0; - unsigned char opts_p = 0; /* Set iff the packet has options. */ - struct inet_protocol *ipprot; - static struct options opt; /* since we don't use these yet, and they - take up stack space. */ - int brd; - int is_frag=0; - - DPRINTF((DBG_IP, "<<\n")); - - /* Is the datagram acceptable? */ - if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) { - DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n")); - DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr))); - DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr))); - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - return(0); - } - - if (iph->ihl != 5) { /* Fast path for the typical optionless IP packet. */ - ip_print(iph); /* Bogus, only for debugging. */ - memset((char *) &opt, 0, sizeof(opt)); - if (do_options(iph, &opt) != 0) - return 0; - opts_p = 1; - } - - if (iph->frag_off & 0x0020) - is_frag|=1; - if (ntohs(iph->frag_off) & 0x1fff) - is_frag|=2; - - /* Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. */ - if ((brd = chk_addr(iph->daddr)) == 0) { + struct iphdr *iph = skb->h.iph; + unsigned char hash; + unsigned char flag = 0; + unsigned char opts_p = 0; /* Set iff the packet has options. */ + struct inet_protocol *ipprot; + struct options *opt=NULL; + + int brd; + int is_frag=0; + + DPRINTF((DBG_IP, "<<\n")); + + /* Is the datagram acceptable? */ + if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) + { + DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n")); + DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr))); + DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr))); + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + return(0); + } + + if (iph->ihl != 5) + { /* Fast path for the typical optionless IP packet. */ +#ifdef IP_DEBUG + ip_print(iph); /* Bogus, only for debugging. */ +#endif + if (do_options(iph, &opt,dev) != 0) + { + if(opt) + kfree(opt); + return 0; + } +/* skb->ip_options=opt_ptr;*/ + kfree(opt); + opt = NULL; + opts_p = 0/*1*/; + } + + if (iph->frag_off & 0x0020) + is_frag|=1; + if (ntohs(iph->frag_off) & 0x1fff) + is_frag|=2; + + /* Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. */ + if ((brd = chk_addr(iph->daddr)) == 0) + { #ifdef CONFIG_IP_FORWARD - ip_forward(skb, dev, is_frag); + ip_forward(skb, dev, is_frag); #else - printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n", + printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n", iph->saddr,iph->daddr); #endif - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - return(0); - } + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + return(0); + } /* * Reassemble IP fragments. */ - if(is_frag) - { + if(is_frag) + { #ifdef CONFIG_IP_DEFRAG - skb=ip_defrag(iph,skb,dev); - if(skb==NULL) - { - return 0; - } - iph=skb->h.iph; + skb=ip_defrag(iph,skb,dev); + if(skb==NULL) + { + return 0; + } + iph=skb->h.iph; #else - printk("\nIP: *** datagram fragmentation not yet implemented ***\n"); - printk(" SRC = %s ", in_ntoa(iph->saddr)); - printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr)); - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - return(0); + printk("\nIP: *** datagram fragmentation not yet implemented ***\n"); + printk(" SRC = %s ", in_ntoa(iph->saddr)); + printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr)); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + return(0); #endif - } + } + /* Point into the IP datagram, just past the header. */ + skb->ip_hdr = iph; + skb->h.raw += iph->ihl*4; + hash = iph->protocol & (MAX_INET_PROTOS -1); + + /* Find someone to deliver it too */ + + for (ipprot = (struct inet_protocol *)inet_protos[hash]; + ipprot != NULL; + ipprot=(struct inet_protocol *)ipprot->next) + { + struct sk_buff *skb2; - if(brd==IS_INVBCAST) - { -/* printk("Invalid broadcast address from %x [target %x] (Probably they have a wrong netmask)\n", - iph->saddr,iph->daddr);*/ - skb->sk=NULL; - kfree_skb(skb,FREE_WRITE); - return(0); - } - - /* Point into the IP datagram, just past the header. */ - skb->h.raw += iph->ihl*4; - hash = iph->protocol & (MAX_INET_PROTOS -1); - for (ipprot = (struct inet_protocol *)inet_protos[hash]; - ipprot != NULL; - ipprot=(struct inet_protocol *)ipprot->next) - { - struct sk_buff *skb2; - - if (ipprot->protocol != iph->protocol) continue; - DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot)); - print_ipprot(ipprot); + if (ipprot->protocol != iph->protocol) + continue; + DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot)); + print_ipprot(ipprot); /* * See if we need to make a copy of it. This will * only be set if more than one protocol wants it. * and then not for the last one. */ - if (ipprot->copy) { - skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC); - if (skb2 == NULL) - continue; - memcpy(skb2, skb, skb->mem_len); - skb2->mem_addr = skb2; - skb2->h.raw = (unsigned char *)( + if (ipprot->copy) + { + skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC); + if (skb2 == NULL) + continue; + memcpy(skb2, skb, skb->mem_len); + skb2->mem_addr = skb2; + skb2->h.raw = (unsigned char *)( (unsigned long)skb2 + (unsigned long) skb->h.raw - (unsigned long)skb); - skb2->free=1; - } else { - skb2 = skb; - } - flag = 1; + skb2->free=1; + } + else + { + skb2 = skb; + } + flag = 1; - /* - * Pass on the datagram to each protocol that wants it, - * based on the datagram protocol. We should really - * check the protocol handler's return values here... - */ - ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr, + /* + * Pass on the datagram to each protocol that wants it, + * based on the datagram protocol. We should really + * check the protocol handler's return values here... + */ + ipprot->handler(skb2, dev, opts_p ? opt : 0, iph->daddr, (ntohs(iph->tot_len) - (iph->ihl * 4)), iph->saddr, 0, ipprot); - } + } /* * All protocols checked. @@ -1317,14 +1490,15 @@ ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * causes (proven, grin) ARP storms and a leakage of memory (i.e. all * ICMP reply messages get queued up for transmission...) */ - if (!flag) { - if (brd != IS_BROADCAST) - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - } + if (!flag) + { + if (brd != IS_BROADCAST) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + } - return(0); + return(0); } @@ -1335,136 +1509,159 @@ ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * This routine also needs to put in the total length, and * compute the checksum. */ -void -ip_queue_xmit(struct sock *sk, struct device *dev, +void ip_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb, int free) { - struct iphdr *iph; - unsigned char *ptr; - - if (sk == NULL) free = 1; - if (dev == NULL) { - printk("IP: ip_queue_xmit dev = NULL\n"); - return; - } - IS_SKB(skb); - skb->free = free; - skb->dev = dev; - skb->when = jiffies; + struct iphdr *iph; + unsigned char *ptr; + + if (sk == NULL) + free = 1; + if (dev == NULL) + { + printk("IP: ip_queue_xmit dev = NULL\n"); + return; + } + IS_SKB(skb); + skb->free = free; + skb->dev = dev; + skb->when = jiffies; - DPRINTF((DBG_IP, ">>\n")); - ptr = (unsigned char *)(skb + 1); - ptr += dev->hard_header_len; - iph = (struct iphdr *)ptr; - iph->tot_len = ntohs(skb->len-dev->hard_header_len); - - if(skb->len > dev->mtu) - { -/* printk("Fragment!\n");*/ - ip_fragment(sk,skb,dev,0); - IS_SKB(skb); - kfree_skb(skb,FREE_WRITE); - return; - } + DPRINTF((DBG_IP, ">>\n")); + ptr = (unsigned char *)(skb + 1); + ptr += dev->hard_header_len; + iph = (struct iphdr *)ptr; + iph->tot_len = ntohs(skb->len-dev->hard_header_len); + + if(skb->len > dev->mtu) + { +/* printk("Fragment!\n");*/ + ip_fragment(sk,skb,dev,0); + IS_SKB(skb); + kfree_skb(skb,FREE_WRITE); + return; + } - ip_send_check(iph); - ip_print(iph); - skb->next = NULL; - - /* See if this is the one trashing our queue. Ross? */ - skb->magic = 1; - if (!free) { - skb->link3 = NULL; - sk->packets_out++; - cli(); - if (sk->send_head == NULL) { - sk->send_tail = skb; - sk->send_head = skb; - } else { - /* See if we've got a problem. */ - if (sk->send_tail == NULL) { - printk("IP: ***bug sk->send_tail == NULL != sk->send_head\n"); - sort_send(sk); - } else { - sk->send_tail->link3 = skb; + ip_send_check(iph); +#ifdef IP_DEBUG + ip_print(iph); +#endif + skb->next = NULL; + + /* See if this is the one trashing our queue. Ross? */ + skb->magic = 1; + if (!free) + { + skb->link3 = NULL; + sk->packets_out++; + cli(); + if (sk->send_head == NULL) + { sk->send_tail = skb; + sk->send_head = skb; + } + else + { + /* See if we've got a problem. */ + if (sk->send_tail == NULL) + { + printk("IP: ***bug sk->send_tail == NULL != sk->send_head\n"); + sort_send(sk); + } + else + { + sk->send_tail->link3 = skb; + sk->send_tail = skb; + } } - } - sti(); - reset_timer(sk, TIME_WRITE, + sti(); + reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); - } else { - skb->sk = sk; - } - - /* If the indicated interface is up and running, kick it. */ - if (dev->flags & IFF_UP) { - if (sk != NULL) { - dev->queue_xmit(skb, dev, sk->priority); } - else { - dev->queue_xmit(skb, dev, SOPRI_NORMAL); + else + { + skb->sk = sk; + } + + /* If the indicated interface is up and running, kick it. */ + if (dev->flags & IFF_UP) + { + if (sk != NULL) + { + dev->queue_xmit(skb, dev, sk->priority); + } + else + { + dev->queue_xmit(skb, dev, SOPRI_NORMAL); + } + } + else + { + if (free) kfree_skb(skb, FREE_WRITE); } - } else { - if (free) kfree_skb(skb, FREE_WRITE); - } } -void -ip_retransmit(struct sock *sk, int all) +void ip_retransmit(struct sock *sk, int all) { - struct sk_buff * skb; - struct proto *prot; - struct device *dev; - - prot = sk->prot; - skb = sk->send_head; - while (skb != NULL) { - dev = skb->dev; - /* I know this can't happen but as it does.. */ - if(dev==NULL) - { - printk("ip_retransmit: NULL device bug!\n"); - goto oops; - } + struct sk_buff * skb; + struct proto *prot; + struct device *dev; - IS_SKB(skb); + prot = sk->prot; + skb = sk->send_head; + while (skb != NULL) + { + dev = skb->dev; + /* I know this can't happen but as it does.. */ + if(dev==NULL) + { + printk("ip_retransmit: NULL device bug!\n"); + goto oops; + } - /* - * The rebuild_header function sees if the ARP is done. - * If not it sends a new ARP request, and if so it builds - * the header. - */ - cli(); /* We might get interrupted by an arp reply here and fill - the frame in twice. Because of the technique used this - would be a little sad */ - if (!skb->arp) { - if (dev->rebuild_header(skb+1, dev)) { - sti(); /* Failed to rebuild - next */ - if (!all) break; - skb = (struct sk_buff *)skb->link3; - continue; + IS_SKB(skb); + + /* + * The rebuild_header function sees if the ARP is done. + * If not it sends a new ARP request, and if so it builds + * the header. + */ + cli(); /* We might get interrupted by an arp reply here and fill + the frame in twice. Because of the technique used this + would be a little sad */ + if (!skb->arp) + { + if (dev->rebuild_header(skb+1, dev)) + { + sti(); /* Failed to rebuild - next */ + if (!all) + break; + skb = (struct sk_buff *)skb->link3; + continue; + } + } + skb->arp = 1; + sti(); + skb->when = jiffies; + + /* If the interface is (still) up and running, kick it. */ + if (dev->flags & IFF_UP) + { + if (sk) + dev->queue_xmit(skb, dev, sk->priority); } - } - skb->arp = 1; - sti(); - skb->when = jiffies; - - /* If the interface is (still) up and running, kick it. */ - if (dev->flags & IFF_UP) { - if (sk) dev->queue_xmit(skb, dev, sk->priority); - /* else dev->queue_xmit(skb, dev, SOPRI_NORMAL ); CANNOT HAVE SK=NULL HERE */ - } -oops: sk->retransmits++; - sk->prot->retransmits ++; - if (!all) break; +oops: sk->retransmits++; + sk->prot->retransmits ++; + if (!all) + break; - /* This should cut it off before we send too many packets. */ - if (sk->retransmits > sk->cong_window) break; - skb = (struct sk_buff *)skb->link3; - } + /* This should cut it off before we send too many packets. */ + if (sk->retransmits > sk->cong_window) + break; + skb = (struct sk_buff *)skb->link3; + } /* * Increase the RTT time every time we retransmit. @@ -1472,11 +1669,14 @@ oops: sk->retransmits++; * get through again. Once we get through, the rtt will settle * back down reasonably quickly. */ - sk->backoff++; - reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); -} + sk->backoff++; + reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); +} + +/* + * Backoff function - the subject of much research + */ -/* Backoff function - the subject of much research */ int backoff(int n) { /* Use binary exponential up to retry #4, and quadratic after that @@ -1504,3 +1704,73 @@ int backoff(int n) } } + +/* + * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on + * an IP socket. + */ + +int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) +{ + int val,err; + + if (optval == NULL) + return(-EINVAL); + + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + + if(level!=SOL_IP) + return -EOPNOTSUPP; + + switch(optname) + { + case IP_TOS: + if(val<0||val>255) + return -EINVAL; + sk->ip_tos=val; + return 0; + case IP_TTL: + if(val<1||val<255) + return -EINVAL; + sk->ip_ttl=val; + return 0; + /* IP_OPTIONS and friends go here eventually */ + default: + return(-ENOPROTOOPT); + } +} + +int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) +{ + int val,err; + + if(level!=SOL_IP) + return -EOPNOTSUPP; + + switch(optname) + { + case IP_TOS: + val=sk->ip_tos; + break; + case IP_TTL: + val=sk->ip_ttl; + break; + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); + + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); + + return(0); +} diff --git a/net/inet/ip.h b/net/inet/ip.h index 3c01cc8..95daf2c 100644 --- a/net/inet/ip.h +++ b/net/inet/ip.h @@ -21,8 +21,8 @@ #include <linux/ip.h> +#include "sockinet.h" -#include "sock.h" /* struct sock */ /* IP flags. */ #define IP_CE 0x8000 /* Flag: "Congestion" */ @@ -69,7 +69,8 @@ extern int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, struct device **dev, int type, - struct options *opt, int len); + struct options *opt, int len, + int tos,int ttl); extern unsigned short ip_compute_csum(unsigned char * buff, int len); extern int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt); @@ -77,5 +78,6 @@ extern void ip_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb, int free); extern void ip_retransmit(struct sock *sk, int all); - +extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen); +extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen); #endif /* _IP_H */ diff --git a/net/inet/loopback.c b/net/inet/loopback.c index 66203eb..949e41a 100644 --- a/net/inet/loopback.c +++ b/net/inet/loopback.c @@ -5,71 +5,77 @@ * * Pseudo-driver for the loopback interface. * - * Version: @(#)loopback.c 1.0.4b 08/16/93 + * Version: @(#)loopback.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Donald Becker, <becker@super.org> * + * This file should be in drivers/net, but our glorious leader + * has put it here, and who are we to argue with the Linus 8-) + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/io.h> #include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/fs.h> +#include <linux/tty.h> #include <linux/types.h> +#include <linux/ptrace.h> #include <linux/string.h> #include <linux/socket.h> #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/in.h> #include <linux/if_ether.h> /* For the statistics structure. */ - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/io.h> - #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "eth.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "arp.h" -static int -loopback_xmit(struct sk_buff *skb, struct device *dev) +static int loopback_xmit(struct sk_buff *skb, struct device *dev) { - struct enet_statistics *stats = (struct enet_statistics *)dev->priv; - int done; + struct enet_statistics *stats = (struct enet_statistics *)dev->priv; + int done; - DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb)); - if (skb == NULL || dev == NULL) return(0); + DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb)); + if (skb == NULL || dev == NULL) + return(0); - cli(); - if (dev->tbusy != 0) { - sti(); - stats->tx_errors++; - return(1); - } - dev->tbusy = 1; - sti(); + cli(); + if (dev->tbusy != 0) + { + sti(); + stats->tx_errors++; + return(1); + } + dev->tbusy = 1; + sti(); - done = dev_rint((unsigned char *)(skb+1), skb->len, 0, dev); - if (skb->free) kfree_skb(skb, FREE_WRITE); + done = dev_rint((unsigned char *)(skb+1), skb->len, 0, dev); + if (skb->free) + kfree_skb(skb, FREE_WRITE); - while (done != 1) { - done = dev_rint(NULL, 0, 0, dev); - } - stats->tx_packets++; + while (done != 1) + { + done = dev_rint(NULL, 0, 0, dev); + } + stats->tx_packets++; - dev->tbusy = 0; + dev->tbusy = 0; #if 1 __asm__("cmpl $0,_intr_count\n\t" @@ -86,52 +92,50 @@ loopback_xmit(struct sk_buff *skb, struct device *dev) : "ax", "dx", "cx"); #endif - return(0); + return(0); } -static struct enet_statistics * -get_stats(struct device *dev) +static struct enet_statistics *get_stats(struct device *dev) { return (struct enet_statistics *)dev->priv; } /* Initialize the rest of the LOOPBACK device. */ -int -loopback_init(struct device *dev) +int loopback_init(struct device *dev) { - dev->mtu = 2000; /* MTU */ - dev->tbusy = 0; - dev->hard_start_xmit = loopback_xmit; - dev->open = NULL; + dev->mtu = 2000; /* MTU */ + dev->tbusy = 0; + dev->hard_start_xmit = loopback_xmit; + dev->open = NULL; #if 1 - dev->hard_header = eth_header; - dev->add_arp = NULL; - dev->hard_header_len = ETH_HLEN; /* 14 */ - dev->addr_len = ETH_ALEN; /* 6 */ - dev->type = ARPHRD_ETHER; /* 0x0001 */ - dev->type_trans = eth_type_trans; - dev->rebuild_header = eth_rebuild_header; + dev->hard_header = eth_header; + dev->add_arp = NULL; + dev->hard_header_len = ETH_HLEN; /* 14 */ + dev->addr_len = ETH_ALEN; /* 6 */ + dev->type = ARPHRD_ETHER; /* 0x0001 */ + dev->type_trans = eth_type_trans; + dev->rebuild_header = eth_rebuild_header; #else - dev->hard_header_length = 0; - dev->add_arp = NULL; - dev->addr_len = 0; - dev->type = 0; /* loopback_type (0) */ - dev->hard_header = NULL; - dev->type_trans = NULL; - dev->rebuild_header = NULL; + dev->hard_header_length = 0; + dev->add_arp = NULL; + dev->addr_len = 0; + dev->type = 0; /* loopback_type (0) */ + dev->hard_header = NULL; + dev->type_trans = NULL; + dev->rebuild_header = NULL; #endif - dev->queue_xmit = dev_queue_xmit; + dev->queue_xmit = dev_queue_xmit; /* New-style flags. */ - dev->flags = IFF_LOOPBACK; - dev->family = AF_INET; - dev->pa_addr = in_aton("127.0.0.1"); - dev->pa_brdaddr = in_aton("127.255.255.255"); - dev->pa_mask = in_aton("255.0.0.0"); - dev->pa_alen = sizeof(unsigned long); - dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL); - memset(dev->priv, 0, sizeof(struct enet_statistics)); - dev->get_stats = get_stats; + dev->flags = IFF_LOOPBACK; + dev->family = AF_INET; + dev->pa_addr = in_aton("127.0.0.1"); + dev->pa_brdaddr = in_aton("127.255.255.255"); + dev->pa_mask = in_aton("255.0.0.0"); + dev->pa_alen = sizeof(unsigned long); + dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct enet_statistics)); + dev->get_stats = get_stats; - return(0); + return(0); }; diff --git a/net/inet/packet.c b/net/inet/packet.c index c0585a6..48b2184 100644 --- a/net/inet/packet.c +++ b/net/inet/packet.c @@ -5,7 +5,7 @@ * * PACKET - implements raw packet sockets. * - * Version: @(#)packet.c 1.0.6 05/25/93 + * Version: @(#)packet.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -18,6 +18,7 @@ * added. Also fixed the peek/read crash * from all old Linux datagram code. * Alan Cox : Uses the improved datagram code. + * Alan Cox : Clean up for final release. * * * This program is free software; you can redistribute it and/or @@ -31,12 +32,12 @@ #include <linux/socket.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include <linux/errno.h> #include <linux/timer.h> #include <asm/system.h> @@ -44,138 +45,152 @@ #include "udp.h" #include "raw.h" +/* + * I'm sure there should be one of these, not one every file. + */ -static unsigned long -min(unsigned long a, unsigned long b) +static unsigned long min(unsigned long a, unsigned long b) { - if (a < b) return(a); - return(b); + if (a < b) + return(a); + return(b); } -/* This should be the easiest of all, all we do is copy it into a buffer. */ -int -packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +/* + * This should be the easiest of all, all we do is copy it into a buffer. + */ + +int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { - struct sock *sk; - - sk = (struct sock *) pt->data; - skb->dev = dev; - skb->len += dev->hard_header_len; - - skb->sk = sk; - - /* Charge it too the socket. */ - if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) { - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return(0); - } - sk->rmem_alloc += skb->mem_len; - skb_queue_tail(&sk->rqueue,skb); - wake_up(sk->sleep); - release_sock(sk); - return(0); + struct sock *sk; + + sk = (struct sock *) pt->data; + skb->dev = dev; + skb->len += dev->hard_header_len; + + skb->sk = sk; + + /* Charge it too the socket. */ + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return(0); + } + sk->rmem_alloc += skb->mem_len; + skb_queue_tail(&sk->rqueue,skb); + release_sock(sk); + sk->data_ready(sk,skb->len); + return(0); } -/* This will do terrible things if len + ipheader + devheader > dev->mtu */ -static int -packet_sendto(struct sock *sk, unsigned char *from, int len, +/* + * This will do terrible things if len + ipheader + devheader > dev->mtu + * Since only root can use these thats okish... + */ + +static int packet_sendto(struct sock *sk, unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { - struct sk_buff *skb; - struct device *dev; - struct sockaddr saddr; - int err; - - /* Check the flags. */ - if (flags) return(-EINVAL); - if (len < 0) return(-EINVAL); - - /* Get and verify the address. */ - if (usin) { - if (addr_len < sizeof(saddr)) return(-EINVAL); - err=verify_area(VERIFY_READ, usin, sizeof(saddr)); - if(err) - return err; - memcpy_fromfs(&saddr, usin, sizeof(saddr)); - } else - return(-EINVAL); + struct sk_buff *skb; + struct device *dev; + struct sockaddr saddr; + int err; + + /* Check the flags. */ + if (flags) + return(-EINVAL); + if (len < 0) + return(-EINVAL); + + /* Get and verify the address. */ + if (usin) + { + if (addr_len < sizeof(saddr)) + return(-EINVAL); + err=verify_area(VERIFY_READ, usin, sizeof(saddr)); + if(err) + return err; + memcpy_fromfs(&saddr, usin, sizeof(saddr)); + } + else + return(-EINVAL); - err=verify_area(VERIFY_READ,from,len); - if(err) - return(err); -/* Find the device first to size check it */ - - saddr.sa_data[13] = 0; - dev = dev_get(saddr.sa_data); - if (dev == NULL) { - return(-ENXIO); - } - if(len>dev->mtu) - return -EMSGSIZE; - -/* Now allocate the buffer, knowing 4K pagelimits wont break this line */ - skb = (struct sk_buff *) sk->prot->wmalloc(sk, len+sizeof(*skb), 0, GFP_KERNEL); - - /* This shouldn't happen, but it could. */ - if (skb == NULL) { - DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n")); - return(-ENOMEM); - } - /* Fill it in */ - skb->mem_addr = skb; - skb->mem_len = len + sizeof(*skb); - skb->sk = sk; - skb->free = 1; - memcpy_fromfs (skb+1, from, len); - skb->len = len; - skb->next = NULL; - if (dev->flags & IFF_UP) dev->queue_xmit(skb, dev, sk->priority); - else kfree_skb(skb, FREE_WRITE); - return(len); + err=verify_area(VERIFY_READ,from,len); + if(err) + return(err); + /* Find the device first to size check it */ + + saddr.sa_data[13] = 0; + dev = dev_get(saddr.sa_data); + if (dev == NULL) + { + return(-ENXIO); + } + if(len>dev->mtu) + return -EMSGSIZE; + + /* Now allocate the buffer, knowing 4K pagelimits wont break this line */ + skb = (struct sk_buff *) sk->prot->wmalloc(sk, len+sizeof(*skb), 0, GFP_KERNEL); + + /* This shouldn't happen, but it could. */ + if (skb == NULL) + { + DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n")); + return(-ENOMEM); + } + /* Fill it in */ + skb->sk = sk; + skb->free = 1; + memcpy_fromfs (skb+1, from, len); + skb->len = len; + skb->next = NULL; + if (dev->flags & IFF_UP) + dev->queue_xmit(skb, dev, sk->priority); + else + kfree_skb(skb, FREE_WRITE); + return(len); } -static int -packet_write(struct sock *sk, unsigned char *buff, +static int packet_write(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { - return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0)); + return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0)); } -static void -packet_close(struct sock *sk, int timeout) +static void packet_close(struct sock *sk, int timeout) { - sk->inuse = 1; - sk->state = TCP_CLOSE; - dev_remove_pack((struct packet_type *)sk->pair); - kfree_s((void *)sk->pair, sizeof(struct packet_type)); - sk->pair = NULL; - release_sock(sk); + sk->inuse = 1; + sk->state = TCP_CLOSE; + dev_remove_pack((struct packet_type *)sk->pair); + kfree_s((void *)sk->pair, sizeof(struct packet_type)); + sk->pair = NULL; + release_sock(sk); } -static int -packet_init(struct sock *sk) +static int packet_init(struct sock *sk) { - struct packet_type *p; + struct packet_type *p; - p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) return(-ENOMEM); + p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return(-ENOMEM); - p->func = packet_rcv; - p->type = sk->num; - p->data = (void *)sk; - dev_add_pack(p); - - /* We need to remember this somewhere. */ - sk->pair = (struct sock *)p; + p->func = packet_rcv; + p->type = sk->num; + p->data = (void *)sk; + dev_add_pack(p); + + /* We need to remember this somewhere. */ + sk->pair = (struct sock *)p; - return(0); + return(0); } @@ -183,89 +198,95 @@ packet_init(struct sock *sk) * This should be easy, if there is something there * we return it, otherwise we block. */ -int -packet_recvfrom(struct sock *sk, unsigned char *to, int len, +int packet_recvfrom(struct sock *sk, unsigned char *to, int len, int noblock, unsigned flags, struct sockaddr_in *sin, int *addr_len) { - int copied=0; - struct sk_buff *skb; - struct sockaddr *saddr; - int err; - - saddr = (struct sockaddr *)sin; - if (len == 0) return(0); - if (len < 0) return(-EINVAL); - - if (sk->shutdown & RCV_SHUTDOWN) return(0); - if (addr_len) { - err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); - if(err) - return err; - put_fs_long(sizeof(*saddr), addr_len); - } + int copied=0; + struct sk_buff *skb; + struct sockaddr *saddr; + int err; + + saddr = (struct sockaddr *)sin; + if (len == 0) + return(0); + if (len < 0) + return(-EINVAL); + + if (sk->shutdown & RCV_SHUTDOWN) + return(0); + if (addr_len) + { + err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); + if(err) + return err; + put_fs_long(sizeof(*saddr), addr_len); + } - err=verify_area(VERIFY_WRITE,to,len); - if(err) - return err; - skb=skb_recv_datagram(sk,flags,noblock,&err); - if(skb==NULL) - return err; - copied = min(len, skb->len); - - memcpy_tofs(to, skb+1, copied); /* Don't use skb_copy_datagram here: We can't get frag chains */ - - /* Copy the address. */ - if (saddr) { - struct sockaddr addr; - - addr.sa_family = skb->dev->type; - memcpy(addr.sa_data,skb->dev->name, 14); - verify_area(VERIFY_WRITE, saddr, sizeof(*saddr)); - memcpy_tofs(saddr, &addr, sizeof(*saddr)); - } - - skb_free_datagram(skb); /* Its either been used up, or its a peek_copy anyway */ - - release_sock(sk); - return(copied); + err=verify_area(VERIFY_WRITE,to,len); + if(err) + return err; + skb=skb_recv_datagram(sk,flags,noblock,&err); + if(skb==NULL) + return err; + copied = min(len, skb->len); + + memcpy_tofs(to, skb+1, copied); /* Don't use skb_copy_datagram here: We can't get frag chains */ + + /* Copy the address. */ + if (saddr) + { + struct sockaddr addr; + + addr.sa_family = skb->dev->type; + memcpy(addr.sa_data,skb->dev->name, 14); + verify_area(VERIFY_WRITE, saddr, sizeof(*saddr)); + memcpy_tofs(saddr, &addr, sizeof(*saddr)); + } + + skb_free_datagram(skb); /* Its either been used up, or its a peek_copy anyway */ + + release_sock(sk); + return(copied); } -int -packet_read(struct sock *sk, unsigned char *buff, +int packet_read(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { - return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); + return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); } -struct proto packet_prot = { - sock_wmalloc, - sock_rmalloc, - sock_wfree, - sock_rfree, - sock_rspace, - sock_wspace, - packet_close, - packet_read, - packet_write, - packet_sendto, - packet_recvfrom, - ip_build_header, - udp_connect, - NULL, - ip_queue_xmit, - ip_retransmit, - NULL, - NULL, - NULL, - datagram_select, - NULL, - packet_init, - NULL, - 128, - 0, - {NULL,}, - "PACKET" +struct proto packet_prot = +{ + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + packet_close, + packet_read, + packet_write, + packet_sendto, + packet_recvfrom, + ip_build_header, + udp_connect, + NULL, + ip_queue_xmit, + ip_retransmit, + NULL, + NULL, + NULL, + datagram_select, + NULL, + packet_init, + NULL, + NULL, /* No set/get socket options */ + NULL, + 128, + 0, + {NULL,}, + "PACKET" }; diff --git a/net/inet/proc.c b/net/inet/proc.c index d5bc16e..04ed9e8 100644 --- a/net/inet/proc.c +++ b/net/inet/proc.c @@ -7,7 +7,7 @@ * PROC file system. It is mainly used for debugging and * statistics. * - * Version: @(#)proc.c 1.0.5 05/27/93 + * Version: @(#)proc.c 1.28 20/12/93 * * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de> @@ -18,10 +18,7 @@ * using hint flag for the netinfo. * Pauline Middelink : Pidentd support * Alan Cox : Make /proc safer. - * - * To Do: - * Put the creating userid in the proc/net/... files. This will - * allow us to write an RFC931 daemon for Linux + * Alan Cox : Final clean up. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,13 +34,13 @@ #include <linux/in.h> #include <linux/param.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "udp.h" -#include "skbuff.h" -#include "sock.h" +#include "socket/skbuff.h" +#include "sockinet.h" #include "raw.h" /* @@ -53,81 +50,83 @@ * As in get_unix_netinfo, the buffer might be too small. If this * happens, get__netinfo returns only part of the available infos. */ -static int -get__netinfo(struct proto *pro, char *buffer, int format) +static int get__netinfo(struct proto *pro, char *buffer, int format) { - struct sock **s_array; - struct sock *sp; - char *pos=buffer; - int i; - int timer_active; - unsigned long dest, src; - unsigned short destp, srcp; - - s_array = pro->sock_array; - pos+=sprintf(pos, "sl local_address rem_address st tx_queue rx_queue tr tm->when uid\n"); + struct sock **s_array; + struct sock *sp; + char *pos=buffer; + int i; + int timer_active; + unsigned long dest, src; + unsigned short destp, srcp; + + s_array = pro->sock_array; + pos+=sprintf(pos, "sl local_address rem_address st tx_queue rx_queue tr tm->when uid\n"); /* * This was very pretty but didn't work when a socket is destroyed at the wrong moment * (eg a syn recv socket getting a reset), or a memory timer destroy. Instead of playing * with timers we just concede defeat and cli(). */ - for(i = 0; i < SOCK_ARRAY_SIZE; i++) { - cli(); - sp = s_array[i]; - while(sp != NULL) { - dest = sp->daddr; - src = sp->saddr; - destp = sp->dummy_th.dest; - srcp = sp->dummy_th.source; + for(i = 0; i < SOCK_ARRAY_SIZE; i++) + { + cli(); + sp = s_array[i]; + while(sp != NULL) + { + dest = sp->daddr; + src = sp->saddr; + destp = sp->dummy_th.dest; + srcp = sp->dummy_th.source; - /* Since we are Little Endian we need to swap the bytes :-( */ - destp = ntohs(destp); - srcp = ntohs(srcp); - timer_active = del_timer(&sp->timer); - if (!timer_active) - sp->timer.expires = 0; - pos+=sprintf(pos, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n", - i, src, srcp, dest, destp, sp->state, - format==0?sp->send_seq-sp->rcv_ack_seq:sp->rmem_alloc, - format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc, - timer_active, sp->timer.expires, (unsigned) sp->retransmits, - SOCK_INODE(sp->socket)->i_uid); - if (timer_active) - add_timer(&sp->timer); - /* Is place in buffer too rare? then abort. */ - if (pos > buffer+PAGE_SIZE-80) { - printk("oops, too many %s sockets for netinfo.\n", + /* Since we are Little Endian we need to swap the bytes :-( */ + destp = ntohs(destp); + srcp = ntohs(srcp); + timer_active = del_timer(&sp->timer); + if (!timer_active) + sp->timer.expires = 0; + pos+=sprintf(pos, "%2d: %08lX:%04X %08lX:%04X %02X %08lX:%08lX %02X:%08lX %08X %d\n", + i, src, srcp, dest, destp, sp->state, + format==0?sp->send_seq-sp->rcv_ack_seq:sp->rmem_alloc, + format==0?sp->acked_seq-sp->copied_seq:sp->wmem_alloc, + timer_active, sp->timer.expires, (unsigned) sp->retransmits, + SOCK_INODE(sp->socket)->i_uid); + if (timer_active) + add_timer(&sp->timer); + /* Is place in buffer too rare? then abort. */ + if (pos > buffer+PAGE_SIZE-80) + { + printk("oops, too many %s sockets for netinfo.\n", pro->name); - return(strlen(buffer)); - } + return(strlen(buffer)); + } - /* - * All sockets with (port mod SOCK_ARRAY_SIZE) = i - * are kept in sock_array[i], so we must follow the - * 'next' link to get them all. - */ - sp = sp->next; - } - sti(); /* We only turn interrupts back on for a moment, but because the interrupt queues anything built up + /* + * All sockets with (port mod SOCK_ARRAY_SIZE) = i + * are kept in sock_array[i], so we must follow the + * 'next' link to get them all. + */ + sp = sp->next; + } + sti(); /* We only turn interrupts back on for a moment, but because the interrupt queues anything built up before this will clear before we jump back and cli, so its not as bad as it looks */ - } - return(strlen(buffer)); + } + return(strlen(buffer)); } int tcp_get_info(char *buffer) { - return get__netinfo(&tcp_prot, buffer,0); + return get__netinfo(&tcp_prot, buffer,0); } int udp_get_info(char *buffer) { - return get__netinfo(&udp_prot, buffer,1); + return get__netinfo(&udp_prot, buffer,1); } int raw_get_info(char *buffer) { - return get__netinfo(&raw_prot, buffer,1); + return get__netinfo(&raw_prot, buffer,1); } diff --git a/net/inet/protocol.c b/net/inet/protocol.c index 5690267..b6390db 100644 --- a/net/inet/protocol.c +++ b/net/inet/protocol.c @@ -5,7 +5,7 @@ * * INET protocol dispatch tables. * - * Version: @(#)protocol.c 1.0.5 05/25/93 + * Version: @(#)protocol.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -14,7 +14,8 @@ * Alan Cox : Ahah! udp icmp errors don't work because * udp_err is never called! * Alan Cox : Added new fields for init and ready for - * proper fragmentation (_NO_ 4K limits!) + * proper fragmentation (_NO_ 4K limits!). + * Alan Cox : Final clean up. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,132 +31,143 @@ #include <linux/socket.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "tcp.h" -#include "skbuff.h" -#include "sock.h" +#include "socket/skbuff.h" +#include "sockinet.h" #include "icmp.h" #include "udp.h" -static struct inet_protocol tcp_protocol = { - tcp_rcv, /* TCP handler */ - NULL, /* No fragment handler (and won't be for a long time) */ - tcp_err, /* TCP error control */ - NULL, /* next */ - IPPROTO_TCP, /* protocol ID */ - 0, /* copy */ - NULL, /* data */ - "TCP" /* name */ +static struct inet_protocol tcp_protocol = +{ + tcp_rcv, /* TCP handler */ + NULL, /* No fragment handler (and won't be for a long time) */ + tcp_err, /* TCP error control */ + NULL, /* next */ + IPPROTO_TCP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "TCP" /* name */ }; -static struct inet_protocol udp_protocol = { - udp_rcv, /* UDP handler */ - NULL, /* Will be UDP fraglist handler */ - udp_err, /* UDP error control */ - &tcp_protocol, /* next */ - IPPROTO_UDP, /* protocol ID */ - 0, /* copy */ - NULL, /* data */ - "UDP" /* name */ +static struct inet_protocol udp_protocol = +{ + udp_rcv, /* UDP handler */ + NULL, /* Will be UDP fraglist handler */ + udp_err, /* UDP error control */ + &tcp_protocol, /* next */ + IPPROTO_UDP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "UDP" /* name */ }; -static struct inet_protocol icmp_protocol = { - icmp_rcv, /* ICMP handler */ - NULL, /* ICMP never fragments anyway */ - NULL, /* ICMP error control */ - &udp_protocol, /* next */ - IPPROTO_ICMP, /* protocol ID */ - 0, /* copy */ - NULL, /* data */ - "ICMP" /* name */ +static struct inet_protocol icmp_protocol = +{ + icmp_rcv, /* ICMP handler */ + NULL, /* ICMP never fragments anyway */ + NULL, /* ICMP error control */ + &udp_protocol, /* next */ + IPPROTO_ICMP, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "ICMP" /* name */ }; struct inet_protocol *inet_protocol_base = &icmp_protocol; -struct inet_protocol *inet_protos[MAX_INET_PROTOS] = { - NULL + +struct inet_protocol *inet_protos[MAX_INET_PROTOS] = +{ + NULL }; -struct inet_protocol * -inet_get_protocol(unsigned char prot) +struct inet_protocol *inet_get_protocol(unsigned char prot) { - unsigned char hash; - struct inet_protocol *p; - - DPRINTF((DBG_PROTO, "get_protocol (%d)\n ", prot)); - hash = prot & (MAX_INET_PROTOS - 1); - for (p = inet_protos[hash] ; p != NULL; p=p->next) { - DPRINTF((DBG_PROTO, "trying protocol %d\n", p->protocol)); - if (p->protocol == prot) return((struct inet_protocol *) p); - } - return(NULL); + unsigned char hash; + struct inet_protocol *p; + + DPRINTF((DBG_PROTO, "get_protocol (%d)\n ", prot)); + hash = prot & (MAX_INET_PROTOS - 1); + for (p = inet_protos[hash] ; p != NULL; p=p->next) + { + DPRINTF((DBG_PROTO, "trying protocol %d\n", p->protocol)); + if (p->protocol == prot) + return((struct inet_protocol *) p); + } + return(NULL); } -void -inet_add_protocol(struct inet_protocol *prot) +void inet_add_protocol(struct inet_protocol *prot) { - unsigned char hash; - struct inet_protocol *p2; - - hash = prot->protocol & (MAX_INET_PROTOS - 1); - prot ->next = inet_protos[hash]; - inet_protos[hash] = prot; - prot->copy = 0; - - /* Set the copy bit if we need to. */ - p2 = (struct inet_protocol *) prot->next; - while(p2 != NULL) { - if (p2->protocol == prot->protocol) { - prot->copy = 1; - break; - } - p2 = (struct inet_protocol *) prot->next; - } + unsigned char hash; + struct inet_protocol *p2; + + hash = prot->protocol & (MAX_INET_PROTOS - 1); + prot ->next = inet_protos[hash]; + inet_protos[hash] = prot; + prot->copy = 0; + + /* Set the copy bit if we need to. */ + p2 = (struct inet_protocol *) prot->next; + while(p2 != NULL) + { + if (p2->protocol == prot->protocol) + { + prot->copy = 1; + break; + } + p2 = (struct inet_protocol *) prot->next; + } } -int -inet_del_protocol(struct inet_protocol *prot) +int inet_del_protocol(struct inet_protocol *prot) { - struct inet_protocol *p; - struct inet_protocol *lp = NULL; - unsigned char hash; - - hash = prot->protocol & (MAX_INET_PROTOS - 1); - if (prot == inet_protos[hash]) { - inet_protos[hash] = (struct inet_protocol *) inet_protos[hash]->next; - return(0); - } - - p = (struct inet_protocol *) inet_protos[hash]; - while(p != NULL) { - /* - * We have to worry if the protocol being deleted is - * the last one on the list, then we may need to reset - * someones copied bit. - */ - if (p->next != NULL && p->next == prot) { - /* - * if we are the last one with this protocol and - * there is a previous one, reset its copy bit. - */ - if (p->copy == 0 && lp != NULL) lp->copy = 0; - p->next = prot->next; - return(0); - } - - if (p->next != NULL && p->next->protocol == prot->protocol) { - lp = p; + struct inet_protocol *p; + struct inet_protocol *lp = NULL; + unsigned char hash; + + hash = prot->protocol & (MAX_INET_PROTOS - 1); + if (prot == inet_protos[hash]) + { + inet_protos[hash] = (struct inet_protocol *) inet_protos[hash]->next; + return(0); } - p = (struct inet_protocol *) p->next; - } - return(-1); + p = (struct inet_protocol *) inet_protos[hash]; + while(p != NULL) + { + /* + * We have to worry if the protocol being deleted is + * the last one on the list, then we may need to reset + * someones copied bit. + */ + if (p->next != NULL && p->next == prot) + { + /* + * if we are the last one with this protocol and + * there is a previous one, reset its copy bit. + */ + if (p->copy == 0 && lp != NULL) + lp->copy = 0; + p->next = prot->next; + return(0); + } + + if (p->next != NULL && p->next->protocol == prot->protocol) + { + lp = p; + } + + p = (struct inet_protocol *) p->next; + } + return(-1); } diff --git a/net/inet/raw.c b/net/inet/raw.c index 7533532..aa56a3c 100644 --- a/net/inet/raw.c +++ b/net/inet/raw.c @@ -5,7 +5,7 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: @(#)raw.c 1.0.4 05/25/93 + * Version: @(#)raw.c 1.28 20/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -19,12 +19,16 @@ * Alan Cox : Checks sk->broadcast. * Alan Cox : Uses skb_free_datagram/skb_copy_datagram * Alan Cox : Raw passes ip options too + * Alan Cox : Cleaned up and reformatted for final release + * Alan Cox : Added socket option call to proto + * Alan Cox : Corrected broadcast check error to EACCES * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + #include <asm/system.h> #include <asm/segment.h> #include <linux/types.h> @@ -37,269 +41,301 @@ #include <linux/socket.h> #include <linux/in.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "icmp.h" #include "udp.h" -static unsigned long -min(unsigned long a, unsigned long b) +static unsigned long min(unsigned long a, unsigned long b) { - if (a < b) return(a); - return(b); + if (a < b) + return(a); + return(b); } -/* raw_err gets called by the icmp module. */ -void -raw_err (int err, unsigned char *header, unsigned long daddr, +/* + * raw_err gets called by the icmp module. + */ + +void raw_err (int err, unsigned char *header, unsigned long daddr, unsigned long saddr, struct inet_protocol *protocol) { - struct sock *sk; + struct sock *sk; - DPRINTF((DBG_RAW, "raw_err(err=%d, hdr=%X, daddr=%X, saddr=%X, protocl=%X)\n", + DPRINTF((DBG_RAW, "raw_err(err=%d, hdr=%X, daddr=%X, saddr=%X, protocl=%X)\n", err, header, daddr, saddr, protocol)); - if (protocol == NULL) return; - sk = (struct sock *) protocol->data; - if (sk == NULL) return; + if (protocol == NULL) + return; + + sk = (struct sock *) protocol->data; + if (sk == NULL) + return; - /* This is meaningless in raw sockets. */ - if (err & 0xff00 == (ICMP_SOURCE_QUENCH << 8)) { - if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2; - return; - } + /* This is meaningless in raw sockets. */ - sk->err = icmp_err_convert[err & 0xff].errno; - wake_up(sk->sleep); - - return; + if (err & 0xff00 == (ICMP_SOURCE_QUENCH << 8)) + { + if (sk->cong_window > 1) + sk->cong_window = sk->cong_window/2; + return; + } + + sk->err = icmp_err_convert[err & 0xff].errno; + sk->error_report(sk); + return; } /* - * This should be the easiest of all, all we do is\ - * copy it into a buffer. + * This should be the easiest of all, all we do is\ + * copy it into a buffer. We do have to diddle the pointer + * to get the ip header too. */ -int -raw_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, + +int raw_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol) { - struct sock *sk; - - DPRINTF((DBG_RAW, "raw_rcv(skb=%X, dev=%X, opt=%X, daddr=%X,\n" - " len=%d, saddr=%X, redo=%d, protocol=%X)\n", - skb, dev, opt, daddr, len, saddr, redo, protocol)); - - if (skb == NULL) return(0); - if (protocol == NULL) { - kfree_skb(skb, FREE_READ); - return(0); - } - sk = (struct sock *) protocol->data; - if (sk == NULL) { - kfree_skb(skb, FREE_READ); - return(0); - } - - /* Now we need to copy this into memory. */ - skb->sk = sk; - skb->len = len; - skb->dev = dev; - skb->saddr = daddr; - skb->daddr = saddr; - - /* Charge it too the socket. */ - if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) { - skb->sk = NULL; - kfree_skb(skb, FREE_READ); - return(0); - } - sk->rmem_alloc += skb->mem_len; - skb_queue_tail(&sk->rqueue,skb); - wake_up(sk->sleep); - release_sock(sk); - return(0); + struct sock *sk; + + DPRINTF((DBG_RAW, "raw_rcv(skb=%X, dev=%X, opt=%X, daddr=%X,\n" + " len=%d, saddr=%X, redo=%d, protocol=%X)\n", + skb, dev, opt, daddr, len, saddr, redo, protocol)); + + if (skb == NULL) + return(0); + + if (protocol == NULL) + { + kfree_skb(skb, FREE_READ); + return(0); + } + sk = (struct sock *) protocol->data; + if (sk == NULL) + { + kfree_skb(skb, FREE_READ); + return(0); + } + + /* Now we need to copy this into memory. */ + skb->sk = sk; + /* + * Adjust to get the header back + */ + + skb->len += skb->ip_hdr->ihl*sizeof(long); + skb->h.iph = skb->ip_hdr; + skb->dev = dev; + skb->saddr = daddr; + skb->daddr = saddr; + + /* Charge it too the socket. */ + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) + { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + return(0); + } + sk->rmem_alloc += skb->mem_len; + skb_queue_tail(&sk->rqueue,skb); + release_sock(sk); + sk->data_ready(sk,skb->len); + return(0); } -/* This will do terrible things if len + ipheader + devheader > dev->mtu */ -static int -raw_sendto(struct sock *sk, unsigned char *from, int len, +/* + * Send a RAW IP packet (user level IP protocols). Root only + * caller provides IP header. + */ + +static int raw_sendto(struct sock *sk, unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { - struct sk_buff *skb; - struct device *dev=NULL; - struct sockaddr_in sin; - int tmp; - int err; - - DPRINTF((DBG_RAW, "raw_sendto(sk=%X, from=%X, len=%d, noblock=%d, flags=%X,\n" - " usin=%X, addr_len = %d)\n", sk, from, len, noblock, - flags, usin, addr_len)); - - /* Check the flags. */ - if (flags) return(-EINVAL); - if (len < 0) return(-EINVAL); - - err=verify_area(VERIFY_READ,from,len); - if(err) - return err; - /* Get and verify the address. */ - if (usin) { - if (addr_len < sizeof(sin)) return(-EINVAL); - err=verify_area (VERIFY_READ, usin, sizeof (sin)); - if(err) - return err; - memcpy_fromfs(&sin, usin, sizeof(sin)); - if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL); - } else { - if (sk->state != TCP_ESTABLISHED) return(-EINVAL); - sin.sin_family = AF_INET; - sin.sin_port = sk->protocol; - sin.sin_addr.s_addr = sk->daddr; - } - if (sin.sin_port == 0) sin.sin_port = sk->protocol; + struct sk_buff *skb; + struct device *dev=NULL; + struct sockaddr_in sin; + int tmp; + int err; + + DPRINTF((DBG_RAW, "raw_sendto(sk=%X, from=%X, len=%d, noblock=%d, flags=%X,\n" + " usin=%X, addr_len = %d)\n", sk, from, len, noblock, + flags, usin, addr_len)); + + /* Check the flags. */ + if (flags) + return(-EINVAL); + if (len < 0) + return(-EINVAL); + + err=verify_area(VERIFY_READ,from,len); + if(err) + return err; + /* Get and verify the address. */ + if (usin) + { + if (addr_len < sizeof(sin)) + return(-EINVAL); + err=verify_area (VERIFY_READ, usin, sizeof (sin)); + if(err) + return err; + memcpy_fromfs(&sin, usin, sizeof(sin)); + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EINVAL); + } + else + { + if (sk->state != TCP_ESTABLISHED) + return(-EINVAL); + sin.sin_family = AF_INET; + sin.sin_port = sk->protocol; + sin.sin_addr.s_addr = sk->daddr; + } + if (sin.sin_port == 0) + sin.sin_port = sk->protocol; - if (sk->broadcast == 0 && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) - return -ENETUNREACH; + if (sk->broadcast == 0 && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) + return -EACCES; - sk->inuse = 1; - skb = NULL; - while (skb == NULL) { - if(sk->err!=0) + sk->inuse = 1; + skb = NULL; + while (skb == NULL) { - err= -sk->err; - sk->err=0; - release_sock(sk); - return(err); - } + if(sk->err!=0) + { + err= -sk->err; + sk->err=0; + release_sock(sk); + return(err); + } - skb = (struct sk_buff *) sk->prot->wmalloc(sk, + skb = (struct sk_buff *) sk->prot->wmalloc(sk, len+sizeof(*skb) + sk->prot->max_header, 0, GFP_KERNEL); - if (skb == NULL) { - int tmp; - - DPRINTF((DBG_RAW, "raw_sendto: write buffer full?\n")); - if (noblock) - return(-EAGAIN); - tmp = sk->wmem_alloc; - release_sock(sk); - cli(); - if (tmp <= sk->wmem_alloc) { - interruptible_sleep_on(sk->sleep); - if (current->signal & ~current->blocked) { - sti(); - return(-ERESTARTSYS); + if (skb == NULL) + { + int tmp; + + DPRINTF((DBG_RAW, "raw_sendto: write buffer full?\n")); + if (noblock) + return(-EAGAIN); + tmp = sk->wmem_alloc; + release_sock(sk); + cli(); + if (tmp <= sk->wmem_alloc) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + return(-ERESTARTSYS); + } } + sk->inuse = 1; + sti(); } - sk->inuse = 1; - sti(); - } - } - skb->mem_addr = skb; - skb->mem_len = len + sizeof(*skb) +sk->prot->max_header; - skb->sk = sk; - - skb->free = 1; /* these two should be unecessary. */ - skb->arp = 0; - - tmp = sk->prot->build_header(skb, sk->saddr, + } + skb->sk = sk; + + skb->free = 1; /* these two should be unecessary. */ + skb->arp = 0; + + tmp = sk->prot->build_header(skb, sk->saddr, sin.sin_addr.s_addr, &dev, - sk->protocol, sk->opt, skb->mem_len); - if (tmp < 0) { - DPRINTF((DBG_RAW, "raw_sendto: error building ip header.\n")); - kfree_skb(skb,FREE_WRITE); - release_sock(sk); - return(tmp); - } - - /* verify_area(VERIFY_WRITE, from, len);*/ - memcpy_fromfs ((unsigned char *)(skb+1)+tmp, from, len); - - /* If we are using IPPROTO_RAW, we need to fill in the source address in - the IP header */ - - if(sk->protocol==IPPROTO_RAW) { - unsigned char *buff; - struct iphdr *iph; - - buff = (unsigned char *)(skb + 1); - buff += tmp; - iph = (struct iphdr *)buff; - iph->saddr = sk->saddr; - } - - skb->len = tmp + len; + sk->protocol, sk->opt, skb->mem_len, + sk->ip_ttl,sk->ip_tos); + if (tmp < 0) + { + DPRINTF((DBG_RAW, "raw_sendto: error building ip header.\n")); + kfree_skb(skb,FREE_WRITE); + release_sock(sk); + return(tmp); + } + + memcpy_fromfs ((unsigned char *)(skb+1)+tmp, from, len); + + /* If we are using IPPROTO_RAW, we need to fill in the source address in + the IP header */ + + if(sk->protocol==IPPROTO_RAW) + { + unsigned char *buff; + struct iphdr *iph; + + buff = (unsigned char *)(skb + 1); + buff += tmp; + iph = (struct iphdr *)buff; + iph->saddr = sk->saddr; + } + + skb->len = tmp + len; - if(dev!=NULL && skb->len > 4095) - { - kfree_skb(skb, FREE_WRITE); - release_sock(sk); - return(-EMSGSIZE); - } + if(dev!=NULL && skb->len > 4095) + { + kfree_skb(skb, FREE_WRITE); + release_sock(sk); + return(-EMSGSIZE); + } - sk->prot->queue_xmit(sk, dev, skb, 1); - release_sock(sk); - return(len); + sk->prot->queue_xmit(sk, dev, skb, 1); + release_sock(sk); + return(len); } -static int -raw_write(struct sock *sk, unsigned char *buff, int len, int noblock, +static int raw_write(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { - return(raw_sendto(sk, buff, len, noblock, flags, NULL, 0)); + return(raw_sendto(sk, buff, len, noblock, flags, NULL, 0)); } -static void -raw_close(struct sock *sk, int timeout) +static void raw_close(struct sock *sk, int timeout) { - sk->inuse = 1; - sk->state = TCP_CLOSE; + sk->inuse = 1; + sk->state = TCP_CLOSE; - DPRINTF((DBG_RAW, "raw_close: deleting protocol %d\n", - ((struct inet_protocol *)sk->pair)->protocol)); + DPRINTF((DBG_RAW, "raw_close: deleting protocol %d\n", + ((struct inet_protocol *)sk->pair)->protocol)); - if (inet_del_protocol((struct inet_protocol *)sk->pair) < 0) + if (inet_del_protocol((struct inet_protocol *)sk->pair) < 0) DPRINTF((DBG_RAW, "raw_close: del_protocol failed.\n")); - kfree_s((void *)sk->pair, sizeof (struct inet_protocol)); - sk->pair = NULL; - release_sock(sk); + kfree_s((void *)sk->pair, sizeof (struct inet_protocol)); + sk->pair = NULL; + release_sock(sk); } -static int -raw_init(struct sock *sk) +static int raw_init(struct sock *sk) { - struct inet_protocol *p; - - p = (struct inet_protocol *) kmalloc(sizeof (*p), GFP_KERNEL); - if (p == NULL) return(-ENOMEM); - - p->handler = raw_rcv; - p->protocol = sk->protocol; - p->data = (void *)sk; - p->err_handler = raw_err; - p->name="USER"; - p->frag_handler = NULL; /* For now */ - inet_add_protocol(p); + struct inet_protocol *p; + + p = (struct inet_protocol *) kmalloc(sizeof (*p), GFP_KERNEL); + if (p == NULL) + return(-ENOMEM); + + p->handler = raw_rcv; + p->protocol = sk->protocol; + p->data = (void *)sk; + p->err_handler = raw_err; + p->name="USER"; + p->frag_handler = NULL; /* For now */ + inet_add_protocol(p); - /* We need to remember this somewhere. */ - sk->pair = (struct sock *)p; + /* We need to remember this somewhere. */ + sk->pair = (struct sock *)p; - DPRINTF((DBG_RAW, "raw init added protocol %d\n", sk->protocol)); + DPRINTF((DBG_RAW, "raw init added protocol %d\n", sk->protocol)); - return(0); + return(0); } @@ -307,91 +343,98 @@ raw_init(struct sock *sk) * This should be easy, if there is something there * we return it, otherwise we block. */ -int -raw_recvfrom(struct sock *sk, unsigned char *to, int len, + +int raw_recvfrom(struct sock *sk, unsigned char *to, int len, int noblock, unsigned flags, struct sockaddr_in *sin, int *addr_len) { - int copied=0; - struct sk_buff *skb; - int err; + int copied=0; + struct sk_buff *skb; + int err; - DPRINTF((DBG_RAW, "raw_recvfrom (sk=%X, to=%X, len=%d, noblock=%d, flags=%X,\n" - " sin=%X, addr_len=%X)\n", + DPRINTF((DBG_RAW, "raw_recvfrom (sk=%X, to=%X, len=%d, noblock=%d, flags=%X,\n" + " sin=%X, addr_len=%X)\n", sk, to, len, noblock, flags, sin, addr_len)); - if (len == 0) return(0); - if (len < 0) return(-EINVAL); - - if (sk->shutdown & RCV_SHUTDOWN) return(0); - if (addr_len) { - err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); - if(err) - return err; - put_fs_long(sizeof(*sin), addr_len); - } - err=verify_area(VERIFY_WRITE,to,len); - if(err) - return err; - - skb=skb_recv_datagram(sk,flags,noblock,&err); - if(skb==NULL) - return err; - - copied = min(len, skb->len); - - skb_copy_datagram(skb, 0, to, copied); + if (len == 0) + return(0); + if (len < 0) + return(-EINVAL); - /* Copy the address. */ - if (sin) { - struct sockaddr_in addr; + if (sk->shutdown & RCV_SHUTDOWN) + return(0); + if (addr_len) + { + err=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); + if(err) + return err; + put_fs_long(sizeof(*sin), addr_len); + } + err=verify_area(VERIFY_WRITE,to,len); + if(err) + return err; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = skb->daddr; - verify_area(VERIFY_WRITE, sin, sizeof(*sin)); - memcpy_tofs(sin, &addr, sizeof(*sin)); - } + skb=skb_recv_datagram(sk,flags,noblock,&err); + if(skb==NULL) + return err; - skb_free_datagram(skb); - release_sock(sk); - return (copied); + copied = min(len, skb->len); + + skb_copy_datagram(skb, 0, to, copied); + + /* Copy the address. */ + if (sin) + { + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = skb->daddr; + verify_area(VERIFY_WRITE, sin, sizeof(*sin)); + memcpy_tofs(sin, &addr, sizeof(*sin)); + } + + skb_free_datagram(skb); + release_sock(sk); + return (copied); } -int -raw_read (struct sock *sk, unsigned char *buff, int len, int noblock, +int raw_read (struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { - return(raw_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); + return(raw_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); } -struct proto raw_prot = { - sock_wmalloc, - sock_rmalloc, - sock_wfree, - sock_rfree, - sock_rspace, - sock_wspace, - raw_close, - raw_read, - raw_write, - raw_sendto, - raw_recvfrom, - ip_build_header, - udp_connect, - NULL, - ip_queue_xmit, - ip_retransmit, - NULL, - NULL, - raw_rcv, - datagram_select, - NULL, - raw_init, - NULL, - 128, - 0, - {NULL,}, - "RAW" +struct proto raw_prot = +{ + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + raw_close, + raw_read, + raw_write, + raw_sendto, + raw_recvfrom, + ip_build_header, + udp_connect, + NULL, + ip_queue_xmit, + ip_retransmit, + NULL, + NULL, + raw_rcv, + datagram_select, + NULL, + raw_init, + NULL, + ip_setsockopt, + ip_getsockopt, + 128, + 0, + {NULL,}, + "RAW" }; diff --git a/net/inet/route.c b/net/inet/route.c index 269c80d..cd2e355 100644 --- a/net/inet/route.c +++ b/net/inet/route.c @@ -47,18 +47,18 @@ static struct rtable *rt_base = NULL; static struct rtable *rt_loopback = NULL; /* Dump the contents of a routing table entry. */ -static void -rt_print(struct rtable *rt) +static void rt_print(struct rtable *rt) { - if (rt == NULL || inet_debug != DBG_RT) return; + if (rt == NULL || inet_debug != DBG_RT) + return; - printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n", + printk("RT: %06lx NXT=%06lx FLAGS=0x%02x\n", (long) rt, (long) rt->rt_next, rt->rt_flags); - printk(" TARGET=%s ", in_ntoa(rt->rt_dst)); - printk("GW=%s ", in_ntoa(rt->rt_gateway)); - printk(" DEV=%s USE=%ld REF=%d\n", - (rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name, - rt->rt_use, rt->rt_refcnt); + printk(" TARGET=%s ", in_ntoa(rt->rt_dst)); + printk("GW=%s ", in_ntoa(rt->rt_gateway)); + printk(" DEV=%s USE=%ld REF=%d\n", + (rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name, + rt->rt_use, rt->rt_refcnt); } @@ -162,8 +162,8 @@ static inline struct device * get_gw_dev(unsigned long gw) /* * rewrote rt_add(), as the old one was weird. Linus */ -void -rt_add(short flags, unsigned long dst, unsigned long mask, unsigned long gw, struct device *dev) +void rt_add(short flags, unsigned long dst, unsigned long mask, + unsigned long gw, struct device *dev) { struct rtable *r, *rt; struct rtable **rp; @@ -280,38 +280,35 @@ static int rt_new(struct rtentry *r) } -static int -rt_kill(struct rtentry *r) +static int rt_kill(struct rtentry *r) { - struct sockaddr_in *trg; - - trg = (struct sockaddr_in *) &r->rt_dst; - rt_del(trg->sin_addr.s_addr); + struct sockaddr_in *trg; - return(0); + trg = (struct sockaddr_in *) &r->rt_dst; + rt_del(trg->sin_addr.s_addr); + return 0; } /* Called from the PROCfs module. */ -int -rt_get_info(char *buffer) +int rt_get_info(char *buffer) { - struct rtable *r; - char *pos; + struct rtable *r; + char *pos; - pos = buffer; + pos = buffer; - pos += sprintf(pos, - "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\n"); + pos += sprintf(pos, + "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\n"); - /* This isn't quite right -- r->rt_dst is a struct! */ - for (r = rt_base; r != NULL; r = r->rt_next) { - pos += sprintf(pos, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n", - r->rt_dev->name, r->rt_dst, r->rt_gateway, - r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric, - r->rt_mask); - } - return(pos - buffer); + /* This isn't quite right -- r->rt_dst is a struct! */ + for (r = rt_base; r != NULL; r = r->rt_next) { + pos += sprintf(pos, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\n", + r->rt_dev->name, r->rt_dst, r->rt_gateway, + r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric, + r->rt_mask); + } + return pos - buffer; } /* @@ -340,39 +337,41 @@ no_route: } -int -rt_ioctl(unsigned int cmd, void *arg) +int rt_ioctl(unsigned int cmd, void *arg) { - struct device *dev; - struct rtentry rt; - char namebuf[32]; - int ret; - int err; + struct device *dev; + struct rtentry rt; + char namebuf[32]; + int ret; + int err; - switch(cmd) { + switch(cmd) { case DDIOCSDBG: ret = dbg_ioctl(arg, DBG_RT); break; + case SIOCADDRT: case SIOCDELRT: - if (!suser()) return(-EPERM); - err=verify_area(VERIFY_READ, arg, sizeof(struct rtentry)); + if (!suser()) + return -EPERM; + err = verify_area(VERIFY_READ, arg, sizeof(struct rtentry)); if(err) return err; memcpy_fromfs(&rt, arg, sizeof(struct rtentry)); if (rt.rt_dev) { - err=verify_area(VERIFY_READ, rt.rt_dev, sizeof namebuf); - if(err) - return err; - memcpy_fromfs(&namebuf, rt.rt_dev, sizeof namebuf); - dev = dev_get(namebuf); - rt.rt_dev = dev; + err = verify_area(VERIFY_READ, rt.rt_dev, sizeof namebuf); + if(err) + return err; + memcpy_fromfs(&namebuf, rt.rt_dev, sizeof namebuf); + dev = dev_get(namebuf); + rt.rt_dev = dev; } ret = (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt); break; + default: ret = -EINVAL; - } + } - return(ret); + return ret; } diff --git a/net/inet/route.h b/net/inet/route.h index bba3acf..c5429c6 100644 --- a/net/inet/route.h +++ b/net/inet/route.h @@ -24,15 +24,15 @@ /* This is an entry in the IP routing table. */ struct rtable { - struct rtable *rt_next; - unsigned long rt_dst; - unsigned long rt_mask; - unsigned long rt_gateway; - u_char rt_flags; - u_char rt_metric; - short rt_refcnt; - u_long rt_use; - struct device *rt_dev; + struct rtable *rt_next; + unsigned long rt_dst; + unsigned long rt_mask; + unsigned long rt_gateway; + u_char rt_flags; + u_char rt_metric; + short rt_refcnt; + u_long rt_use; + struct device *rt_dev; }; diff --git a/net/inet/skbuff.c b/net/inet/skbuff.c index 1f3299d..e69de29 100644 --- a/net/inet/skbuff.c +++ b/net/inet/skbuff.c @@ -1,454 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * A saner implementation of the skbuff stuff scattered everywhere - * in the old NET2D code. - * - * Authors: Alan Cox <iiitac@pyr.swan.ac.uk> - * - * Fixes: - * Alan Cox : Tracks memory and number of buffers for kernel memory report - * and memory leak hunting. - * Alan Cox : More generic kfree handler - */ - -#include <linux/config.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <asm/segment.h> -#include <asm/system.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include "inet.h" -#include "dev.h" -#include "ip.h" -#include "protocol.h" -#include "arp.h" -#include "route.h" -#include "tcp.h" -#include "udp.h" -#include "skbuff.h" -#include "sock.h" - - -/* Socket buffer operations. Ideally much of this list swap stuff ought to be using - exch instructions on the 386, and CAS/CAS2 on a 68K. This is the boring generic - slow C version. No doubt when Linus sees this comment he'll do horrible things - to this code 8-) -*/ - -/* - * Resource tracking variables - */ - -volatile unsigned long net_memory=0; -volatile unsigned long net_skbcount=0; - -/* - * Debugging paranoia. Can go later when this crud stack works - */ - - - -void skb_check(struct sk_buff *skb, int line, char *file) -{ - if(skb->magic_debug_cookie==SK_FREED_SKB) - { - printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n", - file,line); - printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n", - skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free); - } - if(skb->magic_debug_cookie!=SK_GOOD_SKB) - { - printk("File: %s Line %d, passed a non skb!\n", file,line); - printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n", - skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free); - } - if(skb->mem_len!=skb->truesize) - { - printk("File: %s Line %d, Dubious size setting!\n",file,line); - printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p\n", - skb,skb->truesize,skb->mem_len,skb->magic,skb->list); - } - /* Guess it might be acceptable then */ -} - -/* - * Insert an sk_buff at the start of a list. - */ - -void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk) -{ - unsigned long flags; - - IS_SKB(newsk); - if(newsk->list) - printk("Suspicious queue head: sk_buff on list!\n"); - save_flags(flags); - cli(); - newsk->list=list; - - newsk->next=*list; - - if(*list) - newsk->prev=(*list)->prev; - else - newsk->prev=newsk; - newsk->prev->next=newsk; - newsk->next->prev=newsk; - IS_SKB(newsk->prev); - IS_SKB(newsk->next); - *list=newsk; - restore_flags(flags); -} - -/* - * Insert an sk_buff at the end of a list. - */ - -void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk) -{ - unsigned long flags; - - if(newsk->list) - printk("Suspicious queue tail: sk_buff on list!\n"); - - IS_SKB(newsk); - save_flags(flags); - cli(); - - newsk->list=list; - if(*list) - { - (*list)->prev->next=newsk; - newsk->prev=(*list)->prev; - newsk->next=*list; - (*list)->prev=newsk; - } - else - { - newsk->next=newsk; - newsk->prev=newsk; - *list=newsk; - } - IS_SKB(newsk->prev); - IS_SKB(newsk->next); - restore_flags(flags); - -} - -/* - * Remove an sk_buff from a list. This routine is also interrupt safe - * so you can grab read and free buffers as another process adds them. - */ - -struct sk_buff *skb_dequeue(struct sk_buff *volatile* list) -{ - long flags; - struct sk_buff *result; - - save_flags(flags); - cli(); - - if(*list==NULL) - { - restore_flags(flags); - return(NULL); - } - - result=*list; - if(result->next==result) - *list=NULL; - else - { - result->next->prev=result->prev; - result->prev->next=result->next; - *list=result->next; - } - - IS_SKB(result); - restore_flags(flags); - - if(result->list!=list) - printk("Dequeued packet has invalid list pointer\n"); - - result->list=0; - result->next=0; - result->prev=0; - return(result); -} - -/* - * Insert a packet before another one in a list. - */ - -void skb_insert(struct sk_buff *old, struct sk_buff *newsk) -{ - unsigned long flags; - - IS_SKB(old); - IS_SKB(newsk); - - if(!old->list) - printk("insert before unlisted item!\n"); - if(newsk->list) - printk("inserted item is already on a list.\n"); - - save_flags(flags); - cli(); - newsk->list=old->list; - newsk->next=old; - newsk->prev=old->prev; - newsk->next->prev=newsk; - newsk->prev->next=newsk; - - restore_flags(flags); -} - -/* - * Place a packet after a given packet in a list. - */ - -void skb_append(struct sk_buff *old, struct sk_buff *newsk) -{ - unsigned long flags; - - IS_SKB(old); - IS_SKB(newsk); - - if(!old->list) - printk("append before unlisted item!\n"); - if(newsk->list) - printk("append item is already on a list.\n"); - - save_flags(flags); - cli(); - newsk->list=old->list; - newsk->prev=old; - newsk->next=old->next; - newsk->next->prev=newsk; - newsk->prev->next=newsk; - - restore_flags(flags); -} - -/* - * Remove an sk_buff from its list. Works even without knowing the list it - * is sitting on, which can be handy at times. It also means that THE LIST - * MUST EXIST when you unlink. Thus a list must have its contents unlinked - * _FIRST_. - */ - -void skb_unlink(struct sk_buff *skb) -{ - unsigned long flags; - save_flags(flags); - cli(); - - IS_SKB(skb); - - if(skb->list) - { - skb->next->prev=skb->prev; - skb->prev->next=skb->next; - if(*skb->list==skb) - { - if(skb->next==skb) - *skb->list=NULL; - else - *skb->list=skb->next; - } - skb->next=0; - skb->prev=0; - skb->list=0; - } - restore_flags(flags); -} - -/* - * An skbuff list has had its head reassigned. Move all the list - * pointers. Must be called with ints off during the whole head - * shifting - */ - -void skb_new_list_head(struct sk_buff *volatile* list) -{ - struct sk_buff *skb=skb_peek(list); - if(skb!=NULL) - { - do - { - IS_SKB(skb); - skb->list=list; - skb=skb->next; - } - while(skb!=*list); - } -} - -/* - * Peek an sk_buff. Unlike most other operations you _MUST_ - * be careful with this one. A peek leaves the buffer on the - * list and someone else may run off with it. For an interrupt - * type system cli() peek the buffer copy the data and sti(); - */ - -struct sk_buff *skb_peek(struct sk_buff *volatile* list) -{ - return *list; -} - -/* - * Get a clone of an sk_buff. This is the safe way to peek at - * a socket queue without accidents. Its a bit long but most - * of it acutally ends up as tiny bits of inline assembler - * anyway. Only the memcpy of upto 4K with ints off is not - * as nice as I'd like. - */ - -struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) -{ - struct sk_buff *orig,*newsk; - unsigned long flags; - unsigned int len; - /* Now for some games to avoid races */ - - do - { - save_flags(flags); - cli(); - orig=skb_peek(list); - if(orig==NULL) - { - restore_flags(flags); - return NULL; - } - IS_SKB(orig); - len=orig->truesize; - restore_flags(flags); - - newsk=alloc_skb(len,GFP_KERNEL); /* May sleep */ - - if(newsk==NULL) /* Oh dear... not to worry */ - return NULL; - - save_flags(flags); - cli(); - if(skb_peek(list)!=orig) /* List changed go around another time */ - { - restore_flags(flags); - newsk->sk=NULL; - newsk->free=1; - newsk->mem_addr=newsk; - newsk->mem_len=len; - kfree_skb(newsk, FREE_WRITE); - continue; - } - - IS_SKB(orig); - IS_SKB(newsk); - memcpy(newsk,orig,len); - newsk->list=NULL; - newsk->magic=0; - newsk->next=NULL; - newsk->prev=NULL; - newsk->mem_addr=newsk; - newsk->h.raw+=((char *)newsk-(char *)orig); - newsk->link3=NULL; - newsk->sk=NULL; - newsk->free=1; - } - while(0); - - restore_flags(flags); - return(newsk); -} - -/* - * Free an sk_buff. This still knows about things it should - * not need to like protocols and sockets. - */ - -void kfree_skb(struct sk_buff *skb, int rw) -{ - if (skb == NULL) { - printk("kfree_skb: skb = NULL\n"); - return; - } - IS_SKB(skb); - if(skb->free == 2) - printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n"); - if(skb->list) - printk("Warning: kfree_skb passed an skb still on a list.\n"); - skb->magic = 0; - if (skb->sk) - { - if(skb->sk->prot!=NULL) - { - if (rw) - skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len); - else - skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len); - - } - else - { - /* Non INET - default wmalloc/rmalloc handler */ - if (rw) - skb->sk->rmem_alloc-=skb->mem_len; - else - skb->sk->wmem_alloc-=skb->mem_len; - if(!skb->sk->dead) - wake_up(skb->sk->sleep); - kfree_skbmem(skb->mem_addr,skb->mem_len); - } - } - else - kfree_skbmem(skb->mem_addr, skb->mem_len); -} - -/* - * Allocate a new skbuff. We do this ourselves so we can fill in a few 'private' - * fields and also do memory statistics to find all the [BEEP] leaks. - */ - - struct sk_buff *alloc_skb(unsigned int size,int priority) - { - struct sk_buff *skb=(struct sk_buff *)kmalloc(size,priority); - if(skb==NULL) - return NULL; - skb->free= 2; /* Invalid so we pick up forgetful users */ - skb->list= 0; /* Not on a list */ - skb->truesize=size; - skb->mem_len=size; - skb->mem_addr=skb; - skb->fraglist=NULL; - net_memory+=size; - net_skbcount++; - skb->magic_debug_cookie=SK_GOOD_SKB; - skb->users=0; - return skb; - } - -/* - * Free an skbuff by memory - */ - -void kfree_skbmem(void *mem,unsigned size) -{ - struct sk_buff *x=mem; - IS_SKB(x); - if(x->magic_debug_cookie==SK_GOOD_SKB) - { - x->magic_debug_cookie=SK_FREED_SKB; - kfree_s(mem,size); - net_skbcount--; - net_memory-=size; - } -} - diff --git a/net/inet/skbuff.h b/net/inet/skbuff.h index 6d5f879..e69de29 100644 --- a/net/inet/skbuff.h +++ b/net/inet/skbuff.h @@ -1,107 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * Definitions for the 'struct sk_buff' memory handlers. - * - * Version: @(#)skbuff.h 1.0.4 05/20/93 - * - * Authors: Ross Biro, <bir7@leland.Stanford.Edu> - * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> - * Corey Minyard <wf-rch!minyard@relay.EU.net> - * - * Fixes: - * Alan Cox : Volatiles (this makes me unhappy - we want proper asm linked list stuff) - * Alan Cox : Declaration for new primitives - * Alan Cox : Fraglist support (idea by Donald Becker) - * Alan Cox : 'users' counter. Combines with datagram changes to avoid skb_peek_copy - * being used. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#ifndef _SKBUFF_H -#define _SKBUFF_H -#include <linux/malloc.h> - -#ifdef CONFIG_IPX -#include "ipx.h" -#endif - -#define HAVE_ALLOC_SKB /* For the drivers to know */ - - -#define FREE_READ 1 -#define FREE_WRITE 0 - - -struct sk_buff { - unsigned long magic_debug_cookie; - struct sk_buff *volatile next; - struct sk_buff *volatile prev; - struct sk_buff *volatile link3; - struct sk_buff *volatile* list; - struct sock *sk; - volatile unsigned long when; /* used to compute rtt's */ - struct device *dev; - void *mem_addr; - union { - struct tcphdr *th; - struct ethhdr *eth; - struct iphdr *iph; - struct udphdr *uh; - struct arphdr *arp; - unsigned char *raw; - unsigned long seq; -#ifdef CONFIG_IPX - ipx_packet *ipx; -#endif - } h; - unsigned long mem_len; - unsigned long len; - unsigned long fraglen; - struct sk_buff *fraglist; /* Fragment list */ - unsigned long truesize; - unsigned long saddr; - unsigned long daddr; - int magic; - volatile char acked, - used, - free, - arp, - urg_used; - unsigned char tries,lock; /* Lock is now unused */ - unsigned short users; /* User count - see datagram.c (and soon seqpacket.c/stream.c) */ -}; - -#define SK_WMEM_MAX 8192 -#define SK_RMEM_MAX 32767 - -#define SK_FREED_SKB 0x0DE2C0DE -#define SK_GOOD_SKB 0xDEC0DED1 - -extern void print_skb(struct sk_buff *); -extern void kfree_skb(struct sk_buff *skb, int rw); -extern void skb_queue_head(struct sk_buff * volatile *list,struct sk_buff *buf); -extern void skb_queue_tail(struct sk_buff * volatile *list,struct sk_buff *buf); -extern struct sk_buff * skb_dequeue(struct sk_buff * volatile *list); -extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk); -extern void skb_append(struct sk_buff *old,struct sk_buff *newsk); -extern void skb_unlink(struct sk_buff *buf); -extern void skb_new_list_head(struct sk_buff *volatile* list); -extern struct sk_buff * skb_peek(struct sk_buff * volatile *list); -extern struct sk_buff * skb_peek_copy(struct sk_buff * volatile *list); -extern struct sk_buff * alloc_skb(unsigned int size, int priority); -extern void kfree_skbmem(void *mem, unsigned size); - -extern void skb_check(struct sk_buff *skb,int, char *); -#define IS_SKB(skb) skb_check((skb),__LINE__,__FILE__) - -extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err); -extern int datagram_select(struct sock *sk, int sel_type, select_table *wait); -extern void skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size); -extern void skb_free_datagram(struct sk_buff *skb); -#endif /* _SKBUFF_H */ diff --git a/net/inet/sockinet.c b/net/inet/sockinet.c new file mode 100644 index 0000000..004d63c --- /dev/null +++ b/net/inet/sockinet.c @@ -0,0 +1,1636 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * SOCK - AF_INET protocol family socket handler. + * + * Version: @(#)sock.c 1.28 24/12/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Florian La Roche, <flla@stud.uni-sb.de> + * + * Fixes: + * Alan Cox : Numerous verify_area() problems + * Alan Cox : Connecting on a connecting socket + * now returns an error for tcp. + * Alan Cox : sock->protocol is set correctly. + * and is not sometimes left as 0. + * Alan Cox : connect handles icmp errors on a + * connect properly. Unfortunately there + * is a restart syscall nasty there. I + * can't match BSD without hacking the C + * library. Ideas urgently sought! + * Alan Cox : Disallow bind() to addresses that are + * not ours - especially broadcast ones!! + * Alan Cox : Socket 1024 _IS_ ok for users. (fencepost) + * Alan Cox : sock_wfree/sock_rfree don't destroy sockets, + * instead they leave that for the DESTROY timer. + * Alan Cox : Clean up error flag in accept + * Alan Cox : TCP ack handling is buggy, the DESTROY timer + * was buggy. Put a remove_sock() in the handler + * for memory when we hit 0. Also altered the timer + * code. The ACK stuff can wait and needs major + * TCP layer surgery. + * Alan Cox : Fixed TCP ack bug, removed remove sock + * and fixed timer/inet_bh race. + * Alan Cox : Added zapped flag for TCP + * Alan Cox : Move kfree_skb into skbuff.c and tidied up surplus code + * Alan Cox : for new sk_buff allocations wmalloc/rmalloc now call alloc_skb + * Alan Cox : kfree_s calls now are kfree_skbmem so we can track skb resources + * Alan Cox : Supports socket option broadcast now as does udp. Packet and raw need fixing. + * Alan Cox : Added RCVBUF,SNDBUF size setting. It suddenely occured to me how easy it was so... + * Rick Sladkey : Relaxed UDP rules for matching packets. + * C.E.Hawkins : IFF_PROMISC/SIOCGHWADDR support + * Pauline Middelink : Pidentd support + * Alan Cox : Fixed connect() taking signals I think. + * Alan Cox : SO_LINGER supported + * Alan Cox : Error reporting fixes + * Anonymous : inet_create tidied up (sk->reuse setting) + * Alan Cox : Tidy up for release. + * Alan Cox : Moved this to sockinet.c and removed generic code. + * Alan Cox : inet sockets don't set sk->type! + * + * + * To Fix: + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/interrupt.h> + +#include <asm/segment.h> +#include <asm/system.h> + +#include "inet.h" +#include "devinet.h" +#include "ip.h" +#include "protocol.h" +#include "arp.h" +#include "route.h" +#include "tcp.h" +#include "udp.h" +#include "skbuff.h" +#include "sockinet.h" +#include "raw.h" +#include "icmp.h" + + +int inet_debug = DBG_OFF; /* INET module debug flag */ + + +#define min(a,b) ((a)<(b)?(a):(b)) + +extern struct proto packet_prot; + + + +static int sk_inuse(struct proto *prot, int num) +{ + struct sock *sk; + + for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];sk != NULL;sk=sk->next) + { + if (sk->num == num) return(1); + } + return(0); +} + + +unsigned short get_new_socknum(struct proto *prot, unsigned short base) +{ + static int start=0; + + /* + * Used to cycle through the port numbers so the + * chances of a confused connection drop. + */ + int i, j; + int best = 0; + int size = 32767; /* a big num. */ + struct sock *sk; + + if (base == 0) + base = PROT_SOCK+1+(start % 1024); + if (base <= PROT_SOCK) + { + base += PROT_SOCK+(start % 1024); + } + + /* Now look through the entire array and try to find an empty ptr. */ + for(i=0; i < SOCK_ARRAY_SIZE; i++) + { + j = 0; + sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)]; + while(sk != NULL) + { + sk = sk->next; + j++; + } + if (j == 0) + { + start =(i+1+start )%1024; + DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n", + i + base + 1, start)); + return(i+base+1); + } + if (j < size) + { + best = i; + size = j; + } + } + + /* Now make sure the one we want is not in use. */ + while(sk_inuse(prot, base +best+1)) + { + best += SOCK_ARRAY_SIZE; + } + DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n", + best + base + 1, start)); + return(best+base+1); +} + + +void put_sock(unsigned short num, struct sock *sk) +{ + struct sock *sk1; + struct sock *sk2; + int mask; + + DPRINTF((DBG_INET, "put_sock(num = %d, sk = %X\n", num, sk)); + sk->num = num; + sk->next = NULL; + num = num &(SOCK_ARRAY_SIZE -1); + + /* We can't have an interupt re-enter here. */ + cli(); + if (sk->prot->sock_array[num] == NULL) + { + sk->prot->sock_array[num] = sk; + sti(); + return; + } + sti(); + for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) + { + if ((mask & sk->saddr) && + (mask & sk->saddr) != (mask & 0xffffffff)) + { + mask = mask << 8; + break; + } + } + DPRINTF((DBG_INET, "mask = %X\n", mask)); + + cli(); + sk1 = sk->prot->sock_array[num]; + for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) + { + if (!(sk2->saddr & mask)) + { + if (sk2 == sk1) + { + sk->next = sk->prot->sock_array[num]; + sk->prot->sock_array[num] = sk; + sti(); + return; + } + sk->next = sk2; + sk1->next= sk; + sti(); + return; + } + sk1 = sk2; + } + + /* Goes at the end. */ + sk->next = NULL; + sk1->next = sk; + sti(); +} + + +static void remove_sock(struct sock *sk1) +{ + struct sock *sk2; + + DPRINTF((DBG_INET, "remove_sock(sk1=%X)\n", sk1)); + + if (!sk1) + { + printk("sock.c: remove_sock: sk1 == NULL\n"); + return; + } + + if (!sk1->prot) + { + printk("sock.c: remove_sock: sk1->prot == NULL\n"); + return; + } + + /* We can't have this changing out from under us. */ + cli(); + sk2 = sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)]; + if (sk2 == sk1) + { + sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)] = sk1->next; + sti(); + return; + } + + while(sk2 && sk2->next != sk1) + { + sk2 = sk2->next; + } + + if (sk2) + { + sk2->next = sk1->next; + sti(); + return; + } + + sti(); + + if (sk1->num != 0) + DPRINTF((DBG_INET, "remove_sock: sock not found.\n")); +} + + +void destroy_sock(struct sock *sk) +{ + struct sk_buff *skb; + + DPRINTF((DBG_INET, "destroying socket %X\n", sk)); + sk->inuse = 1; /* just to be safe. */ + + /* Incase it's sleeping somewhere. */ + if (!sk->dead) + wake_up(sk->sleep); + + remove_sock(sk); + + /* Now we can no longer get new packets. */ + delete_timer(sk); + + + if (sk->send_tmp != NULL) + { + IS_SKB(sk->send_tmp); + kfree_skb(sk->send_tmp, FREE_WRITE); + } + + /* Cleanup up the write buffer. */ + for(skb = sk->wfront; skb != NULL; ) + { + struct sk_buff *skb2; + + skb2=(struct sk_buff *)skb->next; + if (skb->magic != TCP_WRITE_QUEUE_MAGIC) + { + printk("sock.c:destroy_sock write queue with bad magic(%X)\n", + skb->magic); + break; + } + IS_SKB(skb); + kfree_skb(skb, FREE_WRITE); + skb = skb2; + } + + sk->wfront = NULL; + sk->wback = NULL; + + if (sk->rqueue != NULL) + { + while((skb=skb_dequeue(&sk->rqueue))!=NULL) + { + /* + * This will take care of closing sockets that were + * listening and didn't accept everything. + */ + if (skb->sk != NULL && skb->sk != sk) + { + IS_SKB(skb); + skb->sk->dead = 1; + skb->sk->prot->close(skb->sk, 0); + } + IS_SKB(skb); + kfree_skb(skb, FREE_READ); + } + } + sk->rqueue = NULL; + + /* Now we need to clean up the send head. */ + for(skb = sk->send_head; skb != NULL; ) + { + struct sk_buff *skb2; + + /* + * We need to remove skb from the transmit queue, + * or maybe the arp queue. + */ + cli(); + + if (skb->next != NULL) + { + IS_SKB(skb); + skb_unlink(skb); + } + + skb->dev = NULL; + sti(); + skb2 = (struct sk_buff *)skb->link3; + kfree_skb(skb, FREE_WRITE); + skb = skb2; + } + sk->send_head = NULL; + + /* And now the backlog. */ + if (sk->back_log != NULL) + { + /* this should never happen. */ + printk("Socket Error: Socket destroyed with data backlog.\n"); + cli(); + skb = (struct sk_buff *)sk->back_log; + do + { + struct sk_buff *skb2; + + skb2 = (struct sk_buff *)skb->next; + kfree_skb(skb, FREE_READ); + skb = skb2; + } + while(skb != sk->back_log); + sti(); + } + sk->back_log = NULL; + + /* Now if it has a half accepted/ closed socket. */ + if (sk->pair) + { + sk->pair->dead = 1; + sk->pair->prot->close(sk->pair, 0); + sk->pair = NULL; + } + + /* + * Now if everything is gone we can free the socket + * structure, otherwise we need to keep it around until + * everything is gone. + */ + if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) + { + kfree_s((void *)sk,sizeof(*sk)); + } + else + { + /* this should never happen. */ + /* actually it can if an ack has just been sent. */ + DPRINTF((DBG_INET, "possible memory leak in socket = %X\n", sk)); + sk->destroy = 1; + sk->ack_backlog = 0; + sk->inuse = 0; + reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME); + } + DPRINTF((DBG_INET, "leaving destroy_sock\n")); +} + + +static int inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + switch(cmd) + { + case F_SETOWN: + /* + * This is a little restrictive, but it's the only + * way to make sure that you can't send a sigurg to + * another process. + */ + if (!suser() && current->pgrp != -arg && current->pid != arg) + return(-EPERM); + sk->proc = arg; + return(0); + case F_GETOWN: + return(sk->proc); + default: + return(-EINVAL); + } +} + + +/* + * Set socket options on an inet socket. + */ + +static int inet_setsockopt(struct socket *sock, int level, int optname, + char *optval, int optlen) +{ + struct sock *sk = (struct sock *) sock->data; + if (level == SOL_SOCKET) + return sock_setsockopt(sk,level,optname,optval,optlen); + if (sk->prot->setsockopt==NULL) + return(-EOPNOTSUPP); + else + return sk->prot->setsockopt(sk,level,optname,optval,optlen); +} + + + + +static int inet_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + struct sock *sk = sock->data; + if (level == SOL_SOCKET) + return sock_getsockopt(sk,level,optname,optval,optlen); + if(sk->prot->getsockopt==NULL) + return(-EOPNOTSUPP); + else + return sk->prot->getsockopt(sk,level,optname,optval,optlen); +} + + + +static int inet_listen(struct socket *sock, int backlog) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + /* No listen() on a busy socket. */ + + if(sk->state != TCP_CLOSE) + return -EINVAL; + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + /* Some sanity checks here are a good idea */ + if(backlog<0) + return -EINVAL; + /* Pick a number, any number 8-) */ + if(backlog>5) + backlog=5; + + /* We might as well re use these. */ + sk->max_ack_backlog = backlog; + if (sk->state != TCP_LISTEN) + { + sk->ack_backlog = 0; + sk->state = TCP_LISTEN; + } + return(0); +} + +/* + * Default callbacks for user INET sockets. These just wake up + * the user owning the socket. + */ + +static void def_callback1(struct sock *sk) +{ + if(!sk->dead) + wake_up(sk->sleep); +} + +static void def_callback2(struct sock *sk,int len) +{ + if(!sk->dead) + wake_up(sk->sleep); +} + + +/* + * Create an inet socket. Note that sock=NULL is now legal, and means a kernel + * created socket. + */ + +static int inet_create(struct socket *sock, int protocol) +{ + struct sock *sk; + struct proto *prot; + int err; + + sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL); + + if (sk == NULL) + return(-ENOMEM); + sk->num = 0; + sk->reuse = 0; + switch(sock->type) + { + case SOCK_STREAM: + case SOCK_SEQPACKET: + if (protocol && protocol != IPPROTO_TCP) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + protocol = IPPROTO_TCP; + sk->no_check = TCP_NO_CHECK; + prot = &tcp_prot; + break; + + case SOCK_DGRAM: + if (protocol && protocol != IPPROTO_UDP) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + protocol = IPPROTO_UDP; + sk->no_check = UDP_NO_CHECK; + prot=&udp_prot; + break; + + case SOCK_RAW: + if (!suser()) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPERM); + } + if (!protocol) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + prot = &raw_prot; + sk->reuse = 1; + sk->no_check = 0; /* + * Doesn't matter no checksum is + * preformed anyway. + */ + sk->num = protocol; + break; + + case SOCK_PACKET: + if (!suser()) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPERM); + } + if (!protocol) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-EPROTONOSUPPORT); + } + prot = &packet_prot; + sk->reuse = 1; + sk->no_check = 0; /* Doesn't matter no checksum is + * preformed anyway. + */ + sk->num = protocol; + break; + + default: + kfree_s((void *)sk, sizeof(*sk)); + return(-ESOCKTNOSUPPORT); + } + sk->socket = sock; + sk->type = sock->type; + sk->protocol = protocol; + sk->wmem_alloc = 0; + sk->rmem_alloc = 0; + sk->sndbuf = SK_WMEM_MAX; + sk->rcvbuf = SK_RMEM_MAX; + sk->pair = NULL; + sk->opt = NULL; + sk->send_seq = 0; + sk->acked_seq = 0; + sk->copied_seq = 0; + sk->fin_seq = 0; + sk->proc = 0; + sk->rtt = TCP_WRITE_TIME; + sk->mdev = 0; + sk->backoff = 0; + sk->packets_out = 0; + sk->cong_window = 1; /* start with only sending one packet at a time. */ + sk->exp_growth = 1; /* if set cong_window grow exponentially every time + we get an ack. */ + sk->urginline = 0; + sk->intr = 0; + sk->linger = 0; + sk->destroy = 0; + + sk->priority = 1; + sk->shutdown = 0; + sk->urg = 0; + sk->keepopen = 0; + sk->zapped = 0; + sk->done = 0; + sk->ack_backlog = 0; + sk->window = 0; + sk->bytes_rcv = 0; + sk->state = TCP_CLOSE; + sk->dead = 0; + sk->ack_timed = 0; + sk->send_tmp = NULL; + sk->mss = 0; /* we will try not to send any packets smaller than this. */ + sk->debug = 0; + + /* this is how many unacked bytes we will accept for this socket. */ + sk->max_unacked = 2048; /* needs to be at most 2 full packets. */ + + /* how many packets we should send before forcing an ack. + * if this is set to zero it is the same as sk->delay_acks = 0 + */ + sk->max_ack_backlog = 0; + sk->inuse = 0; + sk->delay_acks = 0; + sk->wback = NULL; + sk->wfront = NULL; + sk->rqueue = NULL; + sk->mtu = 576; /* This is a reasonable typical choice. RFC791 guarantees this is acceptable */ + sk->prot = prot; + sk->sleep = sock->wait; + sk->daddr = 0; + sk->saddr = my_addr(); + sk->err = 0; + sk->next = NULL; + sk->pair = NULL; + sk->send_tail = NULL; + sk->send_head = NULL; + sk->timeout = 0; + sk->broadcast = 0; + sk->timer.data = (unsigned long)sk; + sk->timer.function = &net_timer; + sk->back_log = NULL; + sk->blog = 0; + sock->data =(void *) sk; + sk->dummy_th.doff = sizeof(sk->dummy_th)/4; + sk->dummy_th.res1=0; + sk->dummy_th.res2=0; + sk->dummy_th.urg_ptr = 0; + sk->dummy_th.fin = 0; + sk->dummy_th.syn = 0; + sk->dummy_th.rst = 0; + sk->dummy_th.psh = 0; + sk->dummy_th.ack = 0; + sk->dummy_th.urg = 0; + sk->dummy_th.dest = 0; + + sk->ip_tos=0; + sk->ip_ttl=64; + + sk->state_change = def_callback1; + sk->data_ready = def_callback2; + sk->write_space = def_callback1; + sk->error_report = def_callback1; + + if (sk->num) + { + /* + * It assumes that any protocol which allows + * the user to assign a number at socket + * creation time automatically + * shares. + */ + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + if (sk->prot->init) + { + err = sk->prot->init(sk); + if (err != 0) + { + destroy_sock(sk); + return(err); + } + } + return(0); +} + + +static int inet_dup(struct socket *newsock, struct socket *oldsock) +{ + return(inet_create(newsock,((struct sock *)(oldsock->data))->protocol)); +} + + +/* The peer socket should always be NULL. */ +static int inet_release(struct socket *sock, struct socket *peer) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + return(0); + + DPRINTF((DBG_INET, "inet_release(sock = %X, peer = %X)\n", sock, peer)); + wake_up(sk->sleep); + + /* Start closing the connection. This may take a while. */ + /* + * If linger is set, we don't return until the close + * is complete. Other wise we return immediately. The + * actually closing is done the same either way. + */ + if (sk->linger == 0) + { + sk->prot->close(sk,0); + sk->dead = 1; + } + else + { + DPRINTF((DBG_INET, "sk->linger set.\n")); + sk->prot->close(sk, 0); + cli(); + if (sk->lingertime) + current->timeout = jiffies + HZ*sk->lingertime; + while(sk->state != TCP_CLOSE && sk->state != TCP_FIN_WAIT2 && sk->state !=TCP_TIME_WAIT && current->timeout>0) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + current->timeout=0; + return(-ERESTARTSYS); + } + } + current->timeout=0; + sti(); + sk->dead = 1; + } + sk->inuse = 1; + + /* This will destroy it. */ + release_sock(sk); + sock->data = NULL; + DPRINTF((DBG_INET, "inet_release returning\n")); + return(0); +} + + +/* this needs to be changed to dissallow + the rebinding of sockets. What error + should it return? */ + +static int inet_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len) +{ + struct sockaddr_in addr; + struct sock *sk, *sk2; + unsigned short snum; + int err; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + /* check this error. */ + if (sk->state != TCP_CLOSE) + return(-EIO); + if (sk->num != 0) + return(-EINVAL); + + err=verify_area(VERIFY_READ, uaddr, addr_len); + if(err) + return err; + memcpy_fromfs(&addr, uaddr, min(sizeof(addr), addr_len)); + + snum = ntohs(addr.sin_port); + DPRINTF((DBG_INET, "bind sk =%X to port = %d\n", sk, snum)); + sk = (struct sock *) sock->data; + if (snum == 0) + { + snum = get_new_socknum(sk->prot, 0); + } + if (snum < PROT_SOCK && !suser()) + return(-EACCES); + + if (addr.sin_addr.s_addr!=0 && chk_addr(addr.sin_addr.s_addr)!=IS_MYADDR) + return(-EADDRNOTAVAIL); /* Source address MUST be ours! */ + + if (chk_addr(addr.sin_addr.s_addr) || addr.sin_addr.s_addr == 0) + sk->saddr = addr.sin_addr.s_addr; + + DPRINTF((DBG_INET, "sock_array[%d] = %X:\n", snum &(SOCK_ARRAY_SIZE -1), + sk->prot->sock_array[snum &(SOCK_ARRAY_SIZE -1)])); + + /* Make sure we are allowed to bind here. */ + cli(); +outside_loop: + for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];sk2 != NULL; sk2 = sk2->next) + { + if (sk2->num != snum) + continue; + if (sk2->saddr != sk->saddr) + continue; + if (sk2->dead && !sk2->inuse) /* Added in use check AC 24/12/93 */ + { + destroy_sock(sk2); + goto outside_loop; + } + if (!sk->reuse) + { + sti(); + return(-EADDRINUSE); + } + if (sk2->num != snum) + continue; /* more than one */ + if (sk2->saddr != sk->saddr) + continue; /* socket per slot ! -FB */ + if (!sk2->reuse) + { + sti(); + return(-EADDRINUSE); + } + } + sti(); + + remove_sock(sk); + put_sock(snum, sk); + sk->dummy_th.source = ntohs(sk->num); + sk->daddr = 0; + sk->dummy_th.dest = 0; + return(0); +} + + +static int inet_connect(struct socket *sock, struct sockaddr * uaddr, + int addr_len, int flags) +{ + struct sock *sk; + int err; + + sock->conn = NULL; + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED) + { + sock->state = SS_CONNECTED; + /* Connection completing after a connect/EINPROGRESS/select/connect */ + return 0; /* Rock and roll */ + } + + if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK)) + return -EALREADY; /* Connecting is currently in progress */ + + if (sock->state != SS_CONNECTING) + { + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = htons(sk->num); + } + + if (sk->prot->connect == NULL) + return(-EOPNOTSUPP); + + err = sk->prot->connect(sk, (struct sockaddr_in *)uaddr, addr_len); + if (err < 0) + return(err); + + sock->state = SS_CONNECTING; + } + + if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK)) + return(-EINPROGRESS); + + cli(); /* avoid the race condition */ + while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + return(-ERESTARTSYS); + } + /* This fixes a nasty in the tcp/ip code. There is a hideous hassle with + icmp error packets wanting to close a tcp or udp socket. */ + if(sk->err && sk->protocol == IPPROTO_TCP) + { + sti(); + sock->state = SS_UNCONNECTED; + err = -sk->err; + sk->err=0; + return err; /* set by tcp_err() */ + } + } + sti(); + sock->state = SS_CONNECTED; + + if (sk->state != TCP_ESTABLISHED && sk->err) + { + sock->state = SS_UNCONNECTED; + err=sk->err; + sk->err=0; + return(err); + } + return(0); +} + + +static int inet_socketpair(struct socket *sock1, struct socket *sock2) +{ + return(-EOPNOTSUPP); +} + + +static int inet_accept(struct socket *sock, struct socket *newsock, int flags) +{ + struct sock *sk1, *sk2; + int err; + + sk1 = (struct sock *) sock->data; + if (sk1 == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + /* + * We've been passed an extra socket. + * We need to free it up because the tcp module creates + * it's own when it accepts one. + */ + + if (newsock->data) + kfree_s(newsock->data, sizeof(struct sock)); + newsock->data = NULL; + + if (sk1->prot->accept == NULL) + return(-EOPNOTSUPP); + + /* Restore the state if we have been interrupted, and then returned. */ + if (sk1->pair != NULL ) + { + sk2 = sk1->pair; + sk1->pair = NULL; + } + else + { + sk2 = sk1->prot->accept(sk1,flags); + if (sk2 == NULL) + { + if (sk1->err <= 0) + printk("Warning sock.c:sk1->err <= 0. Returning non-error.\n"); + err=sk1->err; + sk1->err=0; + return(-err); + } + } + newsock->data = (void *)sk2; + sk2->sleep = newsock->wait; + newsock->conn = NULL; + if (flags & O_NONBLOCK) + return(0); + + cli(); /* avoid the race. */ + + while(sk2->state == TCP_SYN_RECV) + { + interruptible_sleep_on(sk2->sleep); + if (current->signal & ~current->blocked) + { + sti(); + sk1->pair = sk2; + sk2->sleep = NULL; + newsock->data = NULL; + return(-ERESTARTSYS); + } + } + sti(); + + if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) + { + + err = -sk2->err; + sk2->err=0; + destroy_sock(sk2); + newsock->data = NULL; + return(err); + } + newsock->state = SS_CONNECTED; + return(0); +} + + +static int inet_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_in sin; + struct sock *sk; + int len; + int err; + + + err = verify_area(VERIFY_WRITE,uaddr_len,sizeof(long)); + if(err) + return err; + + len=get_fs_long(uaddr_len); + + err = verify_area(VERIFY_WRITE, uaddr, len); + if(err) + return err; + + /* Check this error. */ + if (len < sizeof(sin)) + return(-EINVAL); + + sin.sin_family = AF_INET; + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + if (peer) + { + if (!tcp_connected(sk->state)) + return(-ENOTCONN); + sin.sin_port = sk->dummy_th.dest; + sin.sin_addr.s_addr = sk->daddr; + } + else + { + sin.sin_port = sk->dummy_th.source; + if (sk->saddr == 0) + sin.sin_addr.s_addr = my_addr(); + else + sin.sin_addr.s_addr = sk->saddr; + } + len = sizeof(sin); + memcpy_tofs(uaddr, &sin, sizeof(sin)); + put_fs_long(len, uaddr_len); + return(0); +} + + +static int inet_read(struct socket *sock, char *ubuf, int size, int noblock) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock,0)); +} + + +static int inet_recv(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + return(sk->prot->read(sk, (unsigned char *) ubuf, size, noblock, flags)); +} + + +static int inet_write(struct socket *sock, char *ubuf, int size, int noblock) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + if (sk->shutdown & SEND_SHUTDOWN) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, 0)); +} + + +static int inet_send(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + if (sk->shutdown & SEND_SHUTDOWN) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags)); +} + + +static int inet_sendto(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags, struct sockaddr *sin, int addr_len) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + if (sk->shutdown & SEND_SHUTDOWN) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + + if (sk->prot->sendto == NULL) + return(-EOPNOTSUPP); + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + return(sk->prot->sendto(sk, (unsigned char *) ubuf, size, noblock, flags, + (struct sockaddr_in *)sin, addr_len)); +} + + +static int inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, + unsigned flags, struct sockaddr *sin, int *addr_len ) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + if (sk->prot->recvfrom == NULL) + return(-EOPNOTSUPP); + + /* We may need to bind the socket. */ + if (sk->num == 0) + { + sk->num = get_new_socknum(sk->prot, 0); + if (sk->num == 0) + return(-EAGAIN); + put_sock(sk->num, sk); + sk->dummy_th.source = ntohs(sk->num); + } + + return(sk->prot->recvfrom(sk, (unsigned char *) ubuf, size, noblock, flags, + (struct sockaddr_in*)sin, addr_len)); +} + + +static int inet_shutdown(struct socket *sock, int how) +{ + struct sock *sk; + + /* + * This should really check to make sure + * the socket is a TCP socket. + */ + how++; /* maps 0->1 has the advantage of making bit 1 rcvs and + 1->2 bit 2 snds. + 2->3 */ + if (how & ~SHUTDOWN_MASK) + return(-EINVAL); + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED) + sock->state = SS_CONNECTED; + + if (!tcp_connected(sk->state)) + return(-ENOTCONN); + sk->shutdown |= how; + if (sk->prot->shutdown) + sk->prot->shutdown(sk, how); + return(0); +} + + +static int inet_select(struct socket *sock, int sel_type, select_table *wait ) +{ + struct sock *sk; + + sk = (struct sock *) sock->data; + if (sk == NULL) + { + printk("Warning: sock->data = NULL: %d\n" ,__LINE__); + return(0); + } + + if (sk->prot->select == NULL) + { + DPRINTF((DBG_INET, "select on non-selectable socket.\n")); + return(0); + } + return(sk->prot->select(sk, sel_type, wait)); +} + + +static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk; + int err; + + DPRINTF((DBG_INET, "INET: in inet_ioctl\n")); + sk = NULL; + if (sock && (sk = (struct sock *) sock->data) == NULL) + { + printk("AF_INET: Warning: sock->data = NULL: %d\n" , __LINE__); + return(0); + } + + switch(cmd) + { + case FIOSETOWN: + case SIOCSPGRP: + err=verify_area(VERIFY_READ,(int *)arg,sizeof(long)); + if(err) + return err; + if (sk) + sk->proc = get_fs_long((int *) arg); + return(0); + case FIOGETOWN: + case SIOCGPGRP: + if (sk) + { + err=verify_area(VERIFY_WRITE,(void *) arg, sizeof(long)); + if(err) + return err; + put_fs_long(sk->proc,(int *)arg); + } + return(0); + case DDIOCSDBG: + return(dbg_ioctl((void *) arg, DBG_INET)); + + case SIOCADDRT: + case SIOCDELRT: + return(rt_ioctl(cmd,(void *) arg)); + + case SIOCDARP: + case SIOCGARP: + case SIOCSARP: + return(arp_ioctl(cmd,(void *) arg)); + + case IP_SET_DEV: + case SIOCGIFCONF: + case SIOCGIFFLAGS: + case SIOCSIFFLAGS: + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + case SIOCGIFMEM: + case SIOCSIFMEM: + case SIOCGIFMTU: + case SIOCSIFMTU: + case SIOCSIFLINK: + case SIOCGIFHWADDR: + return(dev_ioctl(cmd,(void *) arg)); + + default: + if (!sk || !sk->prot->ioctl) + return(-EINVAL); + return(sk->prot->ioctl(sk, cmd, arg)); + } + /*NOTREACHED*/ + return(0); +} + + + +/* + * This routine must find a socket given a TCP or UDP header. + * Everyhting is assumed to be in net order. + */ +struct sock *get_sock(struct proto *prot, unsigned short num, + unsigned long raddr, + unsigned short rnum, unsigned long laddr) +{ + struct sock *s; + unsigned short hnum; + + hnum = ntohs(num); + DPRINTF((DBG_INET, "get_sock(prot=%X, num=%d, raddr=%X, rnum=%d, laddr=%X)\n", + prot, num, raddr, rnum, laddr)); + + /* + * SOCK_ARRAY_SIZE must be a power of two. This will work better + * than a prime unless 3 or more sockets end up using the same + * array entry. This should not be a problem because most + * well known sockets don't overlap that much, and for + * the other ones, we can just be careful about picking our + * socket number when we choose an arbitrary one. + */ + for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];s != NULL; s = s->next) + { + if (s->num != hnum) + continue; + if(s->dead && (s->state == TCP_CLOSE)) + continue; + if(prot == &udp_prot) + return s; + if(ip_addr_match(s->daddr,raddr)==0) + continue; + if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) + continue; + if(ip_addr_match(s->saddr,laddr) == 0) + continue; + return(s); + } + return(NULL); +} + + +void release_sock(struct sock *sk) +{ + if (!sk) + { + printk("sock.c: release_sock sk == NULL\n"); + return; + } + /* This one _IS_ legal */ + if (!sk->prot) + { + return; + } + + if (sk->blog) + return; + + /* See if we have any packets built up. */ + cli(); + sk->inuse = 1; + while(sk->back_log != NULL) + { + struct sk_buff *skb; + + sk->blog = 1; + skb =(struct sk_buff *)sk->back_log; + DPRINTF((DBG_INET, "release_sock: skb = %X:\n", skb)); + if (skb->next != skb) + { + sk->back_log = skb->next; + skb->prev->next = skb->next; + skb->next->prev = skb->prev; + } + else + { + sk->back_log = NULL; + } + sti(); + DPRINTF((DBG_INET, "sk->back_log = %X\n", sk->back_log)); + if (sk->prot->rcv) + sk->prot->rcv(skb, skb->dev, sk->opt, + skb->saddr, skb->len, skb->daddr, 1, + /* Only used for/by raw sockets. */ + (struct inet_protocol *)sk->pair); + cli(); + } + sk->blog = 0; + sk->inuse = 0; + sti(); + if (sk->dead && sk->state == TCP_CLOSE) + { + /* Should be about 2 rtt's */ + reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME)); + } +} + + +static int inet_fioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int minor, ret; + + /* Extract the minor number on which we work. */ + minor = MINOR(inode->i_rdev); + if (minor != 0) + return(-ENODEV); + + /* Now dispatch on the minor device. */ + switch(minor) + { + case 0: /* INET */ + ret = inet_ioctl(NULL, cmd, arg); + break; + case 1: /* IP */ + ret = ip_ioctl(NULL, cmd, arg); + break; + case 2: /* ICMP */ + ret = icmp_ioctl(NULL, cmd, arg); + break; + case 3: /* TCP */ + ret = tcp_ioctl(NULL, cmd, arg); + break; + case 4: /* UDP */ + ret = udp_ioctl(NULL, cmd, arg); + break; + default: + ret = -ENODEV; + } + + return(ret); +} + + +static struct file_operations inet_fops = +{ + NULL, /* LSEEK */ + NULL, /* READ */ + NULL, /* WRITE */ + NULL, /* READDIR */ + NULL, /* SELECT */ + inet_fioctl, /* IOCTL */ + NULL, /* MMAP */ + NULL, /* OPEN */ + NULL /* CLOSE */ +}; + + +static struct proto_ops inet_proto_ops = +{ + AF_INET, + + inet_create, + inet_dup, + inet_release, + inet_bind, + inet_connect, + inet_socketpair, + inet_accept, + inet_getname, + inet_read, + inet_write, + inet_select, + inet_ioctl, + inet_listen, + inet_send, + inet_recv, + inet_sendto, + inet_recvfrom, + inet_shutdown, + inet_setsockopt, + inet_getsockopt, + inet_fcntl, +}; + +extern unsigned long seq_offset; + +/* + * Called by ddi.c on kernel startup. + */ + +void inet_proto_init(struct ddi_proto *pro) +{ + struct inet_protocol *p; + int i; + + printk("Swansea University Computer Society Net2Debugged [1.28]\n"); + /* Set up our UNIX VFS major device. */ + if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) + { + printk("%s: cannot register major device %d!\n", + pro->name, AF_INET_MAJOR); + return; + } + + /* Tell SOCKET that we are alive... */ + (void) sock_register(inet_proto_ops.family, &inet_proto_ops); + + seq_offset = CURRENT_TIME*250; + + /* Add all the protocols. */ + for(i = 0; i < SOCK_ARRAY_SIZE; i++) + { + tcp_prot.sock_array[i] = NULL; + udp_prot.sock_array[i] = NULL; + raw_prot.sock_array[i] = NULL; + } + printk("IP Protocols: "); + for(p = inet_protocol_base; p != NULL;) + { + struct inet_protocol *tmp; + + tmp = (struct inet_protocol *) p->next; + inet_add_protocol(p); + printk("%s%s",p->name,tmp?", ":"\n"); + p = tmp; + } + +} diff --git a/net/inet/sockinet.h b/net/inet/sockinet.h new file mode 100644 index 0000000..2e25e03 --- /dev/null +++ b/net/inet/sockinet.h @@ -0,0 +1,118 @@ +/* + * Definitions for the socket handler + * + * Version: @(#)sock.h 1.28 26/12/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * Florian La Roche <flla@stud.uni-sb.de> + * + * Fixes: + * Alan Cox : Volatiles in skbuff pointers. See + * skbuff comments. May be overdone, + * better to prove they can be removed + * than the reverse. + * Alan Cox : Added a zapped field for tcp to note + * a socket is reset and must stay shut up + * Alan Cox : New fields for options + * Pauline Middelink : identd support + * Alan Cox : Split into sock.h and sockinet.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _SOCKINET_H +#define _SOCKINET_H + +#ifndef _SOCK_H +#include "socket/sock.h" +#endif + +#ifndef _PROTOCOL_H_ +#include "protocol.h" +#endif + +struct proto { + void *(*wmalloc)(struct sock *sk, + unsigned long size, int force, + int priority); + void *(*rmalloc)(struct sock *sk, + unsigned long size, int force, + int priority); + void (*wfree)(struct sock *sk, void *mem, + unsigned long size); + void (*rfree)(struct sock *sk, void *mem, + unsigned long size); + unsigned long (*rspace)(struct sock *sk); + unsigned long (*wspace)(struct sock *sk); + void (*close)(struct sock *sk, int timeout); + int (*read)(struct sock *sk, unsigned char *to, + int len, int nonblock, unsigned flags); + int (*write)(struct sock *sk, unsigned char *to, + int len, int nonblock, unsigned flags); + int (*sendto)(struct sock *sk, + unsigned char *from, int len, int noblock, + unsigned flags, struct sockaddr_in *usin, + int addr_len); + int (*recvfrom)(struct sock *sk, + unsigned char *from, int len, int noblock, + unsigned flags, struct sockaddr_in *usin, + int *addr_len); + int (*build_header)(struct sk_buff *skb, + unsigned long saddr, + unsigned long daddr, + struct device **dev, int type, + struct options *opt, int len, + int ttl, int tos); + int (*connect)(struct sock *sk, + struct sockaddr_in *usin, int addr_len); + struct sock *(*accept) (struct sock *sk, int flags); + void (*queue_xmit)(struct sock *sk, + struct device *dev, struct sk_buff *skb, + int free); + void (*retransmit)(struct sock *sk, int all); + void (*write_wakeup)(struct sock *sk); + void (*read_wakeup)(struct sock *sk); + int (*rcv)(struct sk_buff *buff, struct device *dev, + struct options *opt, unsigned long daddr, + unsigned short len, unsigned long saddr, + int redo, struct inet_protocol *protocol); + int (*select)(struct sock *sk, int which, + select_table *wait); + int (*ioctl)(struct sock *sk, int cmd, + unsigned long arg); + int (*init)(struct sock *sk); + void (*shutdown)(struct sock *sk, int how); + 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 *option); + unsigned short max_header; + unsigned long retransmits; + struct sock *sock_array[SOCK_ARRAY_SIZE]; + char name[80]; +}; + +extern void destroy_sock(struct sock *sk); +extern unsigned short get_new_socknum(struct proto *, unsigned short); +extern void put_sock(unsigned short, struct sock *); +extern void release_sock(struct sock *sk); +extern struct sock *get_sock(struct proto *, unsigned short, + unsigned long, unsigned short, + unsigned long); + + +/* declarations from timer.c */ +extern struct sock *timer_base; + +void delete_timer (struct sock *); +void reset_timer (struct sock *, int, unsigned long); +void net_timer (unsigned long); + + + +#endif diff --git a/net/inet/tcp.c b/net/inet/tcp.c index 83dfb41..7239e09 100644 --- a/net/inet/tcp.c +++ b/net/inet/tcp.c @@ -4,8 +4,9 @@ * interface as the means of communication with the user level. * * Implementation of the Transmission Control Protocol(TCP). + * This protocol is described in RFC793. * - * Version: @(#)tcp.c 1.0.16 05/25/93 + * Version: @(#)tcp.c 1.28 25/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -14,9 +15,9 @@ * Florian La Roche, <flla@stud.uni-sb.de> * * Fixes: - * Alan Cox : Numerous verify_area() calls - * Alan Cox : Set the ACK bit on a reset - * Alan Cox : Stopped it crashing if it closed while sk->inuse=1 + * Alan Cox : Numerous verify_area() calls. + * Alan Cox : Set the ACK bit on a reset. + * Alan Cox : Stopped it crashing if it closed while sk->inuse=1. * and was trying to connect (tcp_err()). * Alan Cox : All icmp error handling was broken * pointers passed where wrong and the @@ -24,29 +25,39 @@ * tested any icmp error code obviously. * Alan Cox : tcp_err() now handled properly. It wakes people * on errors. select behaves and the icmp error race - * has gone by moving it into sock.c + * has gone by moving it into sock.c. * Alan Cox : tcp_reset() fixed to work for everything not just * packets for unknown sockets. * Alan Cox : tcp option processing. - * Alan Cox : Reset tweaked (still not 100%) [Had syn rule wrong] - * Herp Rosmanith : More reset fixes + * Alan Cox : Reset tweaked (still not 100%) [Had syn rule wrong]. + * Herp Rosmanith : More reset fixes. * Alan Cox : No longer acks invalid rst frames. Acking * any kind of RST is right out. * Alan Cox : Sets an ignore me flag on an rst receive - * otherwise odd bits of prattle escape still + * otherwise odd bits of prattle escape still. * Alan Cox : Fixed another acking RST frame bug. Should stop * LAN workplace lockups. - * Alan Cox : Some tidyups using the new skb list facilities - * Alan Cox : sk->keepopen now seems to work - * Alan Cox : Pulls options out correctly on accepts - * Alan Cox : Fixed assorted sk->rqueue->next errors + * Alan Cox : Some tidyups using the new skb list facilities. + * Alan Cox : sk->keepopen now seems to work. + * Alan Cox : Pulls options out correctly on accepts. + * Alan Cox : Fixed assorted sk->rqueue->next errors. * Alan Cox : PSH doesn't end a TCP read. Switched a bit to skb ops. * Alan Cox : Tidied tcp_data to avoid a potential nasty. - * Alan Cox : Added some beter commenting, as the tcp is hard to follow - * Alan Cox : Removed incorrect check for 20 * psh + * Alan Cox : Added some beter commenting, as the tcp is hard to follow. + * Alan Cox : Removed incorrect check for 20 * psh. * Michael O'Reilly : ack < copied bug fix. * Johannes Stille : Misc tcp fixes (not all in yet). - * Alan Cox : FIN with no memory -> CRASH + * Alan Cox : FIN with no memory -> CRASH. + * Alan Cox : Tidied up ready for final release and merge of other fixes. + * Alan Cox : Missing logic on FIN events. + * Alan Cox : Missing SYN bit check added (was #defined out in error). + * Alan Cox : recvfrom() semantics corrected - short recv ok, return on PSH flag. + * Alan Cox : window no longer memory driven so doesn't shrink illegally. + * Alan Cox : Added socket option proto entries. Also added awareness of them to accept. + * Alan Cox : State machine error causing Linus the ACK explosion problem found + * Unsynchronized states now send RST to bad ack frames as per RFC793 page 34. + * Alan Cox : Added TCP options (SOL_TCP) + * Alan Cox : Switched wakeup calls to callbacks, so the kernel can layer network sockets. * * * To Fix: @@ -54,12 +65,9 @@ * it causes a select. Linux can - given the official select semantics I * feel that _really_ its the BSD network programs that are bust (notably * inetd, which hangs occasionally because of this). - * Proper processing of piggybacked data on connect. - * Add VJ Fastrecovery algorithm ? * Protocol closedown badly messed up. - * Incompatiblity with spider ports (tcp hangs on that - * socket occasionally). - * MSG_PEEK and read on same socket at once can cause crashes. + * MSG_PEEK and read on same socket at once can cause crashes (in theory) + * Should use the partial packet to piggy back acks. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -76,13 +84,13 @@ #include <linux/in.h> #include <linux/fcntl.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "icmp.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "arp.h" #include <linux/errno.h> #include <linux/timer.h> @@ -96,67 +104,72 @@ unsigned long seq_offset; static __inline__ int min(unsigned int a, unsigned int b) { - if (a < b) return(a); - return(b); + if (a < b) + return(a); + return(b); } -void -print_th(struct tcphdr *th) +void print_th(struct tcphdr *th) { - unsigned char *ptr; - - if (inet_debug != DBG_TCP) return; - - printk("TCP header:\n"); - ptr =(unsigned char *)(th + 1); - printk(" source=%d, dest=%d, seq =%ld, ack_seq = %ld\n", - ntohs(th->source), ntohs(th->dest), - ntohl(th->seq), ntohl(th->ack_seq)); - printk(" fin=%d, syn=%d, rst=%d, psh=%d, ack=%d, urg=%d res1=%d res2=%d\n", - th->fin, th->syn, th->rst, th->psh, th->ack, - th->urg, th->res1, th->res2); - printk(" window = %d, check = %d urg_ptr = %d\n", - ntohs(th->window), ntohs(th->check), ntohs(th->urg_ptr)); - printk(" doff = %d\n", th->doff); - printk(" options = %d %d %d %d\n", ptr[0], ptr[1], ptr[2], ptr[3]); + unsigned char *ptr; + + if (inet_debug != DBG_TCP) + return; + + printk("TCP header:\n"); + ptr =(unsigned char *)(th + 1); + printk(" source=%d, dest=%d, seq =%ld, ack_seq = %ld\n", + ntohs(th->source), ntohs(th->dest), + ntohl(th->seq), ntohl(th->ack_seq)); + printk(" fin=%d, syn=%d, rst=%d, psh=%d, ack=%d, urg=%d res1=%d res2=%d\n", + th->fin, th->syn, th->rst, th->psh, th->ack, + th->urg, th->res1, th->res2); + printk(" window = %d, check = %d urg_ptr = %d\n", + ntohs(th->window), ntohs(th->check), ntohs(th->urg_ptr)); + printk(" doff = %d\n", th->doff); + printk(" options = %d %d %d %d\n", ptr[0], ptr[1], ptr[2], ptr[3]); } -/* This routine grabs the first thing off of a rcv queue. */ -static struct sk_buff * -get_firstr(struct sock *sk) +/* + * This routine grabs the first thing off of a rcv queue. + */ + +static struct sk_buff *get_firstr(struct sock *sk) { - return skb_dequeue(&sk->rqueue); + return skb_dequeue(&sk->rqueue); } /* * Difference between two values in tcp ack terms. */ -static long -diff(unsigned long seq1, unsigned long seq2) +static long diff(unsigned long seq1, unsigned long seq2) { - long d; + long d; - d = seq1 - seq2; - if (d > 0) return(d); + d = seq1 - seq2; + if (d > 0) + return(d); - /* I hope this returns what I want. */ - return(~d+1); + /* I hope this returns what I want. */ + return(~d+1); } -/* Enter the time wait state. */ +/* + * Enter the time wait state. + */ static void tcp_time_wait(struct sock *sk) { - sk->state = TCP_TIME_WAIT; - sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) - wake_up(sk->sleep); - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + sk->state = TCP_TIME_WAIT; + sk->shutdown = SHUTDOWN_MASK; + if (!sk->dead) + sk->state_change(sk); + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); } /* @@ -166,20 +179,20 @@ static void tcp_time_wait(struct sock *sk) * nothing clever here. */ -static void -tcp_retransmit(struct sock *sk, int all) +static void tcp_retransmit(struct sock *sk, int all) { - if (all) { - ip_retransmit(sk, all); - return; - } + if (all) + { + ip_retransmit(sk, all); + return; + } - if (sk->cong_window > 4) - sk->cong_window = sk->cong_window / 2; - sk->exp_growth = 0; + if (sk->cong_window > 4) + sk->cong_window = sk->cong_window / 2; + sk->exp_growth = 0; - /* Do the actuall retransmit. */ - ip_retransmit(sk, all); + /* Do the actuall retransmit. */ + ip_retransmit(sk, all); } @@ -191,56 +204,61 @@ tcp_retransmit(struct sock *sk, int all) * header points to the first 8 bytes of the tcp header. We need * to find the appropriate port. */ -void -tcp_err(int err, unsigned char *header, unsigned long daddr, +void tcp_err(int err, unsigned char *header, unsigned long daddr, unsigned long saddr, struct inet_protocol *protocol) { - struct tcphdr *th; - struct sock *sk; - struct iphdr *iph=(struct iphdr *)header; + struct tcphdr *th; + struct sock *sk; + struct iphdr *iph=(struct iphdr *)header; - header+=4*iph->ihl; + header+=4*iph->ihl; - DPRINTF((DBG_TCP, "TCP: tcp_err(%d, hdr=%X, daddr=%X saddr=%X, protocol=%X)\n", + DPRINTF((DBG_TCP, "TCP: tcp_err(%d, hdr=%X, daddr=%X saddr=%X, protocol=%X)\n", err, header, daddr, saddr, protocol)); - th =(struct tcphdr *)header; - sk = get_sock(&tcp_prot, th->source/*dest*/, daddr, th->dest/*source*/, saddr); - print_th(th); + th =(struct tcphdr *)header; + sk = get_sock(&tcp_prot, th->source/*dest*/, daddr, th->dest/*source*/, saddr); + print_th(th); - if (sk == NULL) return; + if (sk == NULL) + return; - if(err<0) - { - sk->err = -err; - wake_up(sk->sleep); - return; - } + if(err<0) + { + sk->err = -err; + sk->error_report(sk); + return; + } - if ((err & 0xff00) == (ICMP_SOURCE_QUENCH << 8)) { - /* - * FIXME: - * For now we will just trigger a linear backoff. - * The slow start code should cause a real backoff here. - */ - if (sk->cong_window > 4) sk->cong_window--; - return; - } + if ((err & 0xff00) == (ICMP_SOURCE_QUENCH << 8)) + { + /* + * FIXME: + * For now we will just trigger a linear backoff. + * The slow start code should cause a real backoff here. + */ + if (sk->cong_window > 4) sk->cong_window--; + return; + } - DPRINTF((DBG_TCP, "TCP: icmp_err got error\n")); - sk->err = icmp_err_convert[err & 0xff].errno; + DPRINTF((DBG_TCP, "TCP: icmp_err got error\n")); + sk->err = icmp_err_convert[err & 0xff].errno; - /* - * If we've already connected we will keep trying - * until we time out, or the user gives up. - */ - if (icmp_err_convert[err & 0xff].fatal) { - if (sk->state == TCP_SYN_SENT) { - sk->state = TCP_CLOSE; - wake_up(sk->sleep); /* Wake people up to see the error (see connect in sock.c) */ - } - } - return; + /* + * If we've already connected we will keep trying + * until we time out, or the user gives up. + */ + + if (icmp_err_convert[err & 0xff].fatal) + { + if (sk->state == TCP_SYN_SENT) + { + sk->state = TCP_CLOSE; + sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ + sk->state_change(sk); + } + } + return; } @@ -249,66 +267,63 @@ tcp_err(int err, unsigned char *header, unsigned long daddr, * in the received data queue (ie a frame missing that needs sending to us) */ -static int -tcp_readable(struct sock *sk) +static int tcp_readable(struct sock *sk) { - unsigned long counted; - unsigned long amount; - struct sk_buff *skb; - int count=0; - int sum; - unsigned long flags; - - DPRINTF((DBG_TCP, "tcp_readable(sk=%X)\n", sk)); - if(sk && sk->debug) - printk("tcp_readable: %p - ",sk); - - if (sk == NULL || skb_peek(&sk->rqueue) == NULL) /* Empty sockets are easy! */ - { - if(sk && sk->debug) - printk("empty\n"); - return(0); - } + unsigned long counted; + unsigned long amount; + struct sk_buff *skb; + int count=0; + int sum; + unsigned long flags; + + DPRINTF((DBG_TCP, "tcp_readable(sk=%X)\n", sk)); + if(sk && sk->debug) + printk("tcp_readable: %p - ",sk); + + if (sk == NULL || skb_peek(&sk->rqueue) == NULL) /* Empty sockets are easy! */ + { + if(sk && sk->debug) + printk("empty\n"); + return(0); + } - counted = sk->copied_seq+1; /* Where we are at the moment */ - amount = 0; + counted = sk->copied_seq+1; /* Where we are at the moment */ + amount = 0; - save_flags(flags); /* So nobody adds things at the wrong moment */ - cli(); - skb =(struct sk_buff *)sk->rqueue; - - /* Do until a push or until we are out of data. */ - do { - count++; -#ifdef OLD - /* This is wrong: It breaks Chameleon amongst other stacks */ - if (count > 20) { - restore_flags(flags); - DPRINTF((DBG_TCP, "tcp_readable, more than 20 packets without a psh\n")); - printk("tcp_read: possible read_queue corruption.\n"); - return(amount); - } -#endif - if (before(counted, skb->h.th->seq)) /* Found a hole so stops here */ - break; - sum = skb->len -(counted - skb->h.th->seq); /* Length - header but start from where we are up to (avoid overlaps) */ - if (skb->h.th->syn) sum++; - if (skb->h.th->urg) { - sum -= ntohs(skb->h.th->urg_ptr); /* Dont count urg data */ - } - if (sum >= 0) { /* Add it up, move on */ - amount += sum; - if (skb->h.th->syn) amount--; - counted += sum; - } -/* if (amount && skb->h.th->psh) break;*/ - skb =(struct sk_buff *)skb->next; /* Move along */ - } while(skb != sk->rqueue); - restore_flags(flags); - DPRINTF((DBG_TCP, "tcp readable returning %d bytes\n", amount)); - if(sk->debug) - printk("got %lu bytes.\n",amount); - return(amount); + save_flags(flags); /* So nobody adds things at the wrong moment */ + cli(); + skb =(struct sk_buff *)sk->rqueue; + + /* Do until a push or until we are out of data. */ + do + { + count++; + if (before(counted, skb->h.th->seq)) /* Found a hole so stops here */ + break; + sum = skb->len -(counted - skb->h.th->seq); /* Length - header but start from where we are up to (avoid overlaps) */ + if (skb->h.th->syn) + sum++; + if (skb->h.th->urg) + { + sum -= ntohs(skb->h.th->urg_ptr); /* Dont count urg data */ + } + if (sum >= 0) + { /* Add it up, move on */ + amount += sum; + if (skb->h.th->syn) + amount--; + counted += sum; + } +/* if (amount && skb->h.th->psh) + break;*/ + skb =(struct sk_buff *)skb->next; /* Move along */ + } + while(skb != sk->rqueue); + restore_flags(flags); + DPRINTF((DBG_TCP, "tcp readable returning %d bytes\n", amount)); + if(sk->debug) + printk("got %lu bytes.\n",amount); + return(amount); } @@ -317,112 +332,120 @@ tcp_readable(struct sock *sk) * listening socket has a receive queue of sockets to accept. */ -static int -tcp_select(struct sock *sk, int sel_type, select_table *wait) +static int tcp_select(struct sock *sk, int sel_type, select_table *wait) { - DPRINTF((DBG_TCP, "tcp_select(sk=%X, sel_type = %d, wait = %X)\n", + DPRINTF((DBG_TCP, "tcp_select(sk=%X, sel_type = %d, wait = %X)\n", sk, sel_type, wait)); - sk->inuse = 1; - switch(sel_type) { - case SEL_IN: - if(sk->debug) - printk("select in"); - select_wait(sk->sleep, wait); - if(sk->debug) + sk->inuse = 1; + switch(sel_type) + { + case SEL_IN: + if(sk->debug) + printk("select in"); + select_wait(sk->sleep, wait); + if(sk->debug) printk("-select out"); - if (skb_peek(&sk->rqueue) != NULL) { - if (sk->state == TCP_LISTEN || tcp_readable(sk)) { + if (skb_peek(&sk->rqueue) != NULL) + { + if (sk->state == TCP_LISTEN || tcp_readable(sk)) + { + release_sock(sk); + if(sk->debug) + printk("-select ok data\n"); + return(1); + } + } + if (sk->err != 0) /* Receiver error */ + { release_sock(sk); if(sk->debug) - printk("-select ok data\n"); + printk("-select ok error"); return(1); } - } - if (sk->err != 0) /* Receiver error */ - { - release_sock(sk); - if(sk->debug) - printk("-select ok error"); - return(1); - } - if (sk->shutdown & RCV_SHUTDOWN) { - release_sock(sk); - if(sk->debug) - printk("-select ok down\n"); - return(1); - } else { - release_sock(sk); - if(sk->debug) - printk("-select fail\n"); - return(0); - } - case SEL_OUT: - select_wait(sk->sleep, wait); - if (sk->shutdown & SEND_SHUTDOWN) { - DPRINTF((DBG_TCP, - "write select on shutdown socket.\n")); - - /* FIXME: should this return an error? */ - release_sock(sk); - return(0); - } - + if (sk->shutdown & RCV_SHUTDOWN) + { + release_sock(sk); + if(sk->debug) + printk("-select ok down\n"); + return(1); + } + else + { + release_sock(sk); + if(sk->debug) + printk("-select fail\n"); + return(0); + } + case SEL_OUT: + select_wait(sk->sleep, wait); + if (sk->shutdown & SEND_SHUTDOWN) + { + DPRINTF((DBG_TCP, + "write select on shutdown socket.\n")); + /* FIXME: should this return an error? */ + release_sock(sk); + return(0); + } /* * FIXME: * Hack so it will probably be able to write * something if it says it's ok to write. */ - if (sk->prot->wspace(sk) >= sk->mtu) { + if (sk->prot->wspace(sk) >= sk->mtu) + { + release_sock(sk); + /* This should cause connect to work ok. */ + if (sk->state == TCP_SYN_RECV || + sk->state == TCP_SYN_SENT) + return(0); + return(1); + } + DPRINTF((DBG_TCP, + "tcp_select: sleeping on write sk->wmem_alloc = %d, " + "sk->packets_out = %d\n" + "sk->wback = %X, sk->wfront = %X\n" + "sk->send_seq = %u, sk->window_seq=%u\n", + sk->wmem_alloc, sk->packets_out, + sk->wback, sk->wfront, + sk->send_seq, sk->window_seq)); + release_sock(sk); - /* This should cause connect to work ok. */ - if (sk->state == TCP_SYN_RECV || - sk->state == TCP_SYN_SENT) return(0); - return(1); - } - DPRINTF((DBG_TCP, - "tcp_select: sleeping on write sk->wmem_alloc = %d, " - "sk->packets_out = %d\n" - "sk->wback = %X, sk->wfront = %X\n" - "sk->send_seq = %u, sk->window_seq=%u\n", - sk->wmem_alloc, sk->packets_out, - sk->wback, sk->wfront, - sk->send_seq, sk->window_seq)); - - release_sock(sk); - return(0); - case SEL_EX: - select_wait(sk->sleep,wait); - if (sk->err) { + return(0); + case SEL_EX: + select_wait(sk->sleep,wait); + if (sk->err) + { + release_sock(sk); + return(1); + } release_sock(sk); - return(1); - } - release_sock(sk); - return(0); - } + return(0); + } - release_sock(sk); - return(0); + release_sock(sk); + return(0); } -int -tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) +int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) { - int err; - DPRINTF((DBG_TCP, "tcp_ioctl(sk=%X, cmd = %d, arg=%X)\n", sk, cmd, arg)); - switch(cmd) { - case DDIOCSDBG: - return(dbg_ioctl((void *) arg, DBG_TCP)); + int err; + DPRINTF((DBG_TCP, "tcp_ioctl(sk=%X, cmd = %d, arg=%X)\n", sk, cmd, arg)); + switch(cmd) + { + case DDIOCSDBG: + return(dbg_ioctl((void *) arg, DBG_TCP)); - case TIOCINQ: + case TIOCINQ: #ifdef FIXME /* FIXME: */ - case FIONREAD: + case FIONREAD: #endif { unsigned long amount; - if (sk->state == TCP_LISTEN) return(-EINVAL); + if (sk->state == TCP_LISTEN) + return(-EINVAL); sk->inuse = 1; amount = tcp_readable(sk); @@ -435,7 +458,7 @@ tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) put_fs_long(amount,(unsigned long *)arg); return(0); } - case SIOCATMARK: + case SIOCATMARK: { struct sk_buff *skb; int answ = 0; @@ -448,7 +471,7 @@ tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) if ((skb=skb_peek(&sk->rqueue)) != NULL) { if (sk->copied_seq+1 == skb->h.th->seq && skb->h.th->urg) - answ = 1; + answ = 1; } release_sock(sk); err=verify_area(VERIFY_WRITE,(void *) arg, @@ -458,11 +481,12 @@ tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) put_fs_long(answ,(int *) arg); return(0); } - case TIOCOUTQ: + case TIOCOUTQ: { unsigned long amount; - if (sk->state == TCP_LISTEN) return(-EINVAL); + if (sk->state == TCP_LISTEN) + return(-EINVAL); amount = sk->prot->wspace(sk); err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); @@ -471,986 +495,1288 @@ tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) put_fs_long(amount,(unsigned long *)arg); return(0); } - default: - return(-EINVAL); - } + default: + return(-EINVAL); + } } -/* This routine computes a TCP checksum. */ -unsigned short -tcp_check(struct tcphdr *th, int len, +/* + * This routine computes a TCP checksum. + */ + +unsigned short tcp_check(struct tcphdr *th, int len, unsigned long saddr, unsigned long daddr) { - unsigned long sum; + unsigned long sum; - if (saddr == 0) saddr = my_addr(); - print_th(th); - __asm__("\t addl %%ecx,%%ebx\n" - "\t adcl %%edx,%%ebx\n" - "\t adcl $0, %%ebx\n" - : "=b"(sum) - : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_TCP*256) - : "cx","bx","dx" ); + if (saddr == 0) + saddr = my_addr(); + print_th(th); + __asm__("\t addl %%ecx,%%ebx\n" + "\t adcl %%edx,%%ebx\n" + "\t adcl $0, %%ebx\n" + : "=b"(sum) + : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_TCP*256) + : "cx","bx","dx" ); - if (len > 3) { - __asm__("\tclc\n" - "1:\n" - "\t lodsl\n" - "\t adcl %%eax, %%ebx\n" - "\t loop 1b\n" - "\t adcl $0, %%ebx\n" - : "=b"(sum) , "=S"(th) - : "0"(sum), "c"(len/4) ,"1"(th) - : "ax", "cx", "bx", "si" ); - } - - /* Convert from 32 bits to 16 bits. */ - __asm__("\t movl %%ebx, %%ecx\n" - "\t shrl $16,%%ecx\n" - "\t addw %%cx, %%bx\n" - "\t adcw $0, %%bx\n" - : "=b"(sum) - : "0"(sum) - : "bx", "cx"); + if (len > 3) + { + __asm__("\tclc\n" + "1:\n" + "\t lodsl\n" + "\t adcl %%eax, %%ebx\n" + "\t loop 1b\n" + "\t adcl $0, %%ebx\n" + : "=b"(sum) , "=S"(th) + : "0"(sum), "c"(len/4) ,"1"(th) + : "ax", "cx", "bx", "si" ); + } - /* Check for an extra word. */ - if ((len & 2) != 0) { - __asm__("\t lodsw\n" - "\t addw %%ax,%%bx\n" - "\t adcw $0, %%bx\n" - : "=b"(sum), "=S"(th) - : "0"(sum) ,"1"(th) - : "si", "ax", "bx"); - } + /* Convert from 32 bits to 16 bits. */ + __asm__("\t movl %%ebx, %%ecx\n" + "\t shrl $16,%%ecx\n" + "\t addw %%cx, %%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum) + : "0"(sum) + : "bx", "cx"); + + /* Check for an extra word. */ + if ((len & 2) != 0) + { + __asm__("\t lodsw\n" + "\t addw %%ax,%%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum), "=S"(th) + : "0"(sum) ,"1"(th) + : "si", "ax", "bx"); + } - /* Now check for the extra byte. */ - if ((len & 1) != 0) { - __asm__("\t lodsb\n" - "\t movb $0,%%ah\n" - "\t addw %%ax,%%bx\n" - "\t adcw $0, %%bx\n" - : "=b"(sum) - : "0"(sum) ,"S"(th) - : "si", "ax", "bx"); - } + /* Now check for the extra byte. */ + if ((len & 1) != 0) + { + __asm__("\t lodsb\n" + "\t movb $0,%%ah\n" + "\t addw %%ax,%%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum) + : "0"(sum) ,"S"(th) + : "si", "ax", "bx"); + } - /* We only want the bottom 16 bits, but we never cleared the top 16. */ - return((~sum) & 0xffff); + /* We only want the bottom 16 bits, but we never cleared the top 16. */ + return((~sum) & 0xffff); } -void -tcp_send_check(struct tcphdr *th, unsigned long saddr, +void tcp_send_check(struct tcphdr *th, unsigned long saddr, unsigned long daddr, int len, struct sock *sk) { - th->check = 0; - th->check = tcp_check(th, len, saddr, daddr); - return; + th->check = 0; + th->check = tcp_check(th, len, saddr, daddr); + return; } +/* + * Send current partially built frame + */ -static void -tcp_send_partial(struct sock *sk) +static void tcp_send_partial(struct sock *sk) { - struct sk_buff *skb; + struct sk_buff *skb; - if (sk == NULL || sk->send_tmp == NULL) return; + if (sk == NULL || sk->send_tmp == NULL) + return; - skb = sk->send_tmp; + skb = sk->send_tmp; - /* We need to complete and send the packet. */ - tcp_send_check(skb->h.th, sk->saddr, sk->daddr, + /* We need to complete and send the packet. */ + + /* + * Fill in the checksum + */ + + tcp_send_check(skb->h.th, sk->saddr, sk->daddr, skb->len-(unsigned long)skb->h.th + (unsigned long)(skb+1), sk); - skb->h.seq = sk->send_seq; - if (after(sk->send_seq , sk->window_seq) || - sk->packets_out >= sk->cong_window) { - DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n", - sk->cong_window, sk->packets_out)); - DPRINTF((DBG_TCP, "sk->send_seq = %d, sk->window_seq = %d\n", - sk->send_seq, sk->window_seq)); - skb->next = NULL; - skb->magic = TCP_WRITE_QUEUE_MAGIC; - if (sk->wback == NULL) { - sk->wfront=skb; - } else { - sk->wback->next = skb; - } - sk->wback = skb; - } else { - sk->prot->queue_xmit(sk, skb->dev, skb,0); - } - sk->send_tmp = NULL; + /* + * Fill in the sequence number + */ + skb->h.seq = sk->send_seq; + + /* + * Check the window and packet counts to + * see where it goes + */ + if (after(sk->send_seq , sk->window_seq) || + sk->packets_out >= sk->cong_window) + { + DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n", + sk->cong_window, sk->packets_out)); + DPRINTF((DBG_TCP, "sk->send_seq = %d, sk->window_seq = %d\n", + sk->send_seq, sk->window_seq)); + skb->next = NULL; + skb->magic = TCP_WRITE_QUEUE_MAGIC; + if (sk->wback == NULL) + { + sk->wfront=skb; + } + else + { + sk->wback->next = skb; + } + sk->wback = skb; + } + else + { + sk->prot->queue_xmit(sk, skb->dev, skb,0); + } + sk->send_tmp = NULL; } -/* This routine sends an ack and also updates the window. */ -static void -tcp_send_ack(unsigned long sequence, unsigned long ack, +/* + * This routine sends an ack and also updates the window. + */ + +static void tcp_send_ack(unsigned long sequence, unsigned long ack, struct sock *sk, struct tcphdr *th, unsigned long daddr) { - struct sk_buff *buff; - struct tcphdr *t1; - struct device *dev = NULL; - int tmp; - - if(sk->zapped) - return; /* We have been reset, we may not send again */ - /* - * We need to grab some memory, and put together an ack, - * and then put it into the queue to be sent. - */ - buff = (struct sk_buff *) sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC); - if (buff == NULL) { - /* Force it to send an ack. */ - sk->ack_backlog++; - if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) { - reset_timer(sk, TIME_WRITE, 10); - } -if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n"); - return; - } - - buff->mem_addr = buff; - buff->mem_len = MAX_ACK_SIZE; - buff->len = sizeof(struct tcphdr); - buff->sk = sk; - t1 =(struct tcphdr *)(buff + 1); - - /* Put in the IP header and routing stuff. */ - tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE); - if (tmp < 0) { - buff->free=1; - sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); -if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n"); - return; - } - buff->len += tmp; - t1 =(struct tcphdr *)((char *)t1 +tmp); - - /* FIXME: */ - memcpy(t1, th, sizeof(*t1)); /* this should probably be removed */ - - /* swap the send and the receive. */ - t1->dest = th->source; - t1->source = th->dest; - t1->seq = ntohl(sequence); - t1->ack = 1; - sk->window = sk->prot->rspace(sk); - t1->window = ntohs(sk->window); - t1->res1 = 0; - t1->res2 = 0; - t1->rst = 0; - t1->urg = 0; - t1->syn = 0; - t1->psh = 0; - t1->fin = 0; - if (ack == sk->acked_seq) { - sk->ack_backlog = 0; - sk->bytes_rcv = 0; - sk->ack_timed = 0; - if (sk->send_head == NULL && sk->wfront == NULL) { -/* delete_timer(sk);*/ - } - } - t1->ack_seq = ntohl(ack); - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk); - if (sk->debug) - printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack); - sk->prot->queue_xmit(sk, dev, buff, 1); + struct sk_buff *buff; + struct tcphdr *t1; + struct device *dev = NULL; + int tmp; + + if(sk->zapped) + return; /* We have been reset, we may not send again */ + /* + * We need to grab some memory, and put together an ack, + * and then put it into the queue to be sent. + */ + buff = (struct sk_buff *) sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC); + if (buff == NULL) + { + /* Force it to send an ack. */ + sk->ack_backlog++; + if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) + { + reset_timer(sk, TIME_WRITE, 10); + } + if (inet_debug == DBG_SLIP) + printk("\rtcp_ack: malloc failed\n"); + return; + } + + buff->mem_addr = buff; + buff->mem_len = MAX_ACK_SIZE; + buff->len = sizeof(struct tcphdr); + buff->sk = sk; + t1 =(struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,IPPROTO_TCP, sk->opt, MAX_ACK_SIZE, + sk->ip_ttl,sk->ip_tos); + if (tmp < 0) + { + buff->free=1; + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n"); + return; + } + buff->len += tmp; + t1 =(struct tcphdr *)((char *)t1 +tmp); + + /* FIXME: */ + memcpy(t1, th, sizeof(*t1)); /* this should probably be removed */ + + /* swap the send and the receive. */ + t1->dest = th->source; + t1->source = th->dest; + t1->seq = ntohl(sequence); + t1->ack = 1; + sk->window = 4096/*sk->prot->rspace(sk)*/; + t1->window = ntohs(sk->window); + t1->res1 = 0; + t1->res2 = 0; + t1->rst = 0; + t1->urg = 0; + t1->syn = 0; + t1->psh = 0; + t1->fin = 0; + if (ack == sk->acked_seq) + { + sk->ack_backlog = 0; + sk->bytes_rcv = 0; + sk->ack_timed = 0; + if (sk->send_head == NULL && sk->wfront == NULL) + { +/* delete_timer(sk);*/ + } + } + t1->ack_seq = ntohl(ack); + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk); + if (sk->debug) + printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack); + sk->prot->queue_xmit(sk, dev, buff, 1); } -/* This routine builds a generic TCP header. */ -static int -tcp_build_header(struct tcphdr *th, struct sock *sk, int push) +/* + * This routine builds a generic TCP header. + */ + +static int tcp_build_header(struct tcphdr *th, struct sock *sk, int push) { - /* FIXME: want to get rid of this. */ - memcpy(th,(void *) &(sk->dummy_th), sizeof(*th)); - th->seq = htonl(sk->send_seq); - th->psh =(push == 0) ? 1 : 0; - th->doff = sizeof(*th)/4; - th->ack = 1; - th->fin = 0; - sk->ack_backlog = 0; - sk->bytes_rcv = 0; - sk->ack_timed = 0; - th->ack_seq = htonl(sk->acked_seq); - sk->window = sk->prot->rspace(sk); - th->window = htons(sk->window); - - return(sizeof(*th)); + /* FIXME: want to get rid of this. */ + memcpy(th,(void *) &(sk->dummy_th), sizeof(*th)); + th->seq = htonl(sk->send_seq); + th->psh =(push == 0) ? 1 : 0; + th->doff = sizeof(*th)/4; + th->ack = 1; + th->fin = 0; + sk->ack_backlog = 0; + sk->bytes_rcv = 0; + sk->ack_timed = 0; + th->ack_seq = htonl(sk->acked_seq); + sk->window = 4096-diff(sk->acked_seq,sk->copied_seq); /* sk->prot->rspace(sk); */ + th->window = htons(sk->window); + + return(sizeof(*th)); } /* - * This routine copies from a user buffer into a socket, - * and starts the transmit system. + * This routine copies from a user buffer into a socket, + * and starts the transmit system. */ -static int -tcp_write(struct sock *sk, unsigned char *from, - int len, int nonblock, unsigned flags) + +static int tcp_write(struct sock *sk, unsigned char *from, + int len, int nonblock, unsigned flags) { - int copied = 0; - int copy; - int tmp; - struct sk_buff *skb; - unsigned char *buff; - struct proto *prot; - struct device *dev = NULL; - - DPRINTF((DBG_TCP, "tcp_write(sk=%X, from=%X, len=%d, nonblock=%d, flags=%X)\n", + int copied = 0; + int copy; + int tmp; + struct sk_buff *skb; + unsigned char *buff; + struct proto *prot; + struct device *dev = NULL; + + DPRINTF((DBG_TCP, "tcp_write(sk=%X, from=%X, len=%d, nonblock=%d, flags=%X)\n", sk, from, len, nonblock, flags)); - sk->inuse=1; - prot = sk->prot; - while(len > 0) { - if (sk->err) { /* Stop on an error */ - release_sock(sk); - if (copied) return(copied); - tmp = -sk->err; - sk->err = 0; - return(tmp); - } - - /* First thing we do is make sure that we are established. */ - if (sk->shutdown & SEND_SHUTDOWN) { - release_sock(sk); - sk->err = EPIPE; - if (copied) return(copied); - sk->err = 0; - return(-EPIPE); - } - - - /* Wait for a connection to finish. */ - - while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) { - if (sk->err) { + sk->inuse=1; + prot = sk->prot; + + while(len > 0) + { + if (sk->err) + { /* Stop on an error */ release_sock(sk); - if (copied) return(copied); + if (copied) + return(copied); + /* FIXME: An error occuring between these two instructions _is_ remotely + possible. This occurs throughout the old and new net code and needs + fixing one day */ tmp = -sk->err; sk->err = 0; return(tmp); } - if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) { + /* First thing we do is make sure that we are established. */ + if (sk->shutdown & SEND_SHUTDOWN) + { release_sock(sk); - DPRINTF((DBG_TCP, "tcp_write: return 1\n")); - if (copied) return(copied); + sk->err = EPIPE; + if (copied) + return(copied); + sk->err = 0; + return(-EPIPE); + } + - if (sk->err) { + /* Wait for a connection to finish. */ + + while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) + { + if (sk->err) + { + release_sock(sk); + if (copied) + return(copied); tmp = -sk->err; sk->err = 0; return(tmp); } - if (sk->keepopen) { - send_sig(SIGPIPE, current, 0); + if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) + { + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_write: return 1\n")); + if (copied) + return(copied); + + if (sk->err) + { + tmp = -sk->err; + sk->err = 0; + return(tmp); + } + + if (sk->keepopen) + { + send_sig(SIGPIPE, current, 0); + } + return(-EPIPE); } - return(-EPIPE); - } - if (nonblock || copied) { - release_sock(sk); - DPRINTF((DBG_TCP, "tcp_write: return 2\n")); - if (copied) return(copied); - return(-EAGAIN); - } + if (nonblock /*|| copied*/) + { + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_write: return 2\n")); + if (copied) + return(copied); + return(-EAGAIN); + } - release_sock(sk); - cli(); - if (sk->state != TCP_ESTABLISHED && - sk->state != TCP_CLOSE_WAIT && sk->err == 0) { - interruptible_sleep_on(sk->sleep); - if (current->signal & ~current->blocked) { - sti(); - DPRINTF((DBG_TCP, "tcp_write: return 3\n")); - if (copied) return(copied); - return(-ERESTARTSYS); + release_sock(sk); + cli(); + if (sk->state != TCP_ESTABLISHED && + sk->state != TCP_CLOSE_WAIT && sk->err == 0) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + DPRINTF((DBG_TCP, "tcp_write: return 3\n")); + if (copied) + return(copied); + return(-ERESTARTSYS); + } } + sk->inuse = 1; + sti(); } - sk->inuse = 1; - sti(); - } - - /* Now we need to check if we have a half built packet. */ - if (sk->send_tmp != NULL) { - /* If sk->mss has been changed this could cause problems. */ + /* + * When we finally arrive at this point we can consider transmission + */ - /* Add more stuff to the end of skb->len */ - skb = sk->send_tmp; - if (!(flags & MSG_OOB)) { - copy = min(sk->mss - skb->len + 128 + + /* + * Now we need to check if we have a half built packet. If so append to + * it. + */ + + if (sk->send_tmp != NULL) + { + copy=0; + /* Add more stuff to the end of skb->len */ + skb = sk->send_tmp; + if (!(flags & MSG_OOB)) + { + copy = min(sk->mss - skb->len + 128 + prot->max_header, len); - /* FIXME: this is really a bug. */ - if (copy <= 0) { - printk("TCP: **bug**: \"copy\" <= 0!!\n"); - copy = 0; - } - - memcpy_fromfs((unsigned char *)(skb+1) + skb->len, from, copy); - skb->len += copy; - from += copy; - copied += copy; - len -= copy; - sk->send_seq += copy; - } - - if (skb->len -(unsigned long)skb->h.th + - (unsigned long)(skb+1) >= sk->mss ||(flags & MSG_OOB)) { - tcp_send_partial(sk); - } - continue; - } + /* FIXME: this is really a bug. */ + if (copy <= 0) + { + printk("TCP: **bug**: \"copy\" <= 0!!\n"); + copy = 0; + } + + /* + * Add data to the packet + */ + + memcpy_fromfs((unsigned char *)(skb+1) + skb->len, from, copy); + skb->len += copy; + from += copy; + copied += copy; + len -= copy; + sk->send_seq += copy; + } - /* - * We also need to worry about the window. - * The smallest we will send is about 200 bytes. - * This is a bit sad for TCP/AMPR people running - * 196 byte windows! - FIXME - */ - copy = min(sk->mtu, diff(sk->window_seq, sk->send_seq)); - - /* FIXME: redundent check here. */ - if (copy < 200 || copy > sk->mtu) copy = sk->mtu; - copy = min(copy, len); - - /* We should really check the window here also. */ - if (sk->packets_out && copy < sk->mss && !(flags & MSG_OOB)) { - /* We will release the socket incase we sleep here. */ - release_sock(sk); - skb = (struct sk_buff *) prot->wmalloc(sk, - sk->mss + 128 + prot->max_header + - sizeof(*skb), 0, GFP_KERNEL); - sk->inuse = 1; - sk->send_tmp = skb; - if (skb != NULL) - skb->mem_len = sk->mss + 128 + prot->max_header + sizeof(*skb); - } else { - /* We will release the socket incase we sleep here. */ - release_sock(sk); - skb = (struct sk_buff *) prot->wmalloc(sk, - copy + prot->max_header + + /* + * Do we need to send it yet or can it wait for more data ? + */ + + if (skb->len -(unsigned long)skb->h.th + (unsigned long)(skb+1) >= sk->mss ||(flags & MSG_OOB) || copy==0) + { + tcp_send_partial(sk); + } + continue; + } + + /* + * We also need to worry about the window. + * The smallest we will send is about 200 bytes. + * This is a bit sad for TCP/AMPR people running + * 196 byte windows! - FIXME + */ + + copy = min(sk->mtu, diff(sk->window_seq, sk->send_seq)); + + /* FIXME: redundant check here. */ + if (copy < 200 || copy > sk->mtu) copy = sk->mtu; + copy = min(copy, len); + + /* + * We should really check the window here also. + * [Why ?, Alan] + */ + + if (sk->packets_out && copy < sk->mss && !(flags & MSG_OOB)) + { + /* We will release the socket in case we sleep here. */ + release_sock(sk); + + /* + * Grab a packet to suit the mss. + */ + + skb = (struct sk_buff *) prot->wmalloc(sk, + sk->mss + 128 + prot->max_header + sizeof(*skb), 0, GFP_KERNEL); - sk->inuse = 1; - if (skb != NULL) - skb->mem_len = copy+prot->max_header + sizeof(*skb); - } - - /* If we didn't get any memory, we need to sleep. */ - if (skb == NULL) { - if (nonblock /* || copied */) { + sk->inuse = 1; + sk->send_tmp = skb; + if (skb != NULL) + skb->mem_len = sk->mss + 128 + prot->max_header + sizeof(*skb); + } + else + { + /* We will release the socket in case we sleep here. */ release_sock(sk); - DPRINTF((DBG_TCP, "tcp_write: return 4\n")); - if (copied) return(copied); - return(-EAGAIN); + skb = (struct sk_buff *) prot->wmalloc(sk, + copy + prot->max_header + + sizeof(*skb), 0, GFP_KERNEL); + sk->inuse = 1; + if (skb != NULL) + skb->mem_len = copy+prot->max_header + sizeof(*skb); } - /* FIXME: here is another race condition. */ - tmp = sk->wmem_alloc; - release_sock(sk); - cli(); - /* Again we will try to avoid it. */ - if (tmp <= sk->wmem_alloc && - (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT) - && sk->err == 0) { - interruptible_sleep_on(sk->sleep); - if (current->signal & ~current->blocked) { - sti(); - DPRINTF((DBG_TCP, "tcp_write: return 5\n")); - if (copied) return(copied); - return(-ERESTARTSYS); + /* If we didn't get any memory, we need to sleep. */ + if (skb == NULL) + { + if (nonblock /* || copied */) + { + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_write: return 4\n")); + if (copied) + return(copied); + return(-EAGAIN); } - } - sk->inuse = 1; - sti(); - continue; - } - skb->mem_addr = skb; - skb->len = 0; - skb->sk = sk; - skb->free = 0; + /* FIXME: here is another race condition. */ + tmp = sk->wmem_alloc; + release_sock(sk); + cli(); + /* Again we will try to avoid it. */ + if (tmp <= sk->wmem_alloc && (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT) && sk->err == 0) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + DPRINTF((DBG_TCP, "tcp_write: return 5\n")); + if (copied) + return(copied); + return(-ERESTARTSYS); + } + } + sk->inuse = 1; + sti(); + continue; + } + + skb->mem_addr = skb; + skb->len = 0; + skb->sk = sk; + skb->free = 0; - buff =(unsigned char *)(skb+1); + buff =(unsigned char *)(skb+1); - /* - * FIXME: we need to optimize this. - * Perhaps some hints here would be good. - */ - tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, skb->mem_len); - if (tmp < 0 ) { - prot->wfree(sk, skb->mem_addr, skb->mem_len); - release_sock(sk); - DPRINTF((DBG_TCP, "tcp_write: return 6\n")); - if (copied) return(copied); - return(tmp); - } - skb->len += tmp; - skb->dev = dev; - buff += tmp; - skb->h.th =(struct tcphdr *) buff; - tmp = tcp_build_header((struct tcphdr *)buff, sk, len-copy); - if (tmp < 0) { - prot->wfree(sk, skb->mem_addr, skb->mem_len); - release_sock(sk); - DPRINTF((DBG_TCP, "tcp_write: return 7\n")); - if (copied) return(copied); - return(tmp); - } + /* + * FIXME: we need to optimize this. + * Perhaps some hints here would be good. + */ + + /* + * Begin putting the headers on the packet + */ + + tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, skb->mem_len, sk->ip_ttl,sk->ip_tos); + if (tmp < 0 ) + { + prot->wfree(sk, skb->mem_addr, skb->mem_len); + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_write: return 6\n")); + if (copied) + return(copied); + return(tmp); + } + + skb->len += tmp; + skb->dev = dev; + buff += tmp; + skb->h.th =(struct tcphdr *) buff; + + /* + * Put the TCP header in + */ + + tmp = tcp_build_header((struct tcphdr *)buff, sk, len-copy); + if (tmp < 0) + { + prot->wfree(sk, skb->mem_addr, skb->mem_len); + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_write: return 7\n")); + if (copied) + return(copied); + return(tmp); + } - if (flags & MSG_OOB) { - ((struct tcphdr *)buff)->urg = 1; - ((struct tcphdr *)buff)->urg_ptr = ntohs(copy); - } - skb->len += tmp; - memcpy_fromfs(buff+tmp, from, copy); + /* + * If this was OOB data it goes at the start of a packet + * with the urg flag and urg ptr set. + */ + + if (flags & MSG_OOB) + { + ((struct tcphdr *)buff)->urg = 1; + ((struct tcphdr *)buff)->urg_ptr = ntohs(copy); + } + + /* + * Set the length + */ + + skb->len += tmp; + + /* + * Insert user data + */ + + memcpy_fromfs(buff+tmp, from, copy); - from += copy; - copied += copy; - len -= copy; - skb->len += copy; - skb->free = 0; - sk->send_seq += copy; + from += copy; + copied += copy; + len -= copy; + skb->len += copy; + skb->free = 0; + sk->send_seq += copy; - if (sk->send_tmp != NULL) continue; + if (sk->send_tmp != NULL) + continue; + + /* + * Checksum + */ - tcp_send_check((struct tcphdr *)buff, sk->saddr, sk->daddr, + tcp_send_check((struct tcphdr *)buff, sk->saddr, sk->daddr, copy + sizeof(struct tcphdr), sk); - skb->h.seq = sk->send_seq; - if (after(sk->send_seq , sk->window_seq) || - sk->packets_out >= sk->cong_window) { - DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n", - sk->cong_window, sk->packets_out)); - DPRINTF((DBG_TCP, "sk->send_seq = %d, sk->window_seq = %d\n", - sk->send_seq, sk->window_seq)); - skb->next = NULL; - skb->magic = TCP_WRITE_QUEUE_MAGIC; - if (sk->wback == NULL) { - sk->wfront = skb; - } else { - sk->wback->next = skb; + /* + * Set the sequence number + */ + + skb->h.seq = sk->send_seq; + + /* + * Can we send (will it fit in the window and is our packet out count low + * enough. + */ + + if (after(sk->send_seq , sk->window_seq) || sk->packets_out >= sk->cong_window) + { + + /* + * Nope - add it to the write queue + */ + + DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n", + sk->cong_window, sk->packets_out)); + DPRINTF((DBG_TCP, "sk->send_seq = %d, sk->window_seq = %d\n", + sk->send_seq, sk->window_seq)); + skb->next = NULL; + skb->magic = TCP_WRITE_QUEUE_MAGIC; + if (sk->wback == NULL) + { + sk->wfront = skb; + } + else + { + sk->wback->next = skb; + } + sk->wback = skb; + } + else + { + /* + * Kick it our to IP for transmission (and maybe retransmission) + */ + + prot->queue_xmit(sk, dev, skb,0); } - sk->wback = skb; - } else { - prot->queue_xmit(sk, dev, skb,0); - } - } - sk->err = 0; - /* Avoid possible race on send_tmp - c/o Johannes Stille */ - if(sk->send_tmp && !sk->packets_out) - tcp_send_partial(sk); - /* -- */ - release_sock(sk); - DPRINTF((DBG_TCP, "tcp_write: return 8\n")); - return(copied); + } + sk->err = 0; + + /* Avoid possible race on send_tmp - c/o Johannes Stille */ + if(sk->send_tmp && sk->packets_out <sk->cong_window) + tcp_send_partial(sk); + /* -- */ + + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_write: return 8\n")); + return(copied); } -static int -tcp_sendto(struct sock *sk, unsigned char *from, +static int tcp_sendto(struct sock *sk, unsigned char *from, int len, int nonblock, unsigned flags, struct sockaddr_in *addr, int addr_len) { - struct sockaddr_in sin; - - if (addr_len < sizeof(sin)) return(-EINVAL); - memcpy_fromfs(&sin, addr, sizeof(sin)); - if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL); - if (sin.sin_port != sk->dummy_th.dest) return(-EINVAL); - if (sin.sin_addr.s_addr != sk->daddr) return(-EINVAL); - return(tcp_write(sk, from, len, nonblock, flags)); + struct sockaddr_in sin; + + if (addr_len < sizeof(sin)) + return(-EINVAL); + + memcpy_fromfs(&sin, addr, sizeof(sin)); + + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EINVAL); + if (sin.sin_port != sk->dummy_th.dest) + return(-EINVAL); + if (sin.sin_addr.s_addr != sk->daddr) + return(-EINVAL); + return(tcp_write(sk, from, len, nonblock, flags)); } static void tcp_read_wakeup(struct sock *sk) { - int tmp; - struct device *dev = NULL; - struct tcphdr *t1; - struct sk_buff *buff; - - DPRINTF((DBG_TCP, "in tcp read wakeup\n")); - if (!sk->ack_backlog) return; + int tmp; + struct device *dev = NULL; + struct tcphdr *t1; + struct sk_buff *buff; + + DPRINTF((DBG_TCP, "in tcp read wakeup\n")); + if (!sk->ack_backlog) + return; + + /* + * FIXME: we need to put code here to prevent this routine from + * being called. Being called once in a while is ok, so only check + * if this is the second time in a row. + */ + + /* + * We need to grab some memory, and put together an ack, + * and then put it into the queue to be sent. + * + * The current code is rather keen to do this. It ought to first look + * for more optimal options like kicking out the pending partial packet. + */ + + buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); + if (buff == NULL) + { + /* Try again real soon. */ + reset_timer(sk, TIME_WRITE, 10); + return; + } - /* - * FIXME: we need to put code here to prevent this routine from - * being called. Being called once in a while is ok, so only check - * if this is the second time in a row. - */ + buff->mem_addr = buff; + buff->mem_len = MAX_ACK_SIZE; + buff->len = sizeof(struct tcphdr); + buff->sk = sk; + + /* Put in the IP header and routing stuff. */ + tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE, sk->ip_ttl,sk->ip_tos); + if (tmp < 0) + { + buff->free=1; + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + return; + } + + buff->len += tmp; + t1 =(struct tcphdr *)((char *)(buff+1) +tmp); - /* - * We need to grab some memory, and put together an ack, - * and then put it into the queue to be sent. - */ - buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); - if (buff == NULL) { - /* Try again real soon. */ - reset_timer(sk, TIME_WRITE, 10); - return; - } - - buff->mem_addr = buff; - buff->mem_len = MAX_ACK_SIZE; - buff->len = sizeof(struct tcphdr); - buff->sk = sk; - - /* Put in the IP header and routing stuff. */ - tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE); - if (tmp < 0) { - buff->free=1; - sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); - return; - } - - buff->len += tmp; - t1 =(struct tcphdr *)((char *)(buff+1) +tmp); - - memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); - t1->seq = ntohl(sk->send_seq); - t1->ack = 1; - t1->res1 = 0; - t1->res2 = 0; - t1->rst = 0; - t1->urg = 0; - t1->syn = 0; - t1->psh = 0; - sk->ack_backlog = 0; - sk->bytes_rcv = 0; - sk->window = sk->prot->rspace(sk); - t1->window = ntohs(sk->window); - t1->ack_seq = ntohl(sk->acked_seq); - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); - sk->prot->queue_xmit(sk, dev, buff, 1); + /* + * Fill in the header. + */ + + memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); + t1->seq = ntohl(sk->send_seq); + t1->ack = 1; + t1->res1 = 0; + t1->res2 = 0; + t1->rst = 0; + t1->urg = 0; + t1->syn = 0; + t1->psh = 0; + sk->ack_backlog = 0; + sk->bytes_rcv = 0; + sk->window = 4096/*sk->prot->rspace(sk)*/; + t1->window = ntohs(sk->window); + t1->ack_seq = ntohl(sk->acked_seq); + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + + /* + * These go straight out and don't retransmit + */ + + sk->prot->queue_xmit(sk, dev, buff, 1); } /* - * FIXME: - * This routine frees used buffers. - * It should consider sending an ACK to let the - * other end know we now have a bigger window. + * FIXME: + * This routine frees used buffers. + * It should consider sending an ACK to let the + * other end know we now have a bigger window. */ -static void -cleanup_rbuf(struct sock *sk) + +static void cleanup_rbuf(struct sock *sk) { - unsigned long flags; - int left; - struct sk_buff *skb; + unsigned long flags; + int left; + struct sk_buff *skb; - if(sk->debug) - printk("cleaning rbuf for sk=%p\n", sk); + if(sk->debug) + printk("cleaning rbuf for sk=%p\n", sk); - save_flags(flags); - cli(); + save_flags(flags); + cli(); - left = sk->prot->rspace(sk); + left = sk->prot->rspace(sk); - /* - * We have to loop through all the buffer headers, - * and try to free up all the space we can. - */ - while((skb=skb_peek(&sk->rqueue)) != NULL ) - { - if (!skb->used) - break; - skb_unlink(skb); - skb->sk = sk; - kfree_skb(skb, FREE_READ); - } + /* + * We have to loop through all the buffer headers, + * and try to free up all the space we can. + */ - restore_flags(flags); + while((skb=skb_peek(&sk->rqueue)) != NULL ) + { + if (!skb->used) + break; + skb_unlink(skb); + skb->sk = sk; + kfree_skb(skb, FREE_READ); + } - /* - * FIXME: - * At this point we should send an ack if the difference - * in the window, and the amount of space is bigger than - * TCP_WINDOW_DIFF. - */ - DPRINTF((DBG_TCP, "sk->window left = %d, sk->prot->rspace(sk)=%d\n", + restore_flags(flags); + + /* + * FIXME: + * At this point we should send an ack if the difference + * in the window, and the amount of space is bigger than + * TCP_WINDOW_DIFF. + */ + DPRINTF((DBG_TCP, "sk->window left = %d, sk->prot->rspace(sk)=%d\n", sk->window - sk->bytes_rcv, sk->prot->rspace(sk))); - if(sk->debug) - printk("sk->rspace = %lu, was %d\n", sk->prot->rspace(sk), - left); - if (sk->prot->rspace(sk) != left) - { - /* - * This area has caused the most trouble. The current strategy - * is to simply do nothing if the other end has room to send at - * least 3 full packets, because the ack from those will auto- - * matically update the window. If the other end doesn't think - * we have much space left, but we have room for atleast 1 more - * complete packet than it thinks we do, we will send an ack - * immediatedly. Otherwise we will wait up to .5 seconds in case - * the user reads some more. - */ - sk->ack_backlog++; - if ((sk->prot->rspace(sk) > (sk->window - sk->bytes_rcv + sk->mtu))) { - /* Send an ack right now. */ - tcp_read_wakeup(sk); - } else { - /* Force it to send an ack soon. */ - int was_active = del_timer(&sk->timer); - if (!was_active || TCP_ACK_TIME < sk->timer.expires) { - reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); - } else - add_timer(&sk->timer); - } - } + if(sk->debug) + printk("sk->rspace = %lu, was %d\n", sk->prot->rspace(sk),left); + if (sk->prot->rspace(sk) != left) + { + /* + * This area has caused the most trouble. The current strategy + * is to simply do nothing if the other end has room to send at + * least 3 full packets, because the ack from those will auto- + * matically update the window. If the other end doesn't think + * we have much space left, but we have room for atleast 1 more + * complete packet than it thinks we do, we will send an ack + * immediatedly. Otherwise we will wait up to .5 seconds in case + * the user reads some more. + * + * Changes this to reflect real windows - Alan + */ + sk->ack_backlog++; + if (4096-diff(sk->acked_seq,sk->copied_seq) - sk->bytes_rcv < 3*sk->mtu) + { + /* Send an ack right now. */ + tcp_read_wakeup(sk); + } + else + { + /* Force it to send an ack soon. */ + int was_active = del_timer(&sk->timer); + if (!was_active || TCP_ACK_TIME < sk->timer.expires) + { + reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); + } + else + add_timer(&sk->timer); + } + } } -/* Handle reading urgent data. */ -static int -tcp_read_urg(struct sock * sk, int nonblock, +/* + * Handle reading urgent data. + */ + +static int tcp_read_urg(struct sock * sk, int nonblock, unsigned char *to, int len, unsigned flags) { - int copied = 0; - struct sk_buff *skb; + int copied = 0; + struct sk_buff *skb; + int err; - DPRINTF((DBG_TCP, "tcp_read_urg(sk=%X, to=%X, len=%d, flags=%X)\n", + DPRINTF((DBG_TCP, "tcp_read_urg(sk=%X, to=%X, len=%d, flags=%X)\n", sk, to, len, flags)); - while(len > 0) - { - sk->inuse = 1; - while(sk->urg==0 || skb_peek(&sk->rqueue) == NULL) { - if (sk->err) { - int tmp; + err=verify_area(VERIFY_WRITE,to,len); + if(err) + return err; + + while(len > 0) + { + sk->inuse = 1; + while(sk->urg==0 || skb_peek(&sk->rqueue) == NULL) + { + if (sk->err) + { + int tmp; - release_sock(sk); - if (copied) return(copied); - tmp = -sk->err; - sk->err = 0; - return(tmp); - } + release_sock(sk); + if (copied) + return(copied); + tmp = -sk->err; + sk->err = 0; + return(tmp); + } - if (sk->state == TCP_CLOSE || sk->done) { - release_sock(sk); - if (copied) return(copied); - if (!sk->done) { - sk->done = 1; - return(0); + if (sk->state == TCP_CLOSE || sk->done) + { + release_sock(sk); + if (copied) + return(copied); + if (!sk->done) + { + sk->done = 1; + return(0); + } + return(-ENOTCONN); } - return(-ENOTCONN); - } - if (sk->shutdown & RCV_SHUTDOWN) { - release_sock(sk); - if (copied == 0) - sk->done = 1; - return(copied); - } + if (sk->shutdown & RCV_SHUTDOWN) + { + release_sock(sk); + if (copied == 0) + sk->done = 1; + return(copied); + } - if (nonblock || copied) { - release_sock(sk); - if (copied) return(copied); - return(-EAGAIN); - } + if (nonblock || copied) + { + release_sock(sk); + if (copied) + return(copied); + return(-EAGAIN); + } - /* Now at this point, we may have gotten some data. */ - release_sock(sk); - cli(); - if ((sk->urg == 0 || skb_peek(&sk->rqueue) == NULL) && - sk->err == 0 && !(sk->shutdown & RCV_SHUTDOWN)) { - interruptible_sleep_on(sk->sleep); - if (current->signal & ~current->blocked) { - sti(); - if (copied) return(copied); - return(-ERESTARTSYS); + /* Now at this point, we may have gotten some data. */ + release_sock(sk); + cli(); + if ((sk->urg == 0 || skb_peek(&sk->rqueue) == NULL) && + sk->err == 0 && !(sk->shutdown & RCV_SHUTDOWN)) + { + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + if (copied) + return(copied); + return(-ERESTARTSYS); + } } + sk->inuse = 1; + sti(); } - sk->inuse = 1; - sti(); - } - skb = skb_peek(&sk->rqueue); - do { - int amt; + skb = skb_peek(&sk->rqueue); + do + { + int amt; - if (skb->h.th->urg && !skb->urg_used) { - if (skb->h.th->urg_ptr == 0) { - skb->h.th->urg_ptr = ntohs(skb->len); - } - amt = min(ntohs(skb->h.th->urg_ptr),len); - if(amt) + if (skb->h.th->urg && !skb->urg_used) { - verify_area(VERIFY_WRITE, to, amt); - memcpy_tofs(to,(unsigned char *)(skb->h.th) + - skb->h.th->doff*4, amt); - } + if (skb->h.th->urg_ptr == 0) + { + skb->h.th->urg_ptr = ntohs(skb->len); + } + amt = min(ntohs(skb->h.th->urg_ptr),len); + if(amt) + { + verify_area(VERIFY_WRITE, to, amt); + memcpy_tofs(to,(unsigned char *)(skb->h.th) + + skb->h.th->doff*4, amt); + } - if (!(flags & MSG_PEEK)) { - skb->urg_used = 1; - sk->urg--; + if (!(flags & MSG_PEEK)) + { + skb->urg_used = 1; + sk->urg--; + } + release_sock(sk); + copied += amt; + return(copied); } - release_sock(sk); - copied += amt; - return(copied); - } - skb =(struct sk_buff *)skb->next; - } while(skb != sk->rqueue); - } - sk->urg = 0; - release_sock(sk); - return(0); + skb =(struct sk_buff *)skb->next; + } + while(skb != sk->rqueue); + } + sk->urg = 0; + release_sock(sk); + return(0); } -/* This routine copies from a sock struct into the user buffer. */ -static int -tcp_read(struct sock *sk, unsigned char *to, +/* + * This routine copies from a sock struct into the user buffer. + */ + +static int tcp_read_data(int type,struct sock *sk, unsigned char *to, int len, int nonblock, unsigned flags) { - int copied=0; /* will be used to say how much has been copied. */ - struct sk_buff *skb; - unsigned long offset; - unsigned long used; - int err; - - if (len == 0) return(0); - if (len < 0) { - return(-EINVAL); - } + /* Type is 0 for read, 1 for recv()/recvfrom() */ + + int copied=0; /* will be used to say how much has been copied. */ + struct sk_buff *skb; + unsigned long offset; + unsigned long used; + int err; + + if (len == 0) + return(0); + if (len < 0) + { + return(-EINVAL); + } - err=verify_area(VERIFY_WRITE,to,len); - if(err) - return err; + err=verify_area(VERIFY_WRITE,to,len); + if(err) + return err; - /* This error should be checked. */ - if (sk->state == TCP_LISTEN) return(-ENOTCONN); + /* This error should be checked. */ + if (sk->state == TCP_LISTEN) + return(-ENOTCONN); - /* Urgent data needs to be handled specially. */ - if ((flags & MSG_OOB)) - return(tcp_read_urg(sk, nonblock, to, len, flags)); + /* Urgent data needs to be handled specially. */ + if ((flags & MSG_OOB)) + return(tcp_read_urg(sk, nonblock, to, len, flags)); - /* So no-one else will use this socket. */ - sk->inuse = 1; + /* So no-one else will use this socket. */ + sk->inuse = 1; - skb=skb_peek(&sk->rqueue); - - DPRINTF((DBG_TCP, "tcp_read(sk=%X, to=%X, len=%d, nonblock=%d, flags=%X)\n", + skb=skb_peek(&sk->rqueue); + + DPRINTF((DBG_TCP, "tcp_read(sk=%X, to=%X, len=%d, nonblock=%d, flags=%X)\n", sk, to, len, nonblock, flags)); - while(len > 0) { - /* skb->used just checks to see if we've gone all the way around. */ + while(len > 0) + { + /* skb->used just checks to see if we've gone all the way around. */ - /* While no data, or first data indicates some is missing, or data is used */ - while(skb == NULL || - before(sk->copied_seq+1, skb->h.th->seq) || skb->used) { - DPRINTF((DBG_TCP, "skb = %X:\n", skb)); - cleanup_rbuf(sk); - if (sk->err) + /* While no data, or first data indicates some is missing, or data is used */ + while(skb == NULL || before(sk->copied_seq+1, skb->h.th->seq) || skb->used) { - int tmp; + DPRINTF((DBG_TCP, "skb = %X:\n", skb)); + + /* + * Clean up anything we can + */ + cleanup_rbuf(sk); + + /* + * If an error has come in off the net report it + */ + if (sk->err) + { + int tmp; + + release_sock(sk); + if (copied) + { + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", + copied)); + return(copied); + } + tmp = -sk->err; + sk->err = 0; + return(tmp); + } - release_sock(sk); - if (copied) + /* + * If we have become closed + */ + if (sk->state == TCP_CLOSE) { - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", + release_sock(sk); + if (copied) + { + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); - return(copied); + return(copied); + } + if (!sk->done) + { + sk->done = 1; + return(0); + } + return(-ENOTCONN); } - tmp = -sk->err; - sk->err = 0; - return(tmp); - } - if (sk->state == TCP_CLOSE) - { - release_sock(sk); - if (copied) { - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", - copied)); + /* + * If we have been shutdown + */ + + if (sk->shutdown & RCV_SHUTDOWN) + { + release_sock(sk); + if (copied == 0) + sk->done = 1; + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); return(copied); } - if (!sk->done) { - sk->done = 1; - return(0); + + /* + * If we don't want to wait. + */ + + if (nonblock || copied) /* Altered AC 24/12/93 */ + { + release_sock(sk); + if(sk->debug) + printk("read: EAGAIN\n"); + if (copied) + { + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", + copied)); + return(copied); + } + return(-EAGAIN); } - return(-ENOTCONN); - } - - if (sk->shutdown & RCV_SHUTDOWN) - { - release_sock(sk); - if (copied == 0) sk->done = 1; - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); - return(copied); - } - if (nonblock || copied) - { - release_sock(sk); - if(sk->debug) - printk("read: EAGAIN\n"); - if (copied) + /* + * Peeking doesn't wait around + */ + + if ((flags & MSG_PEEK) && copied != 0) { - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", - copied)); + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); return(copied); } - return(-EAGAIN); - } - - if ((flags & MSG_PEEK) && copied != 0) - { + + DPRINTF((DBG_TCP, "tcp_read about to sleep. state = %d\n", + sk->state)); release_sock(sk); - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); - return(copied); - } - - DPRINTF((DBG_TCP, "tcp_read about to sleep. state = %d\n", - sk->state)); - release_sock(sk); + + /* + * Now we may have some data waiting or we could + * have changed state. + */ + cli(); + + /* + * We just missed an event. Go round and + * reprocess it. This happens normally because + * release_sock will catch up on the backlog + * queue. + */ + + if (sk->shutdown & RCV_SHUTDOWN || sk->err != 0) + { + sk->inuse = 1; + sti(); + continue; + } + + /* + * Anything present, if so make sure it fits at our current position + */ - /* - * Now we may have some data waiting or we could - * have changed state. - */ - cli(); - if (sk->shutdown & RCV_SHUTDOWN || sk->err != 0) { + if (skb_peek(&sk->rqueue) == NULL || before(sk->copied_seq+1, sk->rqueue->h.th->seq)) + { + if(sk->debug) + printk("Read wait sleep\n"); + interruptible_sleep_on(sk->sleep); + if(sk->debug) + printk("Read wait wakes\n"); + if (current->signal & ~current->blocked) + { + sti(); + if (copied) + { + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", + copied)); + return(copied); + } + return(-ERESTARTSYS); + } + } sk->inuse = 1; sti(); - continue; - } + DPRINTF((DBG_TCP, "tcp_read woke up. \n")); - if (skb_peek(&sk->rqueue) == NULL || - before(sk->copied_seq+1, sk->rqueue->h.th->seq)) { - if(sk->debug) - printk("Read wait sleep\n"); - interruptible_sleep_on(sk->sleep); - if(sk->debug) - printk("Read wait wakes\n"); - if (current->signal & ~current->blocked) { - sti(); - if (copied) { - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", - copied)); - return(copied); - } - return(-ERESTARTSYS); - } - } - sk->inuse = 1; - sti(); - DPRINTF((DBG_TCP, "tcp_read woke up. \n")); + /* + * Grab the first packet pointer + */ - skb=skb_peek(&sk->rqueue); - /* That may have been null if we were beaten, if so we loop again */ - } + skb=skb_peek(&sk->rqueue); + + /* + * That may have been null if we were beaten, if so we loop again + */ + } - /* - * Copy anything from the current block that needs - * to go into the user buffer. - */ - offset = sk->copied_seq+1 - skb->h.th->seq; - - if (skb->h.th->syn) offset--; - if (offset < skb->len) /* Some of the packet is useful */ - { /* - * If there is urgent data we must either - * return or skip over it. + * Copy anything from the current block that needs + * to go into the user buffer. */ - if (skb->h.th->urg) - { - if (skb->urg_used) + + offset = sk->copied_seq+1 - skb->h.th->seq; + + if (skb->h.th->syn) + offset--; + + if (offset < skb->len) /* Some of the packet is useful */ + { + /* + * If there is urgent data we must either + * return or skip over it. + */ + if (skb->h.th->urg) { - sk->copied_seq += ntohs(skb->h.th->urg_ptr); - offset += ntohs(skb->h.th->urg_ptr); - if (offset >= skb->len) + if (skb->urg_used) { - skb->used = 1; - skb =(struct sk_buff *)skb->next; - continue; + sk->copied_seq += ntohs(skb->h.th->urg_ptr); + offset += ntohs(skb->h.th->urg_ptr); + if (offset >= skb->len) + { + skb->used = 1; + skb =(struct sk_buff *)skb->next; + continue; + } + } + else + { + release_sock(sk); + if (copied) + return(copied); + + /* + * This is technically wrong. What do we do if SIGURG is + * being ignored. EINTR as a return is certainly wrong. + */ + + send_sig(SIGURG, current, 0); + return(-EINTR); } - } - else - { - release_sock(sk); - if (copied) - return(copied); - send_sig(SIGURG, current, 0); - return(-EINTR); } - } - /* Ok so how much can we use ? */ - used = min(skb->len - offset, len); - /* Copy it */ - memcpy_tofs(to,((unsigned char *)skb->h.th) + - skb->h.th->doff*4 + offset, used); - copied += used; - len -= used; - to += used; + + /* + * Ok so how much can we use ? + */ + used = min(skb->len - offset, len); + /* + * Copy it + */ + memcpy_tofs(to,((unsigned char *)skb->h.th) + + skb->h.th->doff*4 + offset, used); + copied += used; + len -= used; + to += used; - /* If we were reading the data is 'eaten' */ - if (!(flags & MSG_PEEK)) - sk->copied_seq += used; + /* + * If we were reading the data is 'eaten' + */ + + if (!(flags & MSG_PEEK)) + sk->copied_seq += used; - /* - * Mark this data used if we are really reading it, - * and if it doesn't contain any urgent data. And we - * have used all the data. - */ - if (!(flags & MSG_PEEK) && - (!skb->h.th->urg || skb->urg_used) && - (used + offset >= skb->len)) - skb->used = 1; + /* + * Mark this data used if we are really reading it, + * and if it doesn't contain any urgent data. And we + * have used all the data. + */ + + if (!(flags & MSG_PEEK) && (!skb->h.th->urg || skb->urg_used) && (used + offset >= skb->len)) + skb->used = 1; - /* - * See if this is the end of a message or if the - * remaining data is urgent. - */ - if (/*skb->h.th->psh || */skb->h.th->urg) - { - break; + /* + * See if this is the end of a message or if the + * remaining data is urgent. + */ + + if ((skb->h.th->psh && type) || skb->h.th->urg) + { + break; + } + } + else + { /* + * already used this data, must be a retransmit + */ + skb->used = 1; } - } - else - { /* already used this data, must be a retransmit */ - skb->used = 1; - } - /* Move along a packet */ - skb =(struct sk_buff *)skb->next; - } - /* Clean up data we have read: This will do ACK frames */ - cleanup_rbuf(sk); - release_sock(sk); - DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); - if (copied == 0 && nonblock) - return(-EAGAIN); - return(copied); + + /* + * Move along a packet. We might reach the end if so we will wait + * for more. + */ + + skb =(struct sk_buff *)skb->next; + } + + + /* + * Clean up data we have read: This will do ACK frames + */ + + cleanup_rbuf(sk); + release_sock(sk); + DPRINTF((DBG_TCP, "tcp_read: returning %d\n", copied)); + if (copied == 0 && nonblock) + return(-EAGAIN); + return(copied); } +/* + * The read() user function. Read data, block and don't care + * about PSH and no partial reads + */ + +static int tcp_read(struct sock *sk, unsigned char *to, + int len, int nonblock, unsigned flags) +{ + return(tcp_read_data(1,sk,to,len,nonblock,flags)); +} + /* - * Send a FIN without closing the connection. - * Not called at interrupt time. + * Send a FIN without closing the connection. + * Not called at interrupt time. */ -void -tcp_shutdown(struct sock *sk, int how) + +void tcp_shutdown(struct sock *sk, int how) { - struct sk_buff *buff; - struct tcphdr *t1, *th; - struct proto *prot; - int tmp; - struct device *dev = NULL; + struct sk_buff *buff; + struct tcphdr *t1, *th; + struct proto *prot; + int tmp; + struct device *dev = NULL; /* * We need to grab some memory, and put together a FIN, @@ -1460,179 +1786,204 @@ tcp_shutdown(struct sock *sk, int how) * Most of this is guesswork, so maybe it will work... */ /* If we've already sent a FIN, return. */ - if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) return; - if (!(how & SEND_SHUTDOWN)) return; - sk->inuse = 1; - - /* Clear out any half completed packets. */ - if (sk->send_tmp) tcp_send_partial(sk); - - prot =(struct proto *)sk->prot; - th =(struct tcphdr *)&sk->dummy_th; - release_sock(sk); /* incase the malloc sleeps. */ - buff = (struct sk_buff *) prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL); - if (buff == NULL) return; - sk->inuse = 1; - - DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff)); - buff->mem_addr = buff; - buff->mem_len = MAX_RESET_SIZE; - buff->sk = sk; - buff->len = sizeof(*t1); - t1 =(struct tcphdr *)(buff + 1); - - /* Put in the IP header and routing stuff. */ - tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, - sizeof(struct tcphdr)); - if (tmp < 0) { - buff->free=1; - prot->wfree(sk,buff->mem_addr, buff->mem_len); - release_sock(sk); - DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); - return; - } - - t1 =(struct tcphdr *)((char *)t1 +tmp); - buff ->len += tmp; - buff->dev = dev; - memcpy(t1, th, sizeof(*t1)); - t1->seq = ntohl(sk->send_seq); - sk->send_seq++; - buff->h.seq = sk->send_seq; - t1->ack = 1; - t1->ack_seq = ntohl(sk->acked_seq); - t1->window = ntohs(sk->prot->rspace(sk)); - t1->fin = 1; - t1->rst = 0; - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) + return; + if (!(how & SEND_SHUTDOWN)) + return; + sk->inuse = 1; - /* - * Can't just queue this up. - * It should go at the end of the write queue. - */ - if (sk->wback != NULL) { - buff->free=0; - buff->next = NULL; - sk->wback->next = buff; - sk->wback = buff; - buff->magic = TCP_WRITE_QUEUE_MAGIC; - } else { - sk->prot->queue_xmit(sk, dev, buff, 0); - } - - if (sk->state == TCP_ESTABLISHED) sk->state = TCP_FIN_WAIT1; - else sk->state = TCP_FIN_WAIT2; - - release_sock(sk); + /* Clear out any half completed packets. */ + if (sk->send_tmp) + tcp_send_partial(sk); + + prot =(struct proto *)sk->prot; + th =(struct tcphdr *)&sk->dummy_th; + release_sock(sk); /* incase the malloc sleeps. */ + buff = (struct sk_buff *) prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL); + + if (buff == NULL) + return; + + sk->inuse = 1; + + DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff)); + buff->mem_addr = buff; + buff->mem_len = MAX_RESET_SIZE; + buff->sk = sk; + buff->len = sizeof(*t1); + t1 =(struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, + sizeof(struct tcphdr), + sk->ip_ttl,sk->ip_tos + ); + if (tmp < 0) + { + buff->free=1; + prot->wfree(sk,buff->mem_addr, buff->mem_len); + release_sock(sk); + DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); + return; + } + + t1 =(struct tcphdr *)((char *)t1 +tmp); + buff ->len += tmp; + buff->dev = dev; + memcpy(t1, th, sizeof(*t1)); + t1->seq = ntohl(sk->send_seq); + sk->send_seq++; + buff->h.seq = sk->send_seq; + t1->ack = 1; + t1->ack_seq = ntohl(sk->acked_seq); + t1->window = ntohs(sk->prot->rspace(sk)); + t1->fin = 1; + t1->rst = 0; + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + + /* + * Can't just queue this up. + * It should go at the end of the write queue. + */ + + if (sk->wback != NULL) + { + buff->free=0; + buff->next = NULL; + sk->wback->next = buff; + sk->wback = buff; + buff->magic = TCP_WRITE_QUEUE_MAGIC; + } + else + { + sk->prot->queue_xmit(sk, dev, buff, 0); + } + + if (sk->state == TCP_ESTABLISHED) + sk->state = TCP_FIN_WAIT1; + else + sk->state = TCP_FIN_WAIT2; + + release_sock(sk); } -static int -tcp_recvfrom(struct sock *sk, unsigned char *to, +static int tcp_recvfrom(struct sock *sk, unsigned char *to, int to_len, int nonblock, unsigned flags, struct sockaddr_in *addr, int *addr_len) { - struct sockaddr_in sin; - int len; - int err; - int result; + struct sockaddr_in sin; + int len; + int err; + int result; - /* Have to check these first unlike the old code. If - we check them after we lose data on an error - which is wrong */ - err = verify_area(VERIFY_WRITE,addr_len,sizeof(long)); - if(err) - return err; - len = get_fs_long(addr_len); - if(len > sizeof(sin)) - len = sizeof(sin); - err=verify_area(VERIFY_WRITE, addr, len); - if(err) - return err; - - result=tcp_read(sk, to, to_len, nonblock, flags); - - if (result < 0) return(result); + /* + * Have to check these first unlike the old code. If + * we check them after we lose data on an error + * which is wrong + */ + + err = verify_area(VERIFY_WRITE,addr_len,sizeof(long)); + if(err) + return err; + len = get_fs_long(addr_len); + if(len > sizeof(sin)) + len = sizeof(sin); + err=verify_area(VERIFY_WRITE, addr, len); + if(err) + return err; + + result=tcp_read_data(1,sk, to, to_len, nonblock, flags); + + if (result < 0) + return(result); - sin.sin_family = AF_INET; - sin.sin_port = sk->dummy_th.dest; - sin.sin_addr.s_addr = sk->daddr; + sin.sin_family = AF_INET; + sin.sin_port = sk->dummy_th.dest; + sin.sin_addr.s_addr = sk->daddr; - memcpy_tofs(addr, &sin, len); - put_fs_long(len, addr_len); - return(result); + memcpy_tofs(addr, &sin, len); + put_fs_long(len, addr_len); + return(result); } -/* This routine will send an RST to the other tcp. */ -static void -tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th, +/* + * This routine will send an RST to the other tcp. + */ + +static void tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th, struct proto *prot, struct options *opt, struct device *dev) { - struct sk_buff *buff; - struct tcphdr *t1; - int tmp; + struct sk_buff *buff; + struct tcphdr *t1; + int tmp; /* * We need to grab some memory, and put together an RST, * and then put it into the queue to be sent. */ - buff = (struct sk_buff *) prot->wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC); - if (buff == NULL) - return; - - DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff)); - buff->mem_addr = buff; - buff->mem_len = MAX_RESET_SIZE; - buff->len = sizeof(*t1); - buff->sk = NULL; - buff->dev = dev; - - t1 =(struct tcphdr *)(buff + 1); - - /* Put in the IP header and routing stuff. */ - tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt, - sizeof(struct tcphdr)); - if (tmp < 0) { - buff->free = 1; - prot->wfree(NULL, buff->mem_addr, buff->mem_len); - return; - } - t1 =(struct tcphdr *)((char *)t1 +tmp); - buff->len += tmp; - memcpy(t1, th, sizeof(*t1)); - - /* Swap the send and the receive. */ - t1->dest = th->source; - t1->source = th->dest; - t1->rst = 1; - t1->window = 0; + buff = (struct sk_buff *) prot->wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC); + if (buff == NULL) + return; + + DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff)); + buff->mem_addr = buff; + buff->mem_len = MAX_RESET_SIZE; + buff->len = sizeof(*t1); + buff->sk = NULL; + buff->dev = dev; + + t1 =(struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt, + sizeof(struct tcphdr),255, IPTOS_RELIABILITY); + if (tmp < 0) + { + buff->free = 1; + prot->wfree(NULL, buff->mem_addr, buff->mem_len); + return; + } + t1 =(struct tcphdr *)((char *)t1 +tmp); + buff->len += tmp; + memcpy(t1, th, sizeof(*t1)); + + /* Swap the send and the receive. */ + t1->dest = th->source; + t1->source = th->dest; + t1->rst = 1; + t1->window = 0; - if(th->ack) - { - t1->ack=0; - t1->seq=th->ack_seq; - t1->ack_seq=0; - } - else - { - t1->ack=1; - if(!th->syn) - t1->ack_seq=htonl(th->seq); + /* + * Fill in the ack field etc + */ + + if(th->ack) + { + t1->ack=0; + t1->seq=th->ack_seq; + t1->ack_seq=0; + } else - t1->ack_seq=htonl(th->seq+1); - t1->seq=0; - } - - t1->syn = 0; - t1->urg = 0; - t1->fin = 0; - t1->psh = 0; - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, saddr, daddr, sizeof(*t1), NULL); - prot->queue_xmit(NULL, dev, buff, 1); + { + t1->ack=1; + if(!th->syn) + t1->ack_seq=htonl(th->seq); + else + t1->ack_seq=htonl(th->seq+1); + t1->seq=0; + } + + t1->syn = 0; + t1->urg = 0; + t1->fin = 0; + t1->psh = 0; + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, saddr, daddr, sizeof(*t1), NULL); + prot->queue_xmit(NULL, dev, buff, 1); } @@ -1640,96 +1991,102 @@ tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th, * Look for tcp options. Parses everything but only knows about MSS */ -static void -tcp_options(struct sock *sk, struct tcphdr *th) +static void tcp_options(struct sock *sk, struct tcphdr *th) { - unsigned char *ptr; - int length=(th->doff*4)-sizeof(struct tcphdr); - int mtuset=0; + unsigned char *ptr; + int length=(th->doff*4)-sizeof(struct tcphdr); + int mtuset=0; - ptr = (unsigned char *)(th + 1); + ptr = (unsigned char *)(th + 1); - while(length>0) - { - int opcode=*ptr++; - int opsize=*ptr++; - switch(opcode) - { - case TCPOPT_EOL: - return; - case TCPOPT_NOP: - length-=2; - continue; - - default: - if(opsize<=2) /* Avoid silly options looping forever */ + while(length>0) + { + int opcode=*ptr++; + int opsize=*ptr++; + switch(opcode) + { + case TCPOPT_EOL: return; - switch(opcode) - { - case TCPOPT_MSS: - if(opsize==4) - { - sk->mtu=min(sk->mtu,ntohs(*(unsigned short *)ptr)); - mtuset=1; - } - break; - /* Add other options here as people feel the urge to implement stuff like large windows */ - } - ptr+=opsize-2; - length-=opsize; + case TCPOPT_NOP: + length-=2; + continue; + + default: + if(opsize<=2) /* Avoid silly options looping forever */ + return; + switch(opcode) + { + case TCPOPT_MSS: + if(opsize==4) + { + sk->mtu=min(sk->mtu,ntohs(*(unsigned short *)ptr)); + mtuset=1; + } + break; + /* + * Add other options here as people feel the urge to implement stuff like large windows + */ + } + ptr+=opsize-2; + length-=opsize; + } } - } - if (!mtuset) - { - sk->mtu = min(sk->mtu, 576 - HEADER_SIZE); - return; - } + if (!mtuset) + { + sk->mtu = min(sk->mtu, 576 - HEADER_SIZE); + return; + } } /* - * This routine handles a connection request. - * It should make sure we haven't already responded. - * Because of the way BSD works, we have to send a syn/ack now. - * This also means it will be harder to close a socket which is - * listening. + * This routine handles a connection request. + * It should make sure we haven't already responded. + * Because of the way BSD works, we have to send a syn/ack now. + * This also means it will be harder to close a socket which is + * listening. */ -static void -tcp_conn_request(struct sock *sk, struct sk_buff *skb, + +static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, unsigned long daddr, unsigned long saddr, struct options *opt, struct device *dev) { - struct sk_buff *buff; - struct tcphdr *t1; - unsigned char *ptr; - struct sock *newsk; - struct tcphdr *th; - int tmp; - - DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n" - " opt = %X, dev = %X)\n", - sk, skb, daddr, saddr, opt, dev)); + struct sk_buff *buff; + struct tcphdr *t1; + unsigned char *ptr; + struct sock *newsk; + struct tcphdr *th; + int tmp; + + DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n" + " opt = %X, dev = %X)\n", + sk, skb, daddr, saddr, opt, dev)); - th = skb->h.th; - - /* If the socket is dead, don't accept the connection. */ - if (!sk->dead) { - wake_up(sk->sleep); - } else { - DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n")); - tcp_reset(daddr, saddr, th, sk->prot, opt, dev); - kfree_skb(skb, FREE_READ); - return; - } + th = skb->h.th; + + /* If the socket is dead, don't accept the connection. */ + if (!sk->dead) + { + sk->data_ready(sk,0); + } + else + { + DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n")); + tcp_reset(daddr, saddr, th, sk->prot, opt, dev); + kfree_skb(skb, FREE_READ); + return; + } /* * Make sure we can accept more. This will prevent a * flurry of syns from eating up all our memory. */ - if (sk->ack_backlog >= sk->max_ack_backlog) { - kfree_skb(skb, FREE_READ); - return; - } + + if (sk->ack_backlog >= sk->max_ack_backlog) + { + kfree_skb(skb, FREE_READ); + return; + } /* * We need to build a new sock struct. @@ -1738,343 +2095,360 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb, * and if the listening socket is destroyed before this is taken * off of the queue, this will take care of it. */ - newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC); - if (newsk == NULL) { - /* just ignore the syn. It will get retransmitted. */ - kfree_skb(skb, FREE_READ); - return; - } - - DPRINTF((DBG_TCP, "newsk = %X\n", newsk)); - memcpy((void *)newsk,(void *)sk, sizeof(*newsk)); - newsk->wback = NULL; - newsk->wfront = NULL; - newsk->rqueue = NULL; - newsk->send_head = NULL; - newsk->send_tail = NULL; - newsk->back_log = NULL; - newsk->rtt = TCP_CONNECT_TIME; - newsk->mdev = 0; - newsk->backoff = 0; - newsk->blog = 0; - newsk->intr = 0; - newsk->proc = 0; - newsk->done = 0; - newsk->send_tmp = NULL; - newsk->pair = NULL; - newsk->wmem_alloc = 0; - newsk->rmem_alloc = 0; - - newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF; - - newsk->err = 0; - newsk->shutdown = 0; - newsk->ack_backlog = 0; - newsk->acked_seq = skb->h.th->seq+1; - newsk->fin_seq = skb->h.th->seq; - newsk->copied_seq = skb->h.th->seq; - newsk->state = TCP_SYN_RECV; - newsk->timeout = 0; - newsk->send_seq = jiffies * SEQ_TICK - seq_offset; - newsk->rcv_ack_seq = newsk->send_seq; - newsk->urg =0; - newsk->retransmits = 0; - newsk->destroy = 0; - newsk->timer.data = (unsigned long)newsk; - newsk->timer.function = &net_timer; - newsk->dummy_th.source = skb->h.th->dest; - newsk->dummy_th.dest = skb->h.th->source; - - /* Swap these two, they are from our point of view. */ - newsk->daddr = saddr; - newsk->saddr = daddr; - - put_sock(newsk->num,newsk); - newsk->dummy_th.res1 = 0; - newsk->dummy_th.doff = 6; - newsk->dummy_th.fin = 0; - newsk->dummy_th.syn = 0; - newsk->dummy_th.rst = 0; - newsk->dummy_th.psh = 0; - newsk->dummy_th.ack = 0; - newsk->dummy_th.urg = 0; - newsk->dummy_th.res2 = 0; - newsk->acked_seq = skb->h.th->seq + 1; - newsk->copied_seq = skb->h.th->seq; - -#ifdef OLDWAY - if (skb->h.th->doff == 5) { - newsk->mtu = dev->mtu - HEADER_SIZE; - } else { - ptr =(unsigned char *)(skb->h.th + 1); - if (ptr[0] != 2 || ptr[1] != 4) { - newsk->mtu = dev->mtu - HEADER_SIZE; - } else { - newsk->mtu = min(ptr[2] * 256 + ptr[3] - HEADER_SIZE, - dev->mtu - HEADER_SIZE); - } - } -#else - tcp_options(newsk,skb->h.th); -#endif - buff = (struct sk_buff *) newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC); - if (buff == NULL) { - sk->err = -ENOMEM; - newsk->dead = 1; - release_sock(newsk); - kfree_skb(skb, FREE_READ); - return; - } + newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC); + if (newsk == NULL) + { + /* just ignore the syn. It will get retransmitted. */ + kfree_skb(skb, FREE_READ); + return; + } + + DPRINTF((DBG_TCP, "newsk = %X\n", newsk)); + memcpy((void *)newsk,(void *)sk, sizeof(*newsk)); + newsk->wback = NULL; + newsk->wfront = NULL; + newsk->rqueue = NULL; + newsk->send_head = NULL; + newsk->send_tail = NULL; + newsk->back_log = NULL; + newsk->rtt = TCP_CONNECT_TIME; + newsk->mdev = 0; + newsk->backoff = 0; + newsk->blog = 0; + newsk->intr = 0; + newsk->proc = 0; + newsk->done = 0; + newsk->send_tmp = NULL; + newsk->pair = NULL; + newsk->wmem_alloc = 0; + newsk->rmem_alloc = 0; + + newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF; + + newsk->err = 0; + newsk->shutdown = 0; + newsk->ack_backlog = 0; + newsk->acked_seq = skb->h.th->seq+1; + newsk->fin_seq = skb->h.th->seq; + newsk->copied_seq = skb->h.th->seq; + newsk->state = TCP_SYN_RECV; + newsk->timeout = 0; + newsk->send_seq = jiffies * SEQ_TICK - seq_offset; + newsk->rcv_ack_seq = newsk->send_seq; + newsk->urg =0; + newsk->retransmits = 0; + newsk->destroy = 0; + newsk->timer.data = (unsigned long)newsk; + newsk->timer.function = &net_timer; + newsk->dummy_th.source = skb->h.th->dest; + newsk->dummy_th.dest = skb->h.th->source; + + /* Swap these two, they are from our point of view. */ + newsk->daddr = saddr; + newsk->saddr = daddr; + + put_sock(newsk->num,newsk); + newsk->dummy_th.res1 = 0; + newsk->dummy_th.doff = 6; + newsk->dummy_th.fin = 0; + newsk->dummy_th.syn = 0; + newsk->dummy_th.rst = 0; + newsk->dummy_th.psh = 0; + newsk->dummy_th.ack = 0; + newsk->dummy_th.urg = 0; + newsk->dummy_th.res2 = 0; + newsk->acked_seq = skb->h.th->seq + 1; + newsk->copied_seq = skb->h.th->seq; + + /* Grab the callers ttl and tos values and use them */ + newsk->ip_ttl=skb->ip_hdr->ttl; + newsk->ip_tos=skb->ip_hdr->tos; + + tcp_options(newsk,skb->h.th); + + buff = (struct sk_buff *) newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC); + if (buff == NULL) + { + sk->err = -ENOMEM; + newsk->dead = 1; + release_sock(newsk); + kfree_skb(skb, FREE_READ); + return; + } - buff->mem_addr = buff; - buff->mem_len = MAX_SYN_SIZE; - buff->len = sizeof(struct tcphdr)+4; - buff->sk = newsk; + buff->mem_addr = buff; + buff->mem_len = MAX_SYN_SIZE; + buff->len = sizeof(struct tcphdr)+4; + buff->sk = newsk; - t1 =(struct tcphdr *)(buff + 1); - - /* Put in the IP header and routing stuff. */ - tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev, - IPPROTO_TCP, NULL, MAX_SYN_SIZE); - - /* Something went wrong. */ - if (tmp < 0) { - sk->err = tmp; - buff->free=1; - kfree_skb(buff,FREE_WRITE); - newsk->dead = 1; - release_sock(newsk); - skb->sk = sk; - kfree_skb(skb, FREE_READ); - return; - } - - buff->len += tmp; - t1 =(struct tcphdr *)((char *)t1 +tmp); + t1 =(struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev, + IPPROTO_TCP, NULL, MAX_SYN_SIZE,newsk->ip_ttl,newsk->ip_tos); + + /* Something went wrong. */ + if (tmp < 0) + { + sk->err = tmp; + buff->free=1; + kfree_skb(buff,FREE_WRITE); + newsk->dead = 1; + release_sock(newsk); + skb->sk = sk; + kfree_skb(skb, FREE_READ); + return; + } + + /* + * Assemble a syn|ack frame in reply + */ + + buff->len += tmp; + t1 =(struct tcphdr *)((char *)t1 +tmp); - memcpy(t1, skb->h.th, sizeof(*t1)); - buff->h.seq = newsk->send_seq; - - /* Swap the send and the receive. */ - t1->dest = skb->h.th->source; - t1->source = newsk->dummy_th.source; - t1->seq = ntohl(newsk->send_seq++); - t1->ack = 1; - newsk->window = newsk->prot->rspace(newsk); - t1->window = ntohs(newsk->window); - t1->res1 = 0; - t1->res2 = 0; - t1->rst = 0; - t1->urg = 0; - t1->psh = 0; - t1->syn = 1; - t1->ack_seq = ntohl(skb->h.th->seq+1); - t1->doff = sizeof(*t1)/4+1; - - ptr =(unsigned char *)(t1+1); - ptr[0] = 2; - ptr[1] = 4; - ptr[2] =((dev->mtu - HEADER_SIZE) >> 8) & 0xff; - ptr[3] =(dev->mtu - HEADER_SIZE) & 0xff; - - tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk); - newsk->prot->queue_xmit(newsk, dev, buff, 0); - - reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME); - skb->sk = newsk; - - /* Charge the sock_buff to newsk. */ - sk->rmem_alloc -= skb->mem_len; - newsk->rmem_alloc += skb->mem_len; - - skb_queue_tail(&sk->rqueue,skb); - sk->ack_backlog++; - release_sock(newsk); + memcpy(t1, skb->h.th, sizeof(*t1)); + buff->h.seq = newsk->send_seq; + + /* Swap the send and the receive. */ + t1->dest = skb->h.th->source; + t1->source = newsk->dummy_th.source; + t1->seq = ntohl(newsk->send_seq++); + t1->ack = 1; + newsk->window = 4096/*newsk->prot->rspace(newsk)*/; + t1->window = ntohs(newsk->window); + t1->res1 = 0; + t1->res2 = 0; + t1->rst = 0; + t1->urg = 0; + t1->psh = 0; + t1->syn = 1; + t1->ack_seq = ntohl(skb->h.th->seq+1); + t1->doff = sizeof(*t1)/4+1; + + ptr =(unsigned char *)(t1+1); + ptr[0] = 2; + ptr[1] = 4; + ptr[2] =((dev->mtu - HEADER_SIZE) >> 8) & 0xff; + ptr[3] =(dev->mtu - HEADER_SIZE) & 0xff; + + tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk); + newsk->prot->queue_xmit(newsk, dev, buff, 0); + + reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME); + skb->sk = newsk; + + /* Charge the sock_buff to newsk. */ + sk->rmem_alloc -= skb->mem_len; + newsk->rmem_alloc += skb->mem_len; + + skb_queue_tail(&sk->rqueue,skb); + sk->ack_backlog++; + release_sock(newsk); } +/* + * Close a tcp connection + */ -static void -tcp_close(struct sock *sk, int timeout) +static void tcp_close(struct sock *sk, int timeout) { - struct sk_buff *buff; - int need_reset = 0; - struct tcphdr *t1, *th; - struct proto *prot; - struct device *dev=NULL; - int tmp; + struct sk_buff *buff; + int need_reset = 0; + struct tcphdr *t1, *th; + struct proto *prot; + struct device *dev=NULL; + int tmp; /* * We need to grab some memory, and put together a FIN, * and then put it into the queue to be sent. */ - DPRINTF((DBG_TCP, "tcp_close((struct sock *)%X, %d)\n",sk, timeout)); - sk->inuse = 1; - sk->keepopen = 1; - sk->shutdown = SHUTDOWN_MASK; - - if (!sk->dead) wake_up(sk->sleep); - - /* We need to flush the recv. buffs. */ - if (skb_peek(&sk->rqueue) != NULL) - { - struct sk_buff *skb; -#ifdef OLD - struct sk_buff *skb2; - skb = skb_peek(&sk->rqueue); - do { - skb2 =(struct sk_buff *)skb->next; - /* if there is some real unread data, send a reset. */ - if (skb->len > 0 && - after(skb->h.th->seq + skb->len + 1, sk->copied_seq)) - need_reset = 1; - kfree_skb(skb, FREE_WRITE); - skb = skb2; - } while(skb != sk->rqueue); -#else - if(sk->debug) - printk("Clean rcv queue\n"); - while((skb=skb_dequeue(&sk->rqueue))!=NULL) - { - if(skb->len > 0 && after(skb->h.th->seq + skb->len + 1 , sk->copied_seq)) - need_reset = 1; - kfree_skb(skb, FREE_READ); - } - if(sk->debug) - printk("Cleaned.\n"); -#endif - } - sk->rqueue = NULL; - - /* Get rid off any half-completed packets. */ - if (sk->send_tmp) { - tcp_send_partial(sk); - } - - switch(sk->state) { - case TCP_FIN_WAIT1: - case TCP_FIN_WAIT2: - case TCP_LAST_ACK: - /* start a timer. */ - reset_timer(sk, TIME_CLOSE, 4 * sk->rtt); - if (timeout) tcp_time_wait(sk); - release_sock(sk); - return; /* break causes a double release - messy */ - case TCP_TIME_WAIT: - if (timeout) { - sk->state = TCP_CLOSE; + DPRINTF((DBG_TCP, "tcp_close((struct sock *)%X, %d)\n",sk, timeout)); + sk->inuse = 1; + sk->keepopen = 1; + sk->shutdown = SHUTDOWN_MASK; + + + /* We need to flush the recv. buffs. */ + if (skb_peek(&sk->rqueue) != NULL) + { + struct sk_buff *skb; + if(sk->debug) + printk("Clean rcv queue\n"); + while((skb=skb_dequeue(&sk->rqueue))!=NULL) + { + if(skb->len > 0 && after(skb->h.th->seq + skb->len + 1 , sk->copied_seq)) + need_reset = 1; + kfree_skb(skb, FREE_READ); } - release_sock(sk); - return; - case TCP_LISTEN: - sk->state = TCP_CLOSE; - release_sock(sk); - return; - case TCP_CLOSE: - release_sock(sk); - return; - case TCP_CLOSE_WAIT: - case TCP_ESTABLISHED: - case TCP_SYN_SENT: - case TCP_SYN_RECV: - prot =(struct proto *)sk->prot; - th =(struct tcphdr *)&sk->dummy_th; - buff = (struct sk_buff *) prot->wmalloc(sk, MAX_FIN_SIZE, 1, GFP_ATOMIC); - if (buff == NULL) { - /* This will force it to try again later. */ - /* Or it would have if someone released the socket - first. Anyway it might work now */ + if(sk->debug) + printk("Cleaned.\n"); + } + sk->rqueue = NULL; + + /* Get rid off any half-completed packets. */ + if (sk->send_tmp) + { + tcp_send_partial(sk); + } + + switch(sk->state) + { + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: + case TCP_LAST_ACK: + /* start a timer. */ + reset_timer(sk, TIME_CLOSE, 4 * sk->rtt); + if (timeout) + tcp_time_wait(sk); release_sock(sk); - if (sk->state != TCP_CLOSE_WAIT) - sk->state = TCP_ESTABLISHED; - reset_timer(sk, TIME_CLOSE, 100); + if (!sk->dead) + sk->state_change(sk); + return; /* break causes a double release - messy */ + case TCP_TIME_WAIT: + if (timeout) + { + sk->state = TCP_CLOSE; + } + release_sock(sk); + if (!sk->dead) + sk->state_change(sk); return; - } - buff->mem_addr = buff; - buff->mem_len = MAX_FIN_SIZE; - buff->sk = sk; - buff->free = 1; - buff->len = sizeof(*t1); - t1 =(struct tcphdr *)(buff + 1); - - /* Put in the IP header and routing stuff. */ - tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, - sizeof(struct tcphdr)); - if (tmp < 0) { - kfree_skb(buff,FREE_WRITE); - DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); + case TCP_LISTEN: + sk->state = TCP_CLOSE; release_sock(sk); + if (!sk->dead) + sk->state_change(sk); return; - } - - t1 =(struct tcphdr *)((char *)t1 +tmp); - buff ->len += tmp; - buff->dev = dev; - memcpy(t1, th, sizeof(*t1)); - t1->seq = ntohl(sk->send_seq); - sk->send_seq++; - buff->h.seq = sk->send_seq; - t1->ack = 1; - - /* Ack everything immediately from now on. */ - sk->delay_acks = 0; - t1->ack_seq = ntohl(sk->acked_seq); - t1->window = ntohs(sk->prot->rspace(sk)); - t1->fin = 1; - t1->rst = need_reset; - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); - - if (sk->wfront == NULL) { - prot->queue_xmit(sk, dev, buff, 0); - } else { - reset_timer(sk, TIME_WRITE, - backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); - buff->next = NULL; - if (sk->wback == NULL) { - sk->wfront=buff; - } else { - sk->wback->next = buff; - } - sk->wback = buff; - buff->magic = TCP_WRITE_QUEUE_MAGIC; - } + case TCP_CLOSE: + release_sock(sk); + if (!sk->dead) + sk->state_change(sk); + return; + case TCP_CLOSE_WAIT: + case TCP_ESTABLISHED: + case TCP_SYN_SENT: + case TCP_SYN_RECV: + prot =(struct proto *)sk->prot; + th =(struct tcphdr *)&sk->dummy_th; + buff = (struct sk_buff *) prot->wmalloc(sk, MAX_FIN_SIZE, 1, GFP_ATOMIC); + if (buff == NULL) + { + /* This will force it to try again later. */ + /* Or it would have if someone released the socket + first. Anyway it might work now */ + release_sock(sk); + if (sk->state != TCP_CLOSE_WAIT) + sk->state = TCP_ESTABLISHED; + reset_timer(sk, TIME_CLOSE, 100); + return; + } + buff->mem_addr = buff; + buff->mem_len = MAX_FIN_SIZE; + buff->sk = sk; + buff->free = 1; + buff->len = sizeof(*t1); + t1 =(struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, + sizeof(struct tcphdr),sk->ip_ttl,sk->ip_tos); + + if (tmp < 0) + { + kfree_skb(buff,FREE_WRITE); + DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); + release_sock(sk); + if (!sk->dead) + sk->state_change(sk); + return; + } + + t1 =(struct tcphdr *)((char *)t1 +tmp); + buff ->len += tmp; + buff->dev = dev; + memcpy(t1, th, sizeof(*t1)); + t1->seq = ntohl(sk->send_seq); + sk->send_seq++; + buff->h.seq = sk->send_seq; + t1->ack = 1; + + /* Ack everything immediately from now on. */ + sk->delay_acks = 0; + t1->ack_seq = ntohl(sk->acked_seq); + t1->window = ntohs(sk->prot->rspace(sk)); + t1->fin = 1; + t1->rst = need_reset; + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + + if (sk->wfront == NULL) + { + prot->queue_xmit(sk, dev, buff, 0); + } + else + { + buff->free=0; + reset_timer(sk, TIME_WRITE,backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); + buff->next = NULL; + if (sk->wback == NULL) + { + sk->wfront=buff; + } + else + { + sk->wback->next = buff; + } + sk->wback = buff; + buff->magic = TCP_WRITE_QUEUE_MAGIC; + } - if (sk->state == TCP_CLOSE_WAIT) { - sk->state = TCP_FIN_WAIT2; - } else { - sk->state = TCP_FIN_WAIT1; - } - } - release_sock(sk); + if (sk->state == TCP_CLOSE_WAIT) + { + sk->state = TCP_FIN_WAIT2; + } + else + { + sk->state = TCP_FIN_WAIT1; + } + break; + } + if (!sk->dead) + sk->state_change(sk); + release_sock(sk); } /* - * This routine takes stuff off of the write queue, - * and puts it in the xmit queue. + * This routine takes stuff off of the write queue, + * and puts it in the xmit queue. */ -static void -tcp_write_xmit(struct sock *sk) + +static void tcp_write_xmit(struct sock *sk) { - struct sk_buff *skb; + struct sk_buff *skb; - DPRINTF((DBG_TCP, "tcp_write_xmit(sk=%X)\n", sk)); + DPRINTF((DBG_TCP, "tcp_write_xmit(sk=%X)\n", sk)); - /* The bytes will have to remain here. In time closedown will - empty the write queue and all will be happy */ - if(sk->zapped) - return; + /* The bytes will have to remain here. In time closedown will + empty the write queue and all will be happy */ + if(sk->zapped) + return; - while(sk->wfront != NULL && - before(sk->wfront->h.seq, sk->window_seq) && - sk->packets_out < sk->cong_window) { + while(sk->wfront != NULL && + before(sk->wfront->h.seq, sk->window_seq) && + sk->packets_out < sk->cong_window) + { skb = sk->wfront; IS_SKB(skb); sk->wfront =(struct sk_buff *)skb->next; - if (sk->wfront == NULL) sk->wback = NULL; + if (sk->wfront == NULL) + sk->wback = NULL; skb->next = NULL; - if (skb->magic != TCP_WRITE_QUEUE_MAGIC) { + if (skb->magic != TCP_WRITE_QUEUE_MAGIC) + { printk("tcp.c skb with bad magic(%X) on write queue. Squashing " "queue\n", skb->magic); sk->wfront = NULL; @@ -2085,11 +2459,15 @@ tcp_write_xmit(struct sock *sk) DPRINTF((DBG_TCP, "Sending a packet.\n")); /* See if we really need to send the packet. */ - if (before(skb->h.seq, sk->rcv_ack_seq +1)) { + if (before(skb->h.seq, sk->rcv_ack_seq +1)) + { sk->retransmits = 0; kfree_skb(skb, FREE_WRITE); - if (!sk->dead) wake_up(sk->sleep); - } else { + if (!sk->dead) + sk->write_space(sk); + } + else + { sk->prot->queue_xmit(sk, skb->dev, skb, skb->free); } } @@ -2097,67 +2475,79 @@ tcp_write_xmit(struct sock *sk) /* - * This routine sorts the send list, and resets the - * sk->send_head and sk->send_tail pointers. + * This routine sorts the send list, and resets the + * sk->send_head and sk->send_tail pointers. */ -void -sort_send(struct sock *sk) + +void sort_send(struct sock *sk) { - struct sk_buff *list = NULL; - struct sk_buff *skb,*skb2,*skb3; - - for (skb = sk->send_head; skb != NULL; skb = skb2) { - skb2 = (struct sk_buff *)skb->link3; - if (list == NULL || before (skb2->h.seq, list->h.seq)) { - skb->link3 = list; - sk->send_tail = skb; - list = skb; - } else { - for (skb3 = list; ; skb3 = (struct sk_buff *)skb3->link3) { - if (skb3->link3 == NULL || - before(skb->h.seq, skb3->link3->h.seq)) { - skb->link3 = skb3->link3; - skb3->link3 = skb; - if (skb->link3 == NULL) sk->send_tail = skb; - break; + struct sk_buff *list = NULL; + struct sk_buff *skb,*skb2,*skb3; + + for (skb = sk->send_head; skb != NULL; skb = skb2) + { + skb2 = (struct sk_buff *)skb->link3; + if (list == NULL || before (skb2->h.seq, list->h.seq)) + { + skb->link3 = list; + sk->send_tail = skb; + list = skb; + } + else + { + for (skb3 = list; ; skb3 = (struct sk_buff *)skb3->link3) + { + if (skb3->link3 == NULL || before(skb->h.seq, skb3->link3->h.seq)) + { + skb->link3 = skb3->link3; + skb3->link3 = skb; + if (skb->link3 == NULL) + sk->send_tail = skb; + break; + } } } - } - } - sk->send_head = list; + } + sk->send_head = list; } -/* This routine deals with incoming acks, but not outgoing ones. */ -static int -tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) +/* + * This routine deals with incoming acks, but not outgoing ones. + */ + +static int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) { - unsigned long ack; - int flag = 0; + unsigned long ack; + int flag = 0; - if(sk->zapped) - return(1); /* Dead, cant ack any more so why bother */ + if(sk->zapped) + return(1); /* Dead, cant ack any more so why bother */ - ack = ntohl(th->ack_seq); - DPRINTF((DBG_TCP, "tcp_ack ack=%d, window=%d, " - "sk->rcv_ack_seq=%d, sk->window_seq = %d\n", - ack, ntohs(th->window), sk->rcv_ack_seq, sk->window_seq)); + ack = ntohl(th->ack_seq); + DPRINTF((DBG_TCP, "tcp_ack ack=%d, window=%d, " + "sk->rcv_ack_seq=%d, sk->window_seq = %d\n", + ack, ntohs(th->window), sk->rcv_ack_seq, sk->window_seq)); - if (after(ack, sk->send_seq+1) || before(ack, sk->rcv_ack_seq-1)) { - if (after(ack, sk->send_seq) || - (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)) { - return(0); - } - if (sk->keepopen) { - reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); - } - return(1); - } + if (after(ack, sk->send_seq+1) || before(ack, sk->rcv_ack_seq-1)) + { + if (after(ack, sk->send_seq) || (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)) + { + return(0); + } + if (sk->keepopen) + { + reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + } + return(1); + } - if (len != th->doff*4) flag |= 1; + if (len != th->doff*4) + flag |= 1; - /* See if our window has been shrunk. */ - if (after(sk->window_seq, ack+ntohs(th->window))) { + /* See if our window has been shrunk. */ + if (after(sk->window_seq, ack+ntohs(th->window))) + { /* * We may need to move packets from the send queue * to the write queue, if the window has been shrunk on us. @@ -2165,271 +2555,312 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) * like this, but if the other end does, you must be able * to deal with it. */ - struct sk_buff *skb; - struct sk_buff *skb2; - struct sk_buff *wskb = NULL; + struct sk_buff *skb; + struct sk_buff *skb2; + struct sk_buff *wskb = NULL; - skb2 = sk->send_head; - sk->send_head = NULL; - sk->send_tail = NULL; + skb2 = sk->send_head; + sk->send_head = NULL; + sk->send_tail = NULL; - flag |= 4; + flag |= 4; - sk->window_seq = ack + ntohs(th->window); - cli(); - while (skb2 != NULL) { - skb = skb2; - skb2 = (struct sk_buff *)skb->link3; - skb->link3 = NULL; - if (after(skb->h.seq, sk->window_seq)) { - if (sk->packets_out > 0) sk->packets_out--; - /* We may need to remove this from the dev send list. */ - if (skb->next != NULL) { -#ifdef OLD_WAY - int i; - - if (skb->next != skb) { - skb->next->prev = skb->prev; - skb->prev->next = skb->next; + sk->window_seq = ack + ntohs(th->window); + cli(); + while (skb2 != NULL) + { + skb = skb2; + skb2 = (struct sk_buff *)skb->link3; + skb->link3 = NULL; + if (after(skb->h.seq, sk->window_seq)) + { + if (sk->packets_out > 0) + sk->packets_out--; + /* + * We may need to remove this from the dev send list. + */ + + if (skb->next != NULL) + { + skb_unlink(skb); } - - for(i = 0; i < DEV_NUMBUFFS; i++) { - if (skb->dev->buffs[i] == skb) { - if (skb->next == skb) - skb->dev->buffs[i] = NULL; - else - skb->dev->buffs[i] = skb->next; - break; - } + /* Now add it to the write_queue. */ + skb->magic = TCP_WRITE_QUEUE_MAGIC; + if (wskb == NULL) + { + skb->next = sk->wfront; + sk->wfront = skb; + } + else + { + skb->next = wskb->next; + wskb->next = skb; } - if (arp_q == skb) { - if (skb->next == skb) arp_q = NULL; - else arp_q = skb->next; + if (sk->wback == wskb) + sk->wback = skb; + wskb = skb; + } + else + { + if (sk->send_head == NULL) + { + sk->send_head = skb; + sk->send_tail = skb; + } + else + { + sk->send_tail->link3 = skb; + sk->send_tail = skb; } -#else - skb_unlink(skb); -#endif - } - /* Now add it to the write_queue. */ - skb->magic = TCP_WRITE_QUEUE_MAGIC; - if (wskb == NULL) { - skb->next = sk->wfront; - sk->wfront = skb; - } else { - skb->next = wskb->next; - wskb->next = skb; - } - if (sk->wback == wskb) sk->wback = skb; - wskb = skb; - } else { - if (sk->send_head == NULL) { - sk->send_head = skb; - sk->send_tail = skb; - } else { - sk->send_tail->link3 = skb; - sk->send_tail = skb; + skb->link3 = NULL; } - skb->link3 = NULL; } - } - sti(); - } - - if (sk->send_tail == NULL || sk->send_head == NULL) { - sk->send_head = NULL; - sk->send_tail = NULL; - sk->packets_out= 0; - } - - sk->window_seq = ack + ntohs(th->window); - - /* We don't want too many packets out there. */ - if (sk->cong_window < 2048 && ack != sk->rcv_ack_seq) { - if (sk->exp_growth) sk->cong_window *= 2; - else sk->cong_window++; - } - - DPRINTF((DBG_TCP, "tcp_ack: Updating rcv ack sequence.\n")); - sk->rcv_ack_seq = ack; - - /* See if we can take anything off of the retransmit queue. */ - while(sk->send_head != NULL) { - /* Check for a bug. */ - if (sk->send_head->link3 && - after(sk->send_head->h.seq, sk->send_head->link3->h.seq)) { - printk("INET: tcp.c: *** bug send_list out of order.\n"); - sort_send(sk); - } + sti(); + } - if (before(sk->send_head->h.seq, ack+1)) { - struct sk_buff *oskb; + if (sk->send_tail == NULL || sk->send_head == NULL) + { + sk->send_head = NULL; + sk->send_tail = NULL; + sk->packets_out= 0; + } - sk->retransmits = 0; + sk->window_seq = ack + ntohs(th->window); - /* We have one less packet out there. */ - if (sk->packets_out > 0) sk->packets_out --; - DPRINTF((DBG_TCP, "skb=%X skb->h.seq = %d acked ack=%d\n", - sk->send_head, sk->send_head->h.seq, ack)); + /* We don't want too many packets out there. */ + if (sk->cong_window < 2048 && ack != sk->rcv_ack_seq) + { + if (sk->exp_growth) + sk->cong_window *= 2; + else + sk->cong_window++; + } - /* Wake up the process, it can probably write more. */ - if (!sk->dead) wake_up(sk->sleep); + DPRINTF((DBG_TCP, "tcp_ack: Updating rcv ack sequence.\n")); + sk->rcv_ack_seq = ack; - oskb = sk->send_head; + /* See if we can take anything off of the retransmit queue. */ + while(sk->send_head != NULL) + { + /* Check for a bug. */ + if (sk->send_head->link3 && after(sk->send_head->h.seq, sk->send_head->link3->h.seq)) + { + printk("INET: tcp.c: *** bug send_list out of order.\n"); + sort_send(sk); + } - /* Estimate the RTT. Ignore the ones right after a retransmit. */ - if (sk->retransmits == 0 && !(flag&2)) { - long abserr, rtt = jiffies - oskb->when; + if (before(sk->send_head->h.seq, ack+1)) + { + struct sk_buff *oskb; - if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) - /* first ack, so nothing else to average with */ - sk->rtt = rtt; - else { - abserr = (rtt > sk->rtt) ? rtt - sk->rtt : sk->rtt - rtt; - sk->rtt = (7 * sk->rtt + rtt) >> 3; - sk->mdev = (3 * sk->mdev + abserr) >> 2; - } - sk->backoff = 0; - } - flag |= (2|4); - /* no point retransmitting faster than .1 sec */ - /* 2 minutes is max legal rtt for Internet */ - if (sk->rtt < 10) sk->rtt = 10; - if (sk->rtt > 12000) sk->rtt = 12000; + sk->retransmits = 0; - cli(); + /* We have one less packet out there. */ + if (sk->packets_out > 0) + sk->packets_out --; + DPRINTF((DBG_TCP, "skb=%X skb->h.seq = %d acked ack=%d\n", + sk->send_head, sk->send_head->h.seq, ack)); - oskb = sk->send_head; - IS_SKB(oskb); - sk->send_head =(struct sk_buff *)oskb->link3; - if (sk->send_head == NULL) { - sk->send_tail = NULL; - } + /* Wake up the process, it can probably write more. */ + if (!sk->dead) + sk->write_space(sk); - /* We may need to remove this from the dev send list. */ - skb_unlink(oskb); /* Much easier! */ - sti(); - oskb->magic = 0; - kfree_skb(oskb, FREE_WRITE); /* write. */ - if (!sk->dead) wake_up(sk->sleep); - } else { - break; - } - } + oskb = sk->send_head; - /* - * Maybe we can take some stuff off of the write queue, - * and put it onto the xmit queue. - */ - if (sk->wfront != NULL) { - if (after (sk->window_seq, sk->wfront->h.seq) && - sk->packets_out < sk->cong_window) { - flag |= 1; - tcp_write_xmit(sk); - } - } else { - if (sk->send_head == NULL && sk->ack_backlog == 0 && - sk->state != TCP_TIME_WAIT && !sk->keepopen) { - DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); - if (!sk->dead) wake_up(sk->sleep); + /* Estimate the RTT. Ignore the ones right after a retransmit. */ + + if (sk->retransmits == 0 && !(flag&2)) + { + long abserr, rtt = jiffies - oskb->when; + + if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + /* first ack, so nothing else to average with */ + sk->rtt = rtt; + else + { + abserr = (rtt > sk->rtt) ? rtt - sk->rtt : sk->rtt - rtt; + sk->rtt = (7 * sk->rtt + rtt) >> 3; + sk->mdev = (3 * sk->mdev + abserr) >> 2; + } + sk->backoff = 0; + } + flag |= (2|4); + /* no point retransmitting faster than .1 sec */ + /* 2 minutes is max legal rtt for Internet */ + + if (sk->rtt < 10) + sk->rtt = 10; + + if (sk->rtt > 12000) + sk->rtt = 12000; + + cli(); + + oskb = sk->send_head; + IS_SKB(oskb); + sk->send_head =(struct sk_buff *)oskb->link3; + if (sk->send_head == NULL) + { + sk->send_tail = NULL; + } - if (sk->keepopen) - reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); - else - delete_timer(sk); - } else { - if (sk->state != (unsigned char) sk->keepopen) { - reset_timer(sk, TIME_WRITE, - backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); + /* We may need to remove this from the dev send list. */ + skb_unlink(oskb); /* Much easier! */ + sti(); + oskb->magic = 0; + kfree_skb(oskb, FREE_WRITE); /* write. */ + if (!sk->dead) + sk->write_space(sk); + } + else + { + break; } - if (sk->state == TCP_TIME_WAIT) { - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } + + /* + * Maybe we can take some stuff off of the write queue, + * and put it onto the xmit queue. + */ + if (sk->wfront != NULL) + { + if (after (sk->window_seq, sk->wfront->h.seq) && sk->packets_out < sk->cong_window) + { + flag |= 1; + tcp_write_xmit(sk); } - } - } - - if (sk->packets_out == 0 && sk->send_tmp != NULL && - sk->wfront == NULL && sk->send_head == NULL) { - flag |= 1; - tcp_send_partial(sk); - } - - /* See if we are done. */ - if (sk->state == TCP_TIME_WAIT) { - if (!sk->dead) wake_up(sk->sleep); - if (sk->rcv_ack_seq == sk->send_seq && sk->acked_seq == sk->fin_seq) { - flag |= 1; - sk->state = TCP_CLOSE; - sk->shutdown = SHUTDOWN_MASK; - } - } + } + else + { + if (sk->send_head == NULL && sk->ack_backlog == 0 && sk->state != TCP_TIME_WAIT && !sk->keepopen) + { + DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); + if (!sk->dead) + sk->write_space(sk); + + if (sk->keepopen) + reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + else + delete_timer(sk); + } + else + { + if (sk->state != (unsigned char) sk->keepopen) + { + reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); + } + if (sk->state == TCP_TIME_WAIT) + { + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } + } + } - if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) { - if (!sk->dead) wake_up(sk->sleep); - if (sk->rcv_ack_seq == sk->send_seq) { + /* + * If we have nothing left to send then send the packet we are currently assembling + */ + + if (sk->packets_out == 0 && sk->send_tmp != NULL && sk->wfront == NULL && sk->send_head == NULL) + { flag |= 1; - if (sk->acked_seq != sk->fin_seq) { - tcp_time_wait(sk); - } else { - DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk)); - tcp_send_ack(sk->send_seq, sk->acked_seq, sk, - th, sk->daddr); - sk->shutdown = SHUTDOWN_MASK; + tcp_send_partial(sk); + } + + /* See if we are done. */ + if (sk->state == TCP_TIME_WAIT) + { + + /* + * Our FIN has been acknowledged + */ + + if (sk->rcv_ack_seq == sk->send_seq && sk->acked_seq == sk->fin_seq) + { + flag |= 1; sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + if (!sk->dead) + sk->state_change(sk); } - } - } + } - if (((!flag) || (flag&4)) && sk->send_head != NULL && - (sk->send_head->when + backoff(sk->backoff) * (2 * sk->mdev + sk->rtt) - < jiffies)) { - sk->exp_growth = 0; - ip_retransmit(sk, 0); - } + if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) + { + if (sk->rcv_ack_seq == sk->send_seq) + { + flag |= 1; + if (sk->acked_seq != sk->fin_seq) + { + tcp_time_wait(sk); + } + else + { + DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk)); + tcp_send_ack(sk->send_seq, sk->acked_seq, sk, + th, sk->daddr); + sk->shutdown = SHUTDOWN_MASK; + sk->state = TCP_CLOSE; + } + } + if (!sk->dead) + sk->state_change(sk); + + } - DPRINTF((DBG_TCP, "leaving tcp_ack\n")); - return(1); + if (((!flag) || (flag&4)) && sk->send_head != NULL && (sk->send_head->when + backoff(sk->backoff) * (2 * sk->mdev + sk->rtt) < jiffies)) + { + sk->exp_growth = 0; + ip_retransmit(sk, 0); + } + + DPRINTF((DBG_TCP, "leaving tcp_ack\n")); + return(1); } /* - * This routine handles the data. If there is room in the buffer, - * it will be have already been moved into it. If there is no - * room, then we will just have to discard the packet. + * This routine handles the data. If there is room in the buffer, + * it will be have already been moved into it. If there is no + * room, then we will just have to discard the packet. */ -static int -tcp_data(struct sk_buff *skb, struct sock *sk, + +static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned long saddr, unsigned short len) { - struct sk_buff *skb1, *skb2; - struct tcphdr *th; - int dup_dumped=0; - - th = skb->h.th; - print_th(th); - skb->len = len -(th->doff*4); - - DPRINTF((DBG_TCP, "tcp_data len = %d sk = %X:\n", skb->len, sk)); - - sk->bytes_rcv += skb->len; - if (skb->len == 0 && !th->fin && !th->urg && !th->psh) { - /* Don't want to keep passing ack's back and forth. */ - if (!th->ack) tcp_send_ack(sk->send_seq, sk->acked_seq,sk, th, saddr); - kfree_skb(skb, FREE_READ); - return(0); - } - - if (sk->shutdown & RCV_SHUTDOWN) { - sk->acked_seq = th->seq + skb->len + th->syn + th->fin; - tcp_reset(sk->saddr, sk->daddr, skb->h.th, - sk->prot, NULL, skb->dev); - sk->state = TCP_CLOSE; - sk->err = EPIPE; - sk->shutdown = SHUTDOWN_MASK; - DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk)); - kfree_skb(skb, FREE_READ); - if (!sk->dead) wake_up(sk->sleep); - return(0); - } + struct sk_buff *skb1, *skb2; + struct tcphdr *th; + int dup_dumped=0; + + th = skb->h.th; + print_th(th); + + skb->len = len -(th->doff*4); + + DPRINTF((DBG_TCP, "tcp_data len = %d sk = %X:\n", skb->len, sk)); + + sk->bytes_rcv += skb->len; + if (skb->len == 0 && !th->fin && !th->urg && !th->psh) + { + /* Don't want to keep passing ack's back and forth. */ + if (!th->ack) + tcp_send_ack(sk->send_seq, sk->acked_seq,sk, th, saddr); + kfree_skb(skb, FREE_READ); + return(0); + } + + if (sk->shutdown & RCV_SHUTDOWN) + { + sk->acked_seq = th->seq + skb->len + th->syn + th->fin; + tcp_reset(sk->saddr, sk->daddr, skb->h.th, sk->prot, NULL, skb->dev); + sk->state = TCP_CLOSE; + sk->err = EPIPE; + sk->shutdown = SHUTDOWN_MASK; + DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk)); + kfree_skb(skb, FREE_READ); + if (!sk->dead) + sk->state_change(sk); + return(0); + } /* * Now we have to walk the chain, and figure out where this one @@ -2439,430 +2870,482 @@ tcp_data(struct sk_buff *skb, struct sock *sk, * out of order we will be able to fit things in nicely. */ - /* This should start at the last one, and then go around forwards. */ - if (sk->rqueue == NULL) { - DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb)); -#ifdef OLDWAY - sk->rqueue = skb; - skb->next = skb; - skb->prev = skb; - skb->list = &sk->rqueue; -#else - skb_queue_head(&sk->rqueue,skb); -#endif - skb1= NULL; - } else { - DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk)); - for(skb1=sk->rqueue->prev; ; skb1 =(struct sk_buff *)skb1->prev) { - if(sk->debug) - { - printk("skb1=%p :", skb1); - printk("skb1->h.th->seq = %ld: ", skb1->h.th->seq); - printk("skb->h.th->seq = %ld\n",skb->h.th->seq); - printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq, - sk->acked_seq); - } -#ifdef OLD - if (after(th->seq+1, skb1->h.th->seq)) { - skb->prev = skb1; - skb->next = skb1->next; - skb->next->prev = skb; - skb1->next = skb; - if (skb1 == sk->rqueue) sk->rqueue = skb; - break; - } - if (skb1->prev == sk->rqueue) { - skb->next= skb1; - skb->prev = skb1->prev; - skb->prev->next = skb; - skb1->prev = skb; - skb1 = NULL; /* so we know we might be able - to ack stuff. */ - break; - } -#else - if (th->seq==skb1->h.th->seq && skb->len>= skb1->len) - { - skb_append(skb1,skb); - skb_unlink(skb1); - kfree_skb(skb1,FREE_READ); - dup_dumped=1; - skb1=NULL; - break; - } - if (after(th->seq+1, skb1->h.th->seq)) - { - skb_append(skb1,skb); - break; - } - if (skb1 == sk->rqueue) + /* + * This should start at the last one, and then go around forwards. + */ + + if (sk->rqueue == NULL) + { + DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb)); + skb_queue_head(&sk->rqueue,skb); + skb1= NULL; + } + else + { + DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk)); + for(skb1=sk->rqueue->prev; ; skb1 =(struct sk_buff *)skb1->prev) { - skb_queue_head(&sk->rqueue, skb); - break; - } -#endif - } - DPRINTF((DBG_TCP, "skb = %X:\n", skb)); - } - - th->ack_seq = th->seq + skb->len; - if (th->syn) th->ack_seq++; - if (th->fin) th->ack_seq++; - - if (before(sk->acked_seq, sk->copied_seq)) { - printk("*** tcp.c:tcp_data bug acked < copied\n"); - sk->acked_seq = sk->copied_seq; - } - - /* Now figure out if we can ack anything. */ - if ((!dup_dumped && (skb1 == NULL || skb1->acked)) || before(th->seq, sk->acked_seq+1)) { - if (before(th->seq, sk->acked_seq+1)) { - if (after(th->ack_seq, sk->acked_seq)) - sk->acked_seq = th->ack_seq; - skb->acked = 1; - - /* When we ack the fin, we turn on the RCV_SHUTDOWN flag. */ - if (skb->h.th->fin) { - if (!sk->dead) wake_up(sk->sleep); - sk->shutdown |= RCV_SHUTDOWN; + if(sk->debug) + { + printk("skb1=%p :", skb1); + printk("skb1->h.th->seq = %ld: ", skb1->h.th->seq); + printk("skb->h.th->seq = %ld\n",skb->h.th->seq); + printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq, + sk->acked_seq); + } + if (th->seq==skb1->h.th->seq && skb->len>= skb1->len) + { + skb_append(skb1,skb); + skb_unlink(skb1); + kfree_skb(skb1,FREE_READ); + dup_dumped=1; + skb1=NULL; + break; + } + if (after(th->seq+1, skb1->h.th->seq)) + { + skb_append(skb1,skb); + break; + } + if (skb1 == sk->rqueue) + { + skb_queue_head(&sk->rqueue, skb); + break; + } } + DPRINTF((DBG_TCP, "skb = %X:\n", skb)); + } + + th->ack_seq = th->seq + skb->len; + + if (th->syn) + th->ack_seq++; + + if (th->fin) + th->ack_seq++; + + if (before(sk->acked_seq, sk->copied_seq)) + { + printk("*** tcp.c:tcp_data bug acked < copied\n"); + sk->acked_seq = sk->copied_seq; + } + + /* Now figure out if we can ack anything. */ + + if ((!dup_dumped && (skb1 == NULL || skb1->acked)) || before(th->seq, sk->acked_seq+1)) + { + if (before(th->seq, sk->acked_seq+1)) + { + if (after(th->ack_seq, sk->acked_seq)) + sk->acked_seq = th->ack_seq; + skb->acked = 1; + + /* When we ack the fin, we turn on the RCV_SHUTDOWN flag. */ + if (skb->h.th->fin) + { + if (!sk->dead) + sk->state_change(sk); + sk->shutdown |= RCV_SHUTDOWN; + } - for(skb2 = (struct sk_buff *)skb->next; - skb2 !=(struct sk_buff *) sk->rqueue; - skb2 = (struct sk_buff *)skb2->next) { - if (before(skb2->h.th->seq, sk->acked_seq+1)) { - if (after(skb2->h.th->ack_seq, sk->acked_seq)) - sk->acked_seq = skb2->h.th->ack_seq; - skb2->acked = 1; + for(skb2 = (struct sk_buff *)skb->next; + skb2 !=(struct sk_buff *) sk->rqueue; + skb2 = (struct sk_buff *)skb2->next) + { + if (before(skb2->h.th->seq, sk->acked_seq+1)) + { + if (after(skb2->h.th->ack_seq, sk->acked_seq)) + sk->acked_seq = skb2->h.th->ack_seq; + skb2->acked = 1; + + /* + * When we ack the fin, we turn on + * the RCV_SHUTDOWN flag. + */ + if (skb2->h.th->fin) + { + sk->shutdown |= RCV_SHUTDOWN; + if (!sk->dead) + sk->state_change(sk); + } - /* - * When we ack the fin, we turn on - * the RCV_SHUTDOWN flag. - */ - if (skb2->h.th->fin) { - sk->shutdown |= RCV_SHUTDOWN; - if (!sk->dead) wake_up(sk->sleep); + /* Force an immediate ack. */ + sk->ack_backlog = sk->max_ack_backlog; + } + else + { + break; } + } - /* Force an immediate ack. */ - sk->ack_backlog = sk->max_ack_backlog; - } else { - break; + /* + * This also takes care of updating the window. + * This if statement needs to be simplified. + */ + if (!sk->delay_acks || sk->ack_backlog >= sk->max_ack_backlog || + sk->bytes_rcv > sk->max_unacked || th->fin) + { +/* tcp_send_ack(sk->send_seq, sk->acked_seq,sk,th, saddr); */ + } + else + { + sk->ack_backlog++; + if(sk->debug) + printk("Ack queued.\n"); + reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); } } + } + /* + * If we've missed a packet, send an ack. + * Also start a timer to send another. + */ + if (!skb->acked) + { /* - * This also takes care of updating the window. - * This if statement needs to be simplified. + * This is important. If we don't have much room left, + * we need to throw out a few packets so we have a good + * window. */ - if (!sk->delay_acks || - sk->ack_backlog >= sk->max_ack_backlog || - sk->bytes_rcv > sk->max_unacked || th->fin) { -/* tcp_send_ack(sk->send_seq, sk->acked_seq,sk,th, saddr); */ - } else { - sk->ack_backlog++; - if(sk->debug) - printk("Ack queued.\n"); - reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); - } - } - } - - /* - * If we've missed a packet, send an ack. - * Also start a timer to send another. - */ - if (!skb->acked) { - /* - * This is important. If we don't have much room left, - * we need to throw out a few packets so we have a good - * window. - */ - while (sk->prot->rspace(sk) < sk->mtu) { - skb1 = skb_peek(&sk->rqueue); - if (skb1 == NULL) { - printk("INET: tcp.c:tcp_data memory leak detected.\n"); - break; - } + while (sk->prot->rspace(sk) < sk->mtu) + { + skb1 = skb_peek(&sk->rqueue); + if (skb1 == NULL) + { + printk("INET: tcp.c:tcp_data memory leak detected.\n"); + break; + } - /* Don't throw out something that has been acked. */ - if (skb1->acked) { - break; - } + /* Don't throw out something that has been acked. */ + if (skb1->acked) + { + break; + } - skb_unlink(skb1); -#ifdef OLDWAY - if (skb1->prev == skb1) { - sk->rqueue = NULL; - } else { - sk->rqueue = (struct sk_buff *)skb1->prev; - skb1->next->prev = skb1->prev; - skb1->prev->next = skb1->next; + skb_unlink(skb1); + kfree_skb(skb1, FREE_READ); } -#endif - kfree_skb(skb1, FREE_READ); + tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); + sk->ack_backlog++; + reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); + } + else + { + /* We missed a packet. Send an ack to try to resync things. */ + tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); + } + + /* Now tell the user we may have some data. */ + if (!sk->dead) + { + if(sk->debug) + printk("Data wakeup.\n"); + sk->data_ready(sk,0); + } + else + { + DPRINTF((DBG_TCP, "data received on dead socket.\n")); + } + + if (sk->state == TCP_FIN_WAIT2 && sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->send_seq) + { + DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk)); + +/* tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); */ + sk->shutdown = SHUTDOWN_MASK; + sk->state = TCP_LAST_ACK; + if (!sk->dead) + sk->state_change(sk); } - tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); - sk->ack_backlog++; - reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); - } else { - /* We missed a packet. Send an ack to try to resync things. */ - tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); - } - - /* Now tell the user we may have some data. */ - if (!sk->dead) { - if(sk->debug) - printk("Data wakeup.\n"); - wake_up(sk->sleep); - } else { - DPRINTF((DBG_TCP, "data received on dead socket.\n")); - } - - if (sk->state == TCP_FIN_WAIT2 && - sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->send_seq) { - DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk)); - -/* tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); */ - sk->shutdown = SHUTDOWN_MASK; - sk->state = TCP_LAST_ACK; - if (!sk->dead) wake_up(sk->sleep); - } - return(0); + return(0); } -static int -tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr) +static int tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr) { - extern int kill_pg(int pg, int sig, int priv); - extern int kill_proc(int pid, int sig, int priv); + extern int kill_pg(int pg, int sig, int priv); + extern int kill_proc(int pid, int sig, int priv); - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) + sk->data_ready(sk,0); - if (sk->urginline) { - th->urg = 0; - th->psh = 1; - return(0); - } - - if (!sk->urg) { - /* So if we get more urgent data, we don't signal the user again. */ - if (sk->proc != 0) { - if (sk->proc > 0) { - kill_proc(sk->proc, SIGURG, 1); - } else { - kill_pg(-sk->proc, SIGURG, 1); + if (sk->urginline) + { + th->urg = 0; + th->psh = 1; + return(0); + } + + if (!sk->urg) + { + /* So if we get more urgent data, we don't signal the user again. */ + if (sk->proc != 0) + { + if (sk->proc > 0) + { + kill_proc(sk->proc, SIGURG, 1); + } + else + { + kill_pg(-sk->proc, SIGURG, 1); + } } - } - } - sk->urg++; - return(0); + } + sk->urg++; + return(0); } -/* This deals with incoming fins. */ -static int -tcp_fin(struct sock *sk, struct tcphdr *th, +/* + * This deals with incoming fins. [Page 75] + */ + +static int tcp_fin(struct sock *sk, struct tcphdr *th, unsigned long saddr, struct device *dev) { - DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n", + DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n", sk, th, saddr, dev)); - if (!sk->dead) { - wake_up(sk->sleep); - } - - switch(sk->state) { - case TCP_SYN_RECV: - case TCP_SYN_SENT: - case TCP_ESTABLISHED: - /* Contains the one that needs to be acked */ - sk->fin_seq = th->seq+1; - sk->state = TCP_CLOSE_WAIT; - if (th->rst) sk->shutdown = SHUTDOWN_MASK; - break; - - case TCP_CLOSE_WAIT: - case TCP_FIN_WAIT2: - break; /* we got a retransmit of the fin. */ - - case TCP_FIN_WAIT1: - /* Contains the one that needs to be acked */ - sk->fin_seq = th->seq+1; - sk->state = TCP_FIN_WAIT2; - break; - - default: - case TCP_TIME_WAIT: - sk->state = TCP_LAST_ACK; - /* Start the timers. */ - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - return(0); - } - sk->ack_backlog++; + switch(sk->state) + { + case TCP_SYN_RECV: + case TCP_SYN_SENT: + case TCP_ESTABLISHED: + /* Contains the one that needs to be acked */ + sk->fin_seq = th->seq+1; + sk->state = TCP_CLOSE_WAIT; + if (th->rst) + sk->shutdown = SHUTDOWN_MASK; + break; + + case TCP_CLOSE_WAIT: + break; + + case TCP_FIN_WAIT2: + tcp_time_wait(sk); + break; /* we got a retransmit of the fin. */ + + case TCP_FIN_WAIT1: + /* Contains the one that needs to be acked */ + if(before(sk->send_seq,sk->rcv_ack_seq+1) && after(sk->send_seq,sk->rcv_ack_seq-1)) + tcp_time_wait(sk); + else + { + sk->fin_seq = th->seq+1; + sk->state = TCP_FIN_WAIT2;/* should be closing */ + } + break; + + default: + case TCP_TIME_WAIT: + sk->state = TCP_LAST_ACK; + + /* Start the timers. */ + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + if (!sk->dead) + { + sk->state_change(sk); + } + return(0); + } + sk->ack_backlog++; + if (!sk->dead) + { + sk->state_change(sk); + } - return(0); + return(0); } -/* This will accept the next outstanding connection. */ -static struct sock * -tcp_accept(struct sock *sk, int flags) +/* + * This will accept the next outstanding connection. + */ + +static struct sock *tcp_accept(struct sock *sk, int flags) { - struct sock *newsk; - struct sk_buff *skb; + struct sock *newsk; + struct sk_buff *skb; - DPRINTF((DBG_TCP, "tcp_accept(sk=%X, flags=%X, addr=%s)\n", + DPRINTF((DBG_TCP, "tcp_accept(sk=%X, flags=%X, addr=%s)\n", sk, flags, in_ntoa(sk->saddr))); - /* - * We need to make sure that this socket is listening, - * and that it has something pending. - */ - if (sk->state != TCP_LISTEN) { - sk->err = EINVAL; - return(NULL); - } - - /* avoid the race. */ - cli(); - sk->inuse = 1; - while((skb = get_firstr(sk)) == NULL) { - if (flags & O_NONBLOCK) { - sti(); - release_sock(sk); - sk->err = EAGAIN; - return(NULL); - } + /* + * We need to make sure that this socket is listening, + * and that it has something pending. + */ + + if (sk->state != TCP_LISTEN) + { + sk->err = EINVAL; + return(NULL); + } - release_sock(sk); - interruptible_sleep_on(sk->sleep); - if (current->signal & ~current->blocked) { - sti(); - sk->err = ERESTARTSYS; - return(NULL); - } - sk->inuse = 1; - } - sti(); + /* avoid the race. */ + + cli(); + sk->inuse = 1; + while((skb = get_firstr(sk)) == NULL) + { + /* + * Nobody connecting + */ + if (flags & O_NONBLOCK) + { + sti(); + release_sock(sk); + sk->err = EAGAIN; + return(NULL); + } + + release_sock(sk); + interruptible_sleep_on(sk->sleep); + if (current->signal & ~current->blocked) + { + sti(); + sk->err = ERESTARTSYS; + return(NULL); + } + sk->inuse = 1; + } + sti(); - /* Now all we need to do is return skb->sk. */ - newsk = skb->sk; + /* Now all we need to do is return skb->sk. */ + newsk = skb->sk; - kfree_skb(skb, FREE_READ); - sk->ack_backlog--; - release_sock(sk); - return(newsk); + kfree_skb(skb, FREE_READ); + sk->ack_backlog--; + release_sock(sk); + return(newsk); } -/* This will initiate an outgoing connection. */ -static int -tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) +/* + * This will initiate an outgoing connection. + */ + +static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) { - struct sk_buff *buff; - struct sockaddr_in sin; - struct device *dev=NULL; - unsigned char *ptr; - int tmp; - struct tcphdr *t1; - int err; - - if (sk->state != TCP_CLOSE) return(-EISCONN); - if (addr_len < 8) return(-EINVAL); - - err=verify_area(VERIFY_READ, usin, addr_len); - if(err) - return err; + struct sk_buff *buff; + struct sockaddr_in sin; + struct device *dev=NULL; + unsigned char *ptr; + int tmp; + struct tcphdr *t1; + int err; + + if (sk->state != TCP_CLOSE) + return(-EISCONN); + + if (addr_len < 8) + return(-EINVAL); + + err=verify_area(VERIFY_READ, usin, addr_len); + if(err) + return err; - memcpy_fromfs(&sin,usin, min(sizeof(sin), addr_len)); + memcpy_fromfs(&sin,usin, min(sizeof(sin), addr_len)); - if (sin.sin_family && sin.sin_family != AF_INET) return(-EAFNOSUPPORT); + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EAFNOSUPPORT); - DPRINTF((DBG_TCP, "TCP connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr))); + DPRINTF((DBG_TCP, "TCP connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr))); - /* Don't want a TCP connection going to a broadcast address */ - if (chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) { - DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n")); - return(-ENETUNREACH); - } + /* + * Don't want a TCP connection going to a broadcast address + */ + + if (chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) + { + DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n")); + return(-ENETUNREACH); + } + + sk->inuse = 1; + sk->daddr = sin.sin_addr.s_addr; + sk->send_seq = jiffies * SEQ_TICK - seq_offset; + sk->rcv_ack_seq = sk->send_seq -1; + sk->err = 0; + sk->dummy_th.dest = sin.sin_port; + release_sock(sk); + + buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL); + if (buff == NULL) + { + return(-ENOMEM); + } - sk->inuse = 1; - sk->daddr = sin.sin_addr.s_addr; - sk->send_seq = jiffies * SEQ_TICK - seq_offset; - sk->rcv_ack_seq = sk->send_seq -1; - sk->err = 0; - sk->dummy_th.dest = sin.sin_port; - release_sock(sk); - - buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL); - if (buff == NULL) { - return(-ENOMEM); - } - sk->inuse = 1; - buff->mem_addr = buff; - buff->mem_len = MAX_SYN_SIZE; - buff->len = 24; - buff->sk = sk; - buff->free = 1; - t1 = (struct tcphdr *)(buff + 1); - - /* Put in the IP header and routing stuff. */ - /* We need to build the routing stuff fromt the things saved in skb. */ - tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, NULL, MAX_SYN_SIZE); - if (tmp < 0) { - sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); - release_sock(sk); - return(-ENETUNREACH); - } - buff->len += tmp; - t1 = (struct tcphdr *)((char *)t1 +tmp); - - memcpy(t1,(void *)&(sk->dummy_th), sizeof(*t1)); - t1->seq = ntohl(sk->send_seq++); - buff->h.seq = sk->send_seq; - t1->ack = 0; - t1->window = 2; - t1->res1=0; - t1->res2=0; - t1->rst = 0; - t1->urg = 0; - t1->psh = 0; - t1->syn = 1; - t1->urg_ptr = 0; - t1->doff = 6; - - /* Put in the TCP options to say MTU. */ - ptr = (unsigned char *)(t1+1); - ptr[0] = 2; - ptr[1] = 4; - ptr[2] = (dev->mtu- HEADER_SIZE) >> 8; - ptr[3] = (dev->mtu- HEADER_SIZE) & 0xff; - sk->mtu = dev->mtu - HEADER_SIZE; - tcp_send_check(t1, sk->saddr, sk->daddr, - sizeof(struct tcphdr) + 4, sk); - - /* This must go first otherwise a really quick response will get reset. */ - sk->state = TCP_SYN_SENT; - sk->rtt = TCP_CONNECT_TIME; - reset_timer(sk, TIME_WRITE, TCP_CONNECT_TIME); /* Timer for repeating the SYN until an answer */ - sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES; - - sk->prot->queue_xmit(sk, dev, buff, 0); + sk->inuse = 1; + buff->mem_addr = buff; + buff->mem_len = MAX_SYN_SIZE; + buff->len = 24; + buff->sk = sk; + buff->free = 1; + t1 = (struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + /* We need to build the routing stuff fromt the things saved in skb. */ + tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,IPPROTO_TCP, NULL, MAX_SYN_SIZE, + sk->ip_ttl,sk->ip_tos); - release_sock(sk); - return(0); + if (tmp < 0) + { + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + release_sock(sk); + return(-ENETUNREACH); + } + buff->len += tmp; + t1 = (struct tcphdr *)((char *)t1 +tmp); + + memcpy(t1,(void *)&(sk->dummy_th), sizeof(*t1)); + t1->seq = ntohl(sk->send_seq++); + buff->h.seq = sk->send_seq; + t1->ack = 0; + t1->window = 2; + t1->res1=0; + t1->res2=0; + t1->rst = 0; + t1->urg = 0; + t1->psh = 0; + t1->syn = 1; + t1->urg_ptr = 0; + t1->doff = 6; + + /* Put in the TCP options to say MTU. */ + ptr = (unsigned char *)(t1+1); + ptr[0] = 2; + ptr[1] = 4; + ptr[2] = (dev->mtu- HEADER_SIZE) >> 8; + ptr[3] = (dev->mtu- HEADER_SIZE) & 0xff; + sk->mtu = dev->mtu - HEADER_SIZE; + tcp_send_check(t1, sk->saddr, sk->daddr, + sizeof(struct tcphdr) + 4, sk); + + /* + * This must go first otherwise a really quick response will get reset. + */ + + sk->state = TCP_SYN_SENT; + sk->rtt = TCP_CONNECT_TIME; + reset_timer(sk, TIME_WRITE, TCP_CONNECT_TIME); /* Timer for repeating the SYN until an answer */ + sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES; + + sk->prot->queue_xmit(sk, dev, buff, 0); + + release_sock(sk); + return(0); } -/* This functions checks to see if the tcp header is actually acceptible. */ -static int -tcp_sequence(struct sock *sk, struct tcphdr *th, short len, - struct options *opt, unsigned long saddr) +/* + * This functions checks to see if the tcp header is actually acceptible. + */ + +static int tcp_sequence(struct sock *sk, struct tcphdr *th, short len, + struct options *opt, unsigned long saddr, struct device *dev) { /* * This isn't quite right. sk->acked_seq could be more recent @@ -2870,552 +3353,711 @@ tcp_sequence(struct sock *sk, struct tcphdr *th, short len, * slightly more packets than we should, but it should not cause * problems unless someone is trying to forge packets. */ - DPRINTF((DBG_TCP, "tcp_sequence(sk=%X, th=%X, len = %d, opt=%d, saddr=%X)\n", - sk, th, len, opt, saddr)); - - if (between(th->seq, sk->acked_seq, sk->acked_seq + sk->window)|| - between(th->seq + len-(th->doff*4), sk->acked_seq + 1, - sk->acked_seq + sk->window) || - (before(th->seq, sk->acked_seq) && - after(th->seq + len -(th->doff*4), sk->acked_seq + sk->window))) { - return(1); - } - DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n")); + DPRINTF((DBG_TCP, "tcp_sequence(sk=%X, th=%X, len = %d, opt=%d, saddr=%X)\n", + sk, th, len, opt, saddr)); + + /* Is it within the windows */ + if (between(th->seq, sk->acked_seq, sk->acked_seq + sk->window)|| + between(th->seq + len-(th->doff*4), sk->acked_seq + 1, sk->acked_seq + sk->window) || + (before(th->seq, sk->acked_seq) && + after(th->seq + len -(th->doff*4), sk->acked_seq + sk->window))) + { + return(1); + } + DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n")); + + /* + * Send a reset if we get something not ours and we are + * unsynchronized. Note: We don't do anything to our end. We + * are just killing the bogus remote connection then we will + * connect again and it will work (with luck). + */ + + if(sk->state==TCP_SYN_SENT||sk->state==TCP_SYN_RECV) + { + tcp_reset(sk->saddr,sk->daddr,th,sk->prot,NULL,dev); + return(1); + } - /* - * If it's too far ahead, send an ack to let the - * other end know what we expect. - */ - if (after(th->seq, sk->acked_seq + sk->window)) { - if(!th->rst) + /* + * If it's too far ahead, send an ack to let the + * other end know what we expect. + */ + + if (after(th->seq, sk->acked_seq + sk->window)) + { + if(!th->rst) + tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); + return(0); + } + + /* + * In case it's just a late ack, let it through. + */ + + if (th->ack && len == (th->doff * 4) && after(th->seq, sk->acked_seq - 32767) && !th->fin && !th->syn) + return(1); + + if (!th->rst) + { + /* Try to resync things. */ tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); - return(0); - } - - /* In case it's just a late ack, let it through. */ - if (th->ack && len == (th->doff * 4) && - after(th->seq, sk->acked_seq - 32767) && - !th->fin && !th->syn) return(1); - - if (!th->rst) { - /* Try to resync things. */ - tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); - } - return(0); + } + return(0); } +/* + * A TCP frame has been received by the IP layer and will now + * be thrown to us for processing. I've dropped page numbers + * into this. These are from the ASCII version of the RFC + * and should help anyone following it. + */ - -int -tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, +int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol * protocol) { - struct tcphdr *th; - struct sock *sk; - - if (!skb) { - DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n")); - return(0); - } -#if 0 /* FIXME: it's ok for protocol to be NULL */ - if (!protocol) { - DPRINTF((DBG_TCP, "tcp.c: tcp_rcv protocol = NULL\n")); - return(0); - } - - if (!opt) { /* FIXME: it's ok for opt to be NULL */ - DPRINTF((DBG_TCP, "tcp.c: tcp_rcv opt = NULL\n")); - } -#endif - if (!dev) { - DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n")); - return(0); - } - th = skb->h.th; - - /* Find the socket. */ - sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr); - DPRINTF((DBG_TCP, "<<\n")); - DPRINTF((DBG_TCP, "len = %d, redo = %d, skb=%X\n", len, redo, skb)); - - /* If this socket has got a reset its to all intents and purposes - really dead */ - if (sk!=NULL && sk->zapped) - sk=NULL; - - if (sk) { - DPRINTF((DBG_TCP, "sk = %X:\n", sk)); - } + struct tcphdr *th; + struct sock *sk; - if (!redo) { - if (tcp_check(th, len, saddr, daddr )) { - skb->sk = NULL; - DPRINTF((DBG_TCP, "packet dropped with bad checksum.\n")); -if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); - kfree_skb(skb,FREE_READ); - /* - * We don't release the socket because it was - * never marked in use. - */ + if (!skb) + { + DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n")); return(0); - } + } - /* See if we know about the socket. */ - if (sk == NULL) { - if (!th->rst) - { - th->seq = ntohl(th->seq); - /* So reset is always called with th->seq in host order */ - tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev); - } - skb->sk = NULL; - kfree_skb(skb, FREE_READ); + if (!dev) + { + DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n")); return(0); - } + } + + th = skb->h.th; + + /* Find the socket. */ + sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr); + DPRINTF((DBG_TCP, "<<\n")); + DPRINTF((DBG_TCP, "len = %d, redo = %d, skb=%X\n", len, redo, skb)); + + /* If this socket has got a reset its to all intents and purposes + really dead */ + + if (sk!=NULL && sk->zapped) + sk=NULL; + + if (sk) + { + DPRINTF((DBG_TCP, "sk = %X:\n", sk)); + } - skb->len = len; - skb->sk = sk; - skb->acked = 0; - skb->used = 0; - skb->free = 0; - skb->urg_used = 0; - skb->saddr = daddr; - skb->daddr = saddr; - - th->seq = ntohl(th->seq); - - /* We may need to add it to the backlog here. */ - cli(); - if (sk->inuse) { - if (sk->back_log == NULL) { - sk->back_log = skb; - skb->next = skb; - skb->prev = skb; - } else { - skb->next = sk->back_log; - skb->prev = sk->back_log->prev; - skb->prev->next = skb; - skb->next->prev = skb; + if (!redo) + { + if (tcp_check(th, len, saddr, daddr )) + { + skb->sk = NULL; + DPRINTF((DBG_TCP, "packet dropped with bad checksum.\n")); + if (inet_debug == DBG_SLIP) + printk("\rtcp_rcv: bad checksum\n"); + kfree_skb(skb,FREE_READ); + /* + * We don't release the socket because it was + * never marked in use. + */ + return(0); } - sti(); - return(0); - } - sk->inuse = 1; - sti(); - } else { - if (!sk) { - DPRINTF((DBG_TCP, "tcp.c: tcp_rcv bug sk=NULL redo = 1\n")); - return(0); - } - } - - if (!sk->prot) { - DPRINTF((DBG_TCP, "tcp.c: tcp_rcv sk->prot = NULL \n")); - return(0); - } - - /* Charge the memory to the socket. */ - if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) { - skb->sk = NULL; - DPRINTF((DBG_TCP, "dropping packet due to lack of buffer space.\n")); - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - sk->rmem_alloc += skb->mem_len; - - DPRINTF((DBG_TCP, "About to do switch.\n")); - - /* Now deal with it. */ - switch(sk->state) { - /* - * This should close the system down if it's waiting - * for an ack that is never going to be sent. - */ - case TCP_LAST_ACK: - if (th->rst) { - sk->zapped=1; - sk->err = ECONNRESET; - sk->state = TCP_CLOSE; - sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) { - wake_up(sk->sleep); + + /* See if we know about the socket. */ + if (sk == NULL) + { + /* + * Segment to 'closed' socket [Page 65] + */ + if (!th->rst) + { + th->seq = ntohl(th->seq); + /* So reset is always called with th->seq in host order */ + tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev); } + skb->sk = NULL; kfree_skb(skb, FREE_READ); - release_sock(sk); return(0); } - case TCP_ESTABLISHED: - case TCP_CLOSE_WAIT: - case TCP_FIN_WAIT1: - case TCP_FIN_WAIT2: - case TCP_TIME_WAIT: - if (!tcp_sequence(sk, th, len, opt, saddr)) { -if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); - if(!th->rst) - tcp_send_ack(sk->send_seq, sk->acked_seq, - sk, th, saddr); - kfree_skb(skb, FREE_READ); - release_sock(sk); + skb->len = len; + skb->sk = sk; + skb->acked = 0; + skb->used = 0; + skb->free = 0; + skb->urg_used = 0; + skb->saddr = daddr; + skb->daddr = saddr; + + th->seq = ntohl(th->seq); + + /* We may need to add it to the backlog here. */ + cli(); + if (sk->inuse) + { + if (sk->back_log == NULL) + { + sk->back_log = skb; + skb->next = skb; + skb->prev = skb; + } + else + { + skb->next = sk->back_log; + skb->prev = sk->back_log->prev; + skb->prev->next = skb; + skb->next->prev = skb; + } + sti(); + return(0); + } + sk->inuse = 1; + sti(); + } + else + { + if (!sk) + { + DPRINTF((DBG_TCP, "tcp.c: tcp_rcv bug sk=NULL redo = 1\n")); return(0); } + } - if (th->rst) { - sk->zapped=1; - /* This means the thing should really be closed. */ - sk->err = ECONNRESET; + + if (!sk->prot) + { + DPRINTF((DBG_TCP, "tcp.c: tcp_rcv sk->prot = NULL \n")); + return(0); + } - if (sk->state == TCP_CLOSE_WAIT) { - sk->err = EPIPE; + /* Charge the memory to the socket. */ + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) + { + skb->sk = NULL; + DPRINTF((DBG_TCP, "dropping packet due to lack of buffer space.\n")); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + + sk->rmem_alloc += skb->mem_len; + + DPRINTF((DBG_TCP, "About to do switch.\n")); + + /* Now deal with it. */ + + switch(sk->state) + { + /* + * This should close the system down if it's waiting + * for an ack that is never going to be sent. + * + * [Page 68] + */ + case TCP_LAST_ACK: + if (th->rst) + { + sk->zapped=1; + sk->err = ECONNRESET; + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + if (!sk->dead) + { + sk->state_change(sk); + } + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); } - /* - * A reset with a fin just means that - * the data was not all read. - */ -/* The comment above appears completely bogus --clh */ -/* if (!th->fin) { */ + case TCP_ESTABLISHED: + case TCP_CLOSE_WAIT: + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: + case TCP_TIME_WAIT: + if (!tcp_sequence(sk, th, len, opt, saddr,dev)) + { + if (inet_debug == DBG_SLIP) + printk("\rtcp_rcv: not in seq\n"); + if(!th->rst) + tcp_send_ack(sk->send_seq, sk->acked_seq, + sk, th, saddr); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + + if (th->rst) + { + sk->zapped=1; + /* This means the thing should really be closed. [Page 70] */ + sk->err = ECONNRESET; + + if (sk->state == TCP_CLOSE_WAIT) + { + sk->err = EPIPE; + } + sk->state = TCP_CLOSE; sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) { - wake_up(sk->sleep); + if (!sk->dead) + { + sk->state_change(sk); } kfree_skb(skb, FREE_READ); release_sock(sk); return(0); -/* } */ - } -#if 0 - if (opt && (opt->security != 0 || - opt->compartment != 0 || th->syn)) { - sk->err = ECONNRESET; - sk->state = TCP_CLOSE; - sk->shutdown = SHUTDOWN_MASK; - tcp_reset(daddr, saddr, th, sk->prot, opt,dev); - if (!sk->dead) { - wake_up(sk->sleep); } - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } -#endif - if (th->ack) { - if (!tcp_ack(sk, th, saddr, len)) { + /* [Page 71] - security options: We just say no thanks */ + if (opt && (opt->security != 0 || opt->compartment != 0)) + { + sk->err = ECONNRESET; + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + tcp_reset(daddr, saddr, th, sk->prot, opt,dev); + if (!sk->dead) + { + sk->state_change(sk); + } kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } - } - if (th->urg) { - if (tcp_urg(sk, th, saddr)) { + if (th->syn) + { + sk->err = ECONNRESET; + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + tcp_reset(daddr, saddr, th, sk->prot, opt,dev); + if (!sk->dead) + { + sk->state_change(sk); + } kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } - } - - if (th->fin && tcp_fin(sk, th, saddr, dev)) { - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - - if (tcp_data(skb, sk, saddr, len)) { - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - - release_sock(sk); - return(0); - - case TCP_CLOSE: - if (sk->dead || sk->daddr) { - DPRINTF((DBG_TCP, "packet received for closed,dead socket\n")); - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - - if (!th->rst) { - if (!th->ack) - th->ack_seq = 0; - tcp_reset(daddr, saddr, th, sk->prot, opt,dev); - } - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - - case TCP_LISTEN: - if (th->rst) { - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - if (th->ack) { - tcp_reset(daddr, saddr, th, sk->prot, opt,dev); - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - - if (th->syn) { -#if 0 - if (opt->security != 0 || opt->compartment != 0) { - tcp_reset(daddr, saddr, th, prot, opt,dev); + + if (th->ack) + { + if (!tcp_ack(sk, th, saddr, len)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + } + /* [Page 73] */ + if (th->urg &&(sk->state==TCP_ESTABLISHED||sk->state==TCP_FIN_WAIT1||sk->state==TCP_FIN_WAIT2)) + { + if (tcp_urg(sk, th, saddr)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + } + + + /* [Page 74] */ + if (tcp_data(skb, sk, saddr, len)) + { + kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } -#endif - - /* - * Now we just put the whole thing including - * the header and saddr, and protocol pointer - * into the buffer. We can't respond until the - * user tells us to accept the connection. - */ - tcp_conn_request(sk, skb, daddr, saddr, opt, dev); - release_sock(sk); - return(0); - } + /* Move this below the data processing [Page 74/75] */ - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - - default: - if (!tcp_sequence(sk, th, len, opt, saddr)) { - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - - case TCP_SYN_SENT: - if (th->rst) { - sk->err = ECONNREFUSED; - sk->state = TCP_CLOSE; - sk->shutdown = SHUTDOWN_MASK; - sk->zapped = 1; - if (!sk->dead) { - wake_up(sk->sleep); + if (th->fin && tcp_fin(sk, th, saddr, dev)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); } - kfree_skb(skb, FREE_READ); + release_sock(sk); return(0); - } -#if 0 - if (opt->security != 0 || opt->compartment != 0) { - sk->err = ECONNRESET; - sk->state = TCP_CLOSE; - sk->shutdown = SHUTDOWN_MASK; - tcp_reset(daddr, saddr, th, sk->prot, opt, dev); - if (!sk->dead) { - wake_up(sk->sleep); + + case TCP_CLOSE: + if (sk->dead || sk->daddr) + { + DPRINTF((DBG_TCP, "packet received for closed,dead socket\n")); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); } - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } -#endif - if (!th->ack) { - if (th->syn) { - sk->state = TCP_SYN_RECV; + + if (!th->rst) + { + if (!th->ack) + th->ack_seq = 0; + tcp_reset(daddr, saddr, th, sk->prot, opt,dev); } - kfree_skb(skb, FREE_READ); release_sock(sk); return(0); - } - switch(sk->state) { - case TCP_SYN_SENT: - if (!tcp_ack(sk, th, saddr, len)) { - tcp_reset(daddr, saddr, th, - sk->prot, opt,dev); - kfree_skb(skb, FREE_READ); + case TCP_LISTEN: + if (th->rst) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + if (th->ack) + { + tcp_reset(daddr, saddr, th, sk->prot, opt,dev); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + + if (th->syn) + { + if (opt && (opt->security != 0 || opt->compartment != 0)) + { + tcp_reset(daddr, saddr, th, sk->prot, opt,dev); release_sock(sk); return(0); } /* - * If the syn bit is also set, switch to - * tcp_syn_recv, and then to established. + * Now we just put the whole thing including + * the header and saddr, and protocol pointer + * into the buffer. We can't respond until the + * user tells us to accept the connection. */ - if (!th->syn) { - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } + tcp_conn_request(sk, skb, daddr, saddr, opt, dev); + release_sock(sk); + return(0); + } + + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); - /* Ack the syn and fall through. */ - sk->acked_seq = th->seq+1; - sk->fin_seq = th->seq; - tcp_send_ack(sk->send_seq, th->seq+1, - sk, th, sk->daddr); + default: + if (!tcp_sequence(sk, th, len, opt, saddr,dev)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } - case TCP_SYN_RECV: - if (!tcp_ack(sk, th, saddr, len)) { - tcp_reset(daddr, saddr, th, - sk->prot, opt, dev); - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); + case TCP_SYN_SENT: + if (th->rst) + { + sk->err = ECONNREFUSED; + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + sk->zapped = 1; + if (!sk->dead) + { + sk->state_change(sk); } - sk->state = TCP_ESTABLISHED; - - /* - * Now we need to finish filling out - * some of the tcp header. - */ - /* We need to check for mtu info. */ - tcp_options(sk, th); - sk->dummy_th.dest = th->source; - sk->copied_seq = sk->acked_seq-1; - if (!sk->dead) { - wake_up(sk->sleep); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + if (opt && (opt->security != 0 || opt->compartment != 0)) + { + sk->err = ECONNRESET; + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + tcp_reset(daddr, saddr, th, sk->prot, opt, dev); + if (!sk->dead) + { + sk->state_change(sk); + } + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + if (!th->ack) + { + if (th->syn) + { + sk->state = TCP_SYN_RECV; } + + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } - /* - * Now process the rest like we were - * already in the established state. - */ - if (th->urg) { - if (tcp_urg(sk, th, saddr)) { + switch(sk->state) + { + case TCP_SYN_SENT: + /* [Page 73] */ + if (!tcp_ack(sk, th, saddr, len)) + { + tcp_reset(daddr, saddr, th, + sk->prot, opt,dev); kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } - } - if (tcp_data(skb, sk, saddr, len)) + + /* + * If the syn bit is also set, switch to + * tcp_syn_recv, and then to established. + */ + if (!th->syn) + { kfree_skb(skb, FREE_READ); - - if (th->fin) tcp_fin(sk, th, saddr, dev); - release_sock(sk); - return(0); - } - - if (th->urg) { - if (tcp_urg(sk, th, saddr)) { + release_sock(sk); + return(0); + } + + /* Ack the syn and fall through. */ + sk->acked_seq = th->seq+1; + sk->fin_seq = th->seq; + tcp_send_ack(sk->send_seq, th->seq+1, sk, th, sk->daddr); + + case TCP_SYN_RECV: + if (!tcp_ack(sk, th, saddr, len)) + { + tcp_reset(daddr, saddr, th, sk->prot, opt, dev); + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + sk->state = TCP_ESTABLISHED; + + /* + * Now we need to finish filling out + * some of the tcp header. + */ + /* We need to check for mtu info. */ + tcp_options(sk, th); + sk->dummy_th.dest = th->source; + sk->copied_seq = sk->acked_seq-1; + if (!sk->dead) + { + sk->state_change(sk); + } + + /* + * Now process the rest like we were + * already in the established state. + */ + + if (th->urg) + { + if (tcp_urg(sk, th, saddr)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + } + if (tcp_data(skb, sk, saddr, len)) + kfree_skb(skb, FREE_READ); + + if (th->fin) + tcp_fin(sk, th, saddr, dev); + release_sock(sk); + return(0); + } + + if (th->urg) + { + if (tcp_urg(sk, th, saddr)) + { + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); + } + } + + if (tcp_data(skb, sk, saddr, len)) + { kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } - } - - if (tcp_data(skb, sk, saddr, len)) { - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); - } - if (!th->fin) { + if (!th->fin) + { + release_sock(sk); + return(0); + } + tcp_fin(sk, th, saddr, dev); release_sock(sk); return(0); - } - tcp_fin(sk, th, saddr, dev); - release_sock(sk); - return(0); } } -/* - * This routine sends a packet with an out of date sequence - * number. It assumes the other end will try to ack it. + /* + * This routine sends a packet with an out of date sequence + * number. It assumes the other end will try to ack it. */ -static void -tcp_write_wakeup(struct sock *sk) + +static void tcp_write_wakeup(struct sock *sk) { - struct sk_buff *buff; - struct tcphdr *t1; - struct device *dev=NULL; - int tmp; + struct sk_buff *buff; + struct tcphdr *t1; + struct device *dev=NULL; + int tmp; + + if (sk->zapped) + return; /* Afer a valid reset we can send no more */ - if (sk->zapped) - return; /* Afer a valid reset we can send no more */ + if (sk -> state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) + return; - if (sk -> state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) return; + buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); + + if (buff == NULL) + return; + + buff->mem_addr = buff; + buff->mem_len = MAX_ACK_SIZE; + buff->len = sizeof(struct tcphdr); + buff->free = 1; + buff->sk = sk; + DPRINTF((DBG_TCP, "in tcp_write_wakeup\n")); + t1 = (struct tcphdr *)(buff + 1); + + /* Put in the IP header and routing stuff. */ + tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE, sk->ip_ttl,sk->ip_tos); + if (tmp < 0) + { + sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); + return; + } - buff = (struct sk_buff *) sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); - if (buff == NULL) return; + buff->len += tmp; + t1 = (struct tcphdr *)((char *)t1 +tmp); + + memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); + + /* + * Use a previous sequence. + * This should cause the other end to send an ack. + */ + t1->seq = ntohl(sk->send_seq-1); + t1->ack = 1; + t1->res1= 0; + t1->res2= 0; + t1->rst = 0; + t1->urg = 0; + t1->psh = 0; + t1->fin = 0; + t1->syn = 0; + t1->ack_seq = ntohl(sk->acked_seq); + t1->window = ntohs(sk->prot->rspace(sk)); + t1->doff = sizeof(*t1)/4; + tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); + + /* Send it and free it. + * This will prevent the timer from automatically being restarted. + */ + sk->prot->queue_xmit(sk, dev, buff, 1); +} - buff->mem_addr = buff; - buff->mem_len = MAX_ACK_SIZE; - buff->len = sizeof(struct tcphdr); - buff->free = 1; - buff->sk = sk; - DPRINTF((DBG_TCP, "in tcp_write_wakeup\n")); - t1 = (struct tcphdr *)(buff + 1); +/* + * Socket option code for TCP. + */ + +int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) +{ + int val,err; - /* Put in the IP header and routing stuff. */ - tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE); - if (tmp < 0) { - sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); - return; - } + if(level!=SOL_TCP) + return ip_setsockopt(sk,level,optname,optval,optlen); - buff->len += tmp; - t1 = (struct tcphdr *)((char *)t1 +tmp); + if (optval == NULL) + return(-EINVAL); - memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); - /* - * Use a previous sequence. - * This should cause the other end to send an ack. - */ - t1->seq = ntohl(sk->send_seq-1); - t1->ack = 1; - t1->res1= 0; - t1->res2= 0; - t1->rst = 0; - t1->urg = 0; - t1->psh = 0; - t1->fin = 0; - t1->syn = 0; - t1->ack_seq = ntohl(sk->acked_seq); - t1->window = ntohs(sk->prot->rspace(sk)); - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); - - /* Send it and free it. - * This will prevent the timer from automatically being restarted. - */ - sk->prot->queue_xmit(sk, dev, buff, 1); + switch(optname) + { + case TCP_MSS: + if(val<200||val>2048) + return -EINVAL; + sk->mss=val; + return 0; + case TCP_NODELAY: + /* Ready for Johannes delayed ACK code */ + return 0; + default: + return(-ENOPROTOOPT); + } } +int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) +{ + int val,err; + + if(level!=SOL_TCP) + return ip_getsockopt(sk,level,optname,optval,optlen); + + switch(optname) + { + case TCP_MSS: + val=sk->mss; + break; + case TCP_NODELAY: + val=1; /* Until Johannes stuff is in */ + break; + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); + + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); -struct proto tcp_prot = { - sock_wmalloc, - sock_rmalloc, - sock_wfree, - sock_rfree, - sock_rspace, - sock_wspace, - tcp_close, - tcp_read, - tcp_write, - tcp_sendto, - tcp_recvfrom, - ip_build_header, - tcp_connect, - tcp_accept, - ip_queue_xmit, - tcp_retransmit, - tcp_write_wakeup, - tcp_read_wakeup, - tcp_rcv, - tcp_select, - tcp_ioctl, - NULL, - tcp_shutdown, - 128, - 0, - {NULL,}, - "TCP" + return(0); +} + +struct proto tcp_prot = +{ + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + tcp_close, + tcp_read, + tcp_write, + tcp_sendto, + tcp_recvfrom, + ip_build_header, + tcp_connect, + tcp_accept, + ip_queue_xmit, + tcp_retransmit, + tcp_write_wakeup, + tcp_read_wakeup, + tcp_rcv, + tcp_select, + tcp_ioctl, + NULL, + tcp_shutdown, + tcp_setsockopt, + tcp_getsockopt, + 128, + 0, + {NULL,}, + "TCP" }; diff --git a/net/inet/tcp.h b/net/inet/tcp.h index cb051e4..9077dbf 100644 --- a/net/inet/tcp.h +++ b/net/inet/tcp.h @@ -75,43 +75,46 @@ * normal compare so long as neither of the numbers is within * 4K of wrapping. Otherwise we must check for the wrap. */ -static inline int -before (unsigned long seq1, unsigned long seq2) +static inline int before (unsigned long seq1, unsigned long seq2) { - /* this inequality is strict. */ - if (seq1 == seq2) return(0); - - if (seq1 < seq2) { - if ((unsigned long)seq2-(unsigned long)seq1 < 65536UL) { + /* this inequality is strict. */ + if (seq1 == seq2) + return(0); + + if (seq1 < seq2) + { + if ((unsigned long)seq2-(unsigned long)seq1 < 65536UL) + { + return(1); + } + else + { + return(0); + } + } + + /* + * Now we know seq1 > seq2. So all we need to do is check + * to see if seq1 has wrapped. + */ + if (seq2 < 8192UL && seq1 > (0xffffffffUL - 8192UL)) + { return(1); - } else { - return(0); - } - } - - /* - * Now we know seq1 > seq2. So all we need to do is check - * to see if seq1 has wrapped. - */ - if (seq2 < 8192UL && seq1 > (0xffffffffUL - 8192UL)) { - return(1); - } - return(0); + } + return(0); } -static inline int -after(unsigned long seq1, unsigned long seq2) +static inline int after(unsigned long seq1, unsigned long seq2) { - return(before(seq2, seq1)); + return(before(seq2, seq1)); } /* is s2<=s1<=s3 ? */ -static inline int -between(unsigned long seq1, unsigned long seq2, unsigned long seq3) +static inline int between(unsigned long seq1, unsigned long seq2, unsigned long seq3) { - return(after(seq1+1, seq2) && before(seq1, seq3+1)); + return(after(seq1+1, seq2) && before(seq1, seq3+1)); } @@ -121,12 +124,11 @@ between(unsigned long seq1, unsigned long seq2, unsigned long seq3) * convinced that this is the solution for the 'getpeername(2)' * problem. Thanks to Stephen A. Wood <saw@cebaf.gov> -FvK */ -static inline const int -tcp_connected(const int state) +static inline const int tcp_connected(const int state) { - return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT || - state == TCP_FIN_WAIT1 || state == TCP_FIN_WAIT2 || - state == TCP_SYN_RECV); + return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT || + state == TCP_FIN_WAIT1 || state == TCP_FIN_WAIT2 || + state == TCP_SYN_RECV); } diff --git a/net/inet/timer.c b/net/inet/timer.c index f6540a1..7cd66b5 100644 --- a/net/inet/timer.c +++ b/net/inet/timer.c @@ -5,7 +5,7 @@ * * TIMER - implementation of software timers. * - * Version: @(#)timer.c 1.0.7 05/25/93 + * Version: @(#)timer.c 1.28 22/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -24,6 +24,8 @@ * of inet_bh() with this socket being handled it goes * BOOM! Have to stop timer going off if inet_bh is * active or the destroy causes crashes. + * Alan Cox : Clean up for final release + * Alan Cox : Oops - timeouts destroyed permanent arp entries! * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,188 +43,202 @@ #include <asm/system.h> #include <linux/interrupt.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "arp.h" -void -delete_timer (struct sock *t) +void delete_timer (struct sock *t) { - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli(); + save_flags (flags); + cli(); - t->timeout = 0; - del_timer (&t->timer); + t->timeout = 0; + del_timer (&t->timer); - restore_flags (flags); + restore_flags (flags); } -void -reset_timer (struct sock *t, int timeout, unsigned long len) +void reset_timer (struct sock *t, int timeout, unsigned long len) { - delete_timer (t); - - if (timeout != -1) - t->timeout = timeout; + delete_timer (t); + if (timeout != -1) + t->timeout = timeout; #if 1 - /* FIXME: ??? */ - if ((int) len < 0) /* prevent close to infinite timers. THEY _DO_ */ - len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ + /* FIXME: ??? */ + if ((int) len < 0) /* prevent close to infinite timers. THEY _DO_ */ + len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ #endif - t->timer.expires = len; - add_timer (&t->timer); + t->timer.expires = len; + add_timer (&t->timer); } /* - * Now we will only be called whenever we need to do - * something, but we must be sure to process all of the - * sockets that need it. + * Now we will only be called whenever we need to do + * something, but we must be sure to process all of the + * sockets that need it. */ -void -net_timer (unsigned long data) + +void net_timer (unsigned long data) { - struct sock *sk = (struct sock*)data; - int why = sk->timeout; - /* timeout is overwritten by 'delete_timer' and 'reset_timer' */ - - if (sk->inuse || in_inet_bh()) { - sk->timer.expires = 10; - add_timer(&sk->timer); - return; - } - sk->inuse = 1; - - DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why)); - if (sk->keepopen) - reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); - - /* Always see if we need to send an ack. */ - if (sk->ack_backlog) { - sk->prot->read_wakeup (sk); - if (! sk->dead) - wake_up (sk->sleep); - } - - /* Now we need to figure out why the socket was on the timer. */ - switch (why) { - case TIME_DONE: - if (! sk->dead || sk->state != TCP_CLOSE) { - printk ("non dead socket in time_done\n"); - release_sock (sk); - break; - } - destroy_sock (sk); - break; - case TIME_DESTROY: - /* We've waited for a while for all the memory associated with - * the socket to be freed. We need to print an error message. - */ - if(sk->wmem_alloc!=0 || sk->rmem_alloc!=0) - { - DPRINTF ((DBG_TMR, "possible memory leak. sk = %X\n", sk)); - sk->wmem_alloc++; /* So it DOESNT go away */ - destroy_sock (sk); - sk->wmem_alloc--; /* Might now have hit 0 - fall through and do it again if so */ - sk->inuse = 0; /* This will be ok, the destroy won't totally work */ - } - if(sk->wmem_alloc==0 && sk->rmem_alloc==0) - destroy_sock(sk); /* Socket gone, DONT update sk->inuse! */ - break; - case TIME_CLOSE: - /* We've waited long enough, close the socket. */ - sk->state = TCP_CLOSE; - delete_timer (sk); - /* Kill the ARP entry in case the hardware has changed. */ - arp_destroy (sk->daddr); - if (!sk->dead) - wake_up (sk->sleep); - sk->shutdown = SHUTDOWN_MASK; - reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME); - release_sock (sk); - break; - case TIME_WRITE: /* try to retransmit. */ - /* It could be we got here because we needed to send an ack. - * So we need to check for that. - */ - if (sk->send_head) { - if (jiffies < (sk->send_head->when + backoff (sk->backoff) - * (2 * sk->mdev + sk->rtt))) { - reset_timer (sk, TIME_WRITE, (sk->send_head->when - + backoff (sk->backoff) * (2 * sk->mdev + sk->rtt)) - jiffies); - release_sock (sk); - break; - } - /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq, - sk->retransmits, sk->packets_out, sk->cong_window); */ - DPRINTF ((DBG_TMR, "retransmitting.\n")); - sk->prot->retransmit (sk, 0); - if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) - || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) { - DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n")); - arp_destroy (sk->daddr); - ip_route_check (sk->daddr); - } - if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) { - DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); - sk->err = ETIMEDOUT; - if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 - || sk->state == TCP_LAST_ACK) { - sk->state = TCP_TIME_WAIT; - reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - } else { - sk->prot->close (sk, 1); - break; - } - } - } - release_sock (sk); - break; - case TIME_KEEPOPEN: - /* Send something to keep the connection open. */ - if (sk->prot->write_wakeup) - sk->prot->write_wakeup (sk); - sk->retransmits++; - if (sk->shutdown == SHUTDOWN_MASK) { - sk->prot->close (sk, 1); - sk->state = TCP_CLOSE; - } - - if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) - || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) { - DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n")); - arp_destroy (sk->daddr); - ip_route_check (sk->daddr); - release_sock (sk); - break; - } - if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) { - DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n")); - arp_destroy (sk->daddr); - sk->err = ETIMEDOUT; - if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) { - sk->state = TCP_TIME_WAIT; - if (!sk->dead) - wake_up (sk->sleep); - release_sock (sk); - } else { - sk->prot->close (sk, 1); - } - break; - } - release_sock (sk); - break; - default: - printk ("net timer expired - reason unknown, sk=%08X\n", (int)sk); - release_sock (sk); - break; - } + struct sock *sk = (struct sock*)data; + int why = sk->timeout; + /* timeout is overwritten by 'delete_timer' and 'reset_timer' */ + + if (sk->inuse || in_inet_bh()) + { + sk->timer.expires = 10; + add_timer(&sk->timer); + return; + } + sk->inuse = 1; + + DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why)); + if (sk->keepopen) + reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + + /* Always see if we need to send an ack. */ + if (sk->ack_backlog) + { + sk->prot->read_wakeup (sk); + if (! sk->dead) + wake_up (sk->sleep); + } + + /* Now we need to figure out why the socket was on the timer. */ + switch (why) + { + case TIME_DONE: + if (! sk->dead || sk->state != TCP_CLOSE) + { + printk ("non dead socket in time_done\n"); + release_sock (sk); + break; + } + destroy_sock (sk); + break; + case TIME_DESTROY: + /* We've waited for a while for all the memory associated with + * the socket to be freed. We need to print an error message. + */ + if(sk->wmem_alloc!=0 || sk->rmem_alloc!=0) + { + DPRINTF ((DBG_TMR, "possible memory leak. sk = %X\n", sk)); + sk->wmem_alloc++; /* So it DOESNT go away */ + destroy_sock (sk); + sk->wmem_alloc--; /* Might now have hit 0 - fall through and do it again if so */ + sk->inuse = 0; /* This will be ok, the destroy won't totally work */ + } + if(sk->wmem_alloc==0 && sk->rmem_alloc==0) + destroy_sock(sk); /* Socket gone, DONT update sk->inuse! */ + break; + case TIME_CLOSE: + /* We've waited long enough, close the socket. */ + sk->state = TCP_CLOSE; + delete_timer (sk); + /* Kill the ARP entry in case the hardware has changed. */ + arp_destroy_maybe (sk->daddr); + if (!sk->dead) + wake_up (sk->sleep); + sk->shutdown = SHUTDOWN_MASK; + reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME); + release_sock (sk); + break; + case TIME_WRITE: /* try to retransmit. */ + /* It could be we got here because we needed to send an ack. + * So we need to check for that. + */ + if (sk->send_head) + { + if (jiffies < (sk->send_head->when + backoff (sk->backoff) + * (2 * sk->mdev + sk->rtt))) + { + reset_timer (sk, TIME_WRITE, (sk->send_head->when + + backoff (sk->backoff) * (2 * sk->mdev + sk->rtt)) - jiffies); + release_sock (sk); + break; + } + /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq, + sk->retransmits, sk->packets_out, sk->cong_window); */ + DPRINTF ((DBG_TMR, "retransmitting.\n")); + sk->prot->retransmit (sk, 0); + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) + { + DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n")); + arp_destroy_maybe (sk->daddr); + ip_route_check (sk->daddr); + } + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) + { + DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); + sk->err = ETIMEDOUT; + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 + || sk->state == TCP_LAST_ACK) + { + sk->state = TCP_TIME_WAIT; + reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } + else + { + sk->prot->close (sk, 1); + break; + } + } + } + release_sock (sk); + break; + case TIME_KEEPOPEN: + /* Send something to keep the connection open. */ + if (sk->prot->write_wakeup) + sk->prot->write_wakeup (sk); + sk->retransmits++; + if (sk->shutdown == SHUTDOWN_MASK) + { + sk->prot->close (sk, 1); + sk->state = TCP_CLOSE; + } + + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) + { + DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n")); + arp_destroy_maybe (sk->daddr); + ip_route_check (sk->daddr); + release_sock (sk); + break; + } + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) + { + DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n")); + arp_destroy_maybe (sk->daddr); + sk->err = ETIMEDOUT; + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) + { + sk->state = TCP_TIME_WAIT; + if (!sk->dead) + wake_up (sk->sleep); + release_sock (sk); + } + else + { + sk->prot->close (sk, 1); + } + break; + } + release_sock (sk); + break; + default: + printk ("net timer expired - reason unknown, sk=%08X\n", (int)sk); + release_sock (sk); + break; + } } diff --git a/net/inet/udp.c b/net/inet/udp.c index 78066c3..33d2b38 100644 --- a/net/inet/udp.c +++ b/net/inet/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: @(#)udp.c 1.0.13 06/02/93 + * Version: @(#)udp.c 1.28 22/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -30,10 +30,11 @@ * bug no longer crashes it. * Fred Van Kempen : Net2e support for sk->broadcast. * Alan Cox : Uses skb_free_datagram + * Alan Cox : Tidy up ready for the 'real' thing. + * Alan Cox : Added get/set sockopt support. + * Alan Cox : Broadcasting without option set returns EACCES. + * Alan Cox : No wakeup calls. Instead we now use the callbacks. * - * To Do: - * Verify all the error codes from UDP operations match the - * BSD behaviour, since thats effectively the formal spec. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,12 +55,12 @@ #include <linux/termios.h> #include <linux/mm.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "ip.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" -#include "sock.h" +#include "sockinet.h" #include "udp.h" #include "icmp.h" @@ -67,17 +68,18 @@ #define min(a,b) ((a)<(b)?(a):(b)) -static void -print_udp(struct udphdr *uh) +static void print_udp(struct udphdr *uh) { - if (inet_debug != DBG_UDP) return; - - if (uh == NULL) { - printk("(NULL)\n"); - return; - } - printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest)); - printk(" len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check)); + if (inet_debug != DBG_UDP) + return; + + if (uh == NULL) + { + printk("(NULL)\n"); + return; + } + printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest)); + printk(" len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check)); } @@ -91,285 +93,289 @@ print_udp(struct udphdr *uh) * header points to the first 8 bytes of the udp header. We need * to find the appropriate port. */ -void -udp_err(int err, unsigned char *header, unsigned long daddr, + +void udp_err(int err, unsigned char *header, unsigned long daddr, unsigned long saddr, struct inet_protocol *protocol) { - struct udphdr *th; - struct sock *sk; - struct iphdr *ip=(struct iphdr *)header; - - header += 4*ip->ihl; - - th = (struct udphdr *)header; - - DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\ -sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest)); + struct udphdr *th; + struct sock *sk; + struct iphdr *ip=(struct iphdr *)header; - sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr); + header += 4*ip->ihl; - if (sk == NULL) - return; /* No socket for error */ + th = (struct udphdr *)header; + + DPRINTF((DBG_UDP,"UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n\ + sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th->dest)); + + sk = get_sock(&udp_prot, th->source, daddr, th->dest, saddr); + + if (sk == NULL) + return; /* No socket for error */ - if (err < 0) /* As per the calling spec */ - { - sk->err = -err; - wake_up(sk->sleep); /* User process wakes to see error */ - return; - } + if (err < 0) /* As per the calling spec */ + { + sk->err = -err; + sk->error_report(sk); /* User process wakes to see error */ + return; + } - if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) { /* Slow down! */ - if (sk->cong_window > 1) - sk->cong_window = sk->cong_window/2; - return; - } - - sk->err = icmp_err_convert[err & 0xff].errno; - - /* It's only fatal if we have connected to them. */ - if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) { - sk->err=ECONNREFUSED; - } - wake_up(sk->sleep); + if (err & 0xff00 ==(ICMP_SOURCE_QUENCH << 8)) + { /* Slow down! */ + if (sk->cong_window > 1) + sk->cong_window = sk->cong_window/2; + return; + } + + sk->err = icmp_err_convert[err & 0xff].errno; + + /* It's only fatal if we have connected to them. */ + if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) + { + sk->err=ECONNREFUSED; + } + sk->error_report(sk); } -static unsigned short -udp_check(struct udphdr *uh, int len, +static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr) { - unsigned long sum; + unsigned long sum; - DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n", + DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n", uh, len, saddr, daddr)); - print_udp(uh); - - __asm__("\t addl %%ecx,%%ebx\n" - "\t adcl %%edx,%%ebx\n" - "\t adcl $0, %%ebx\n" - : "=b"(sum) - : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256) - : "cx","bx","dx" ); - - if (len > 3) { - __asm__("\tclc\n" - "1:\n" - "\t lodsl\n" - "\t adcl %%eax, %%ebx\n" - "\t loop 1b\n" - "\t adcl $0, %%ebx\n" - : "=b"(sum) , "=S"(uh) - : "0"(sum), "c"(len/4) ,"1"(uh) - : "ax", "cx", "bx", "si" ); - } - - /* Convert from 32 bits to 16 bits. */ - __asm__("\t movl %%ebx, %%ecx\n" - "\t shrl $16,%%ecx\n" - "\t addw %%cx, %%bx\n" - "\t adcw $0, %%bx\n" - : "=b"(sum) - : "0"(sum) - : "bx", "cx"); - - /* Check for an extra word. */ - if ((len & 2) != 0) { - __asm__("\t lodsw\n" - "\t addw %%ax,%%bx\n" - "\t adcw $0, %%bx\n" - : "=b"(sum), "=S"(uh) - : "0"(sum) ,"1"(uh) - : "si", "ax", "bx"); - } - - /* Now check for the extra byte. */ - if ((len & 1) != 0) { - __asm__("\t lodsb\n" - "\t movb $0,%%ah\n" - "\t addw %%ax,%%bx\n" - "\t adcw $0, %%bx\n" - : "=b"(sum) - : "0"(sum) ,"S"(uh) - : "si", "ax", "bx"); - } - - /* We only want the bottom 16 bits, but we never cleared the top 16. */ - return((~sum) & 0xffff); + print_udp(uh); + + __asm__("\t addl %%ecx,%%ebx\n" + "\t adcl %%edx,%%ebx\n" + "\t adcl $0, %%ebx\n" + : "=b"(sum) + : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_UDP*256) + : "cx","bx","dx" ); + + if (len > 3) + { + __asm__("\tclc\n" + "1:\n" + "\t lodsl\n" + "\t adcl %%eax, %%ebx\n" + "\t loop 1b\n" + "\t adcl $0, %%ebx\n" + : "=b"(sum) , "=S"(uh) + : "0"(sum), "c"(len/4) ,"1"(uh) + : "ax", "cx", "bx", "si" ); + } + + /* Convert from 32 bits to 16 bits. */ + __asm__("\t movl %%ebx, %%ecx\n" + "\t shrl $16,%%ecx\n" + "\t addw %%cx, %%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum) + : "0"(sum) + : "bx", "cx"); + + /* Check for an extra word. */ + if ((len & 2) != 0) + { + __asm__("\t lodsw\n" + "\t addw %%ax,%%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum), "=S"(uh) + : "0"(sum) ,"1"(uh) + : "si", "ax", "bx"); + } + + /* Now check for the extra byte. */ + if ((len & 1) != 0) + { + __asm__("\t lodsb\n" + "\t movb $0,%%ah\n" + "\t addw %%ax,%%bx\n" + "\t adcw $0, %%bx\n" + : "=b"(sum) + : "0"(sum) ,"S"(uh) + : "si", "ax", "bx"); + } + + /* We only want the bottom 16 bits, but we never cleared the top 16. */ + return((~sum) & 0xffff); } +/* + * Calculate the UDP checksum. Note 0 becomes FFFF because 0 means + * 'no checksum'. + */ -static void -udp_send_check(struct udphdr *uh, unsigned long saddr, +static void udp_send_check(struct udphdr *uh, unsigned long saddr, unsigned long daddr, int len, struct sock *sk) { - uh->check = 0; - if (sk && sk->no_check) - return; - uh->check = udp_check(uh, len, saddr, daddr); - if (uh->check == 0) uh->check = 0xffff; + uh->check = 0; + if (sk && sk->no_check) + return; + uh->check = udp_check(uh, len, saddr, daddr); + if (uh->check == 0) + uh->check = 0xffff; } -static int -udp_send(struct sock *sk, struct sockaddr_in *sin, +static int udp_send(struct sock *sk, struct sockaddr_in *sin, unsigned char *from, int len) { - struct sk_buff *skb; - struct device *dev; - struct udphdr *uh; - unsigned char *buff; - unsigned long saddr; - int size, tmp; - int err; + struct sk_buff *skb; + struct device *dev; + struct udphdr *uh; + unsigned char *buff; + unsigned long saddr; + int size, tmp; + int err; - DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n", + DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n", in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port), from, len)); - err=verify_area(VERIFY_READ, from, len); - if(err) - return(err); - - /* Allocate a copy of the packet. */ - size = sizeof(struct sk_buff) + sk->prot->max_header + len; - skb = (struct sk_buff *) sk->prot->wmalloc(sk, size, 0, GFP_KERNEL); - if (skb == NULL) return(-ENOMEM); - - skb->mem_addr = skb; - skb->mem_len = size; - skb->sk = NULL; /* to avoid changing sk->saddr */ - skb->free = 1; - skb->arp = 0; - - /* Now build the IP and MAC header. */ - buff = (unsigned char *) (skb+1); - saddr = 0; - dev = NULL; - DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n", + err=verify_area(VERIFY_READ, from, len); + if(err) + return(err); + + /* Allocate a copy of the packet. */ + size = sizeof(struct sk_buff) + sk->prot->max_header + len; + skb = (struct sk_buff *) sk->prot->wmalloc(sk, size, 0, GFP_KERNEL); + if (skb == NULL) + return(-ENOMEM); + + skb->sk = NULL; /* to avoid changing sk->saddr */ + skb->free = 1; + skb->arp = 0; + + /* Now build the IP and MAC header. */ + buff = (unsigned char *) (skb+1); + saddr = 0; + dev = NULL; + DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n", saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len)); - tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr, - &dev, IPPROTO_UDP, sk->opt, skb->mem_len); - skb->sk=sk; /* So memory is freed correctly */ + tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr, + &dev, IPPROTO_UDP, sk->opt, skb->mem_len, + sk->ip_ttl,sk->ip_tos); + skb->sk=sk; /* So memory is freed correctly */ - if (tmp < 0 ) { - sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); - return(tmp); - } - buff += tmp; - saddr = dev->pa_addr; - DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp)); - - skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */ - skb->dev = dev; -#ifdef OLD - /* - * This code used to hack in some form of fragmentation. - * I removed that, since it didn't work anyway, and it made the - * code a bad thing to read and understand. -FvK - */ - if (len > dev->mtu) { -#else - if (skb->len > 4095) - { -#endif - printk("UDP: send: length %d > mtu %d (ignored)\n", len, dev->mtu); - sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); - return(-EMSGSIZE); - } - - /* Fill in the UDP header. */ - uh = (struct udphdr *) buff; - uh->len = htons(len + sizeof(struct udphdr)); - uh->source = sk->dummy_th.source; - uh->dest = sin->sin_port; - buff = (unsigned char *) (uh + 1); - - /* Copy the user data. */ - memcpy_fromfs(buff, from, len); - - /* Set up the UDP checksum. */ - udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk); - - /* Send the datagram to the interface. */ - sk->prot->queue_xmit(sk, dev, skb, 1); - - return(len); + if (tmp < 0 ) + { + sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); + return(tmp); + } + buff += tmp; + saddr = dev->pa_addr; + DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp)); + + skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */ + skb->dev = dev; + if (skb->len > 4095) + { + printk("UDP: send: length %d > mtu %d (ignored)\n", len, 4095); + sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); + return(-EMSGSIZE); + } + + /* Fill in the UDP header. */ + uh = (struct udphdr *) buff; + uh->len = htons(len + sizeof(struct udphdr)); + uh->source = sk->dummy_th.source; + uh->dest = sin->sin_port; + buff = (unsigned char *) (uh + 1); + + /* Copy the user data. */ + memcpy_fromfs(buff, from, len); + + /* Set up the UDP checksum. */ + udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk); + + /* Send the datagram to the interface. */ + sk->prot->queue_xmit(sk, dev, skb, 1); + + return(len); } -static int -udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock, +static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len) { - struct sockaddr_in sin; - int tmp; - int err; - - DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags)); - - /* Check the flags. */ - if (flags) - return(-EINVAL); - if (len < 0) - return(-EINVAL); - if (len == 0) - return(0); - - /* Get and verify the address. */ - if (usin) { - if (addr_len < sizeof(sin)) return(-EINVAL); - err=verify_area(VERIFY_READ, usin, sizeof(sin)); - if(err) - return err; - memcpy_fromfs(&sin, usin, sizeof(sin)); - if (sin.sin_family && sin.sin_family != AF_INET) - return(-EINVAL); - if (sin.sin_port == 0) - return(-EINVAL); - } else { - if (sk->state != TCP_ESTABLISHED) return(-EINVAL); - sin.sin_family = AF_INET; - sin.sin_port = sk->dummy_th.dest; - sin.sin_addr.s_addr = sk->daddr; - } + struct sockaddr_in sin; + int tmp; + int err; + + DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags)); + + /* Check the flags. */ + if (flags) + return(-EINVAL); + if (len < 0) + return(-EINVAL); + if (len == 0) + return(0); + + /* Get and verify the address. */ + if (usin) + { + if (addr_len < sizeof(sin)) + return(-EINVAL); + err=verify_area(VERIFY_READ, usin, sizeof(sin)); + if(err) + return err; + memcpy_fromfs(&sin, usin, sizeof(sin)); + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EINVAL); + if (sin.sin_port == 0) + return(-EINVAL); + } + else + { + if (sk->state != TCP_ESTABLISHED) + return(-EINVAL); + sin.sin_family = AF_INET; + sin.sin_port = sk->dummy_th.dest; + sin.sin_addr.s_addr = sk->daddr; + } - if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) - return -ENETUNREACH; /* Must turn broadcast on first */ - sk->inuse = 1; + if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) + return -EACCES; /* Must turn broadcast on first */ + sk->inuse = 1; - /* Send the packet. */ - tmp = udp_send(sk, &sin, from, len); + /* Send the packet. */ + tmp = udp_send(sk, &sin, from, len); - /* The datagram has been sent off. Release the socket. */ - release_sock(sk); - return(tmp); + /* The datagram has been sent off. Release the socket. */ + release_sock(sk); + return(tmp); } -static int -udp_write(struct sock *sk, unsigned char *buff, int len, int noblock, +static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { - return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0)); + return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0)); } -int -udp_ioctl(struct sock *sk, int cmd, unsigned long arg) +int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) { - int err; - switch(cmd) { - case DDIOCSDBG: + int err; + switch(cmd) + { + case DDIOCSDBG: { int val; - if (!suser()) return(-EPERM); + if (!suser()) + return(-EPERM); err=verify_area(VERIFY_READ, (void *)arg, sizeof(int)); if(err) return err; val = get_fs_long((int *)arg); - switch(val) { + switch(val) + { case 0: inet_debug = 0; break; @@ -381,12 +387,14 @@ udp_ioctl(struct sock *sk, int cmd, unsigned long arg) } } break; - case TIOCOUTQ: + + case TIOCOUTQ: { unsigned long amount; - if (sk->state == TCP_LISTEN) return(-EINVAL); - amount = sk->prot->wspace(sk)/*/2*/; + if (sk->state == TCP_LISTEN) + return(-EINVAL); + amount = sk->prot->wspace(sk); err=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); if(err) @@ -395,15 +403,17 @@ udp_ioctl(struct sock *sk, int cmd, unsigned long arg) return(0); } - case TIOCINQ: + case TIOCINQ: { struct sk_buff *skb; unsigned long amount; - if (sk->state == TCP_LISTEN) return(-EINVAL); + if (sk->state == TCP_LISTEN) + return(-EINVAL); amount = 0; skb = sk->rqueue; - if (skb != NULL) { + if (skb != NULL) + { /* * We will only return the amount * of this packet since that is all @@ -419,224 +429,233 @@ udp_ioctl(struct sock *sk, int cmd, unsigned long arg) return(0); } - default: - return(-EINVAL); - } - return(0); + default: + return(-EINVAL); + } + return(0); } /* - * This should be easy, if there is something there we\ + * This should be easy, if there is something there we * return it, otherwise we block. */ -int -udp_recvfrom(struct sock *sk, unsigned char *to, int len, +int udp_recvfrom(struct sock *sk, unsigned char *to, int len, int noblock, unsigned flags, struct sockaddr_in *sin, int *addr_len) { - int copied = 0; - struct sk_buff *skb; - int er; - - - /* - * This will pick up errors that occured while the program - * was doing something else. - */ - if (sk->err) { - int err; - - err = -sk->err; - sk->err = 0; - return(err); - } + int copied = 0; + struct sk_buff *skb; + int er; + + + /* + * This will pick up errors that occured while the program + * was doing something else. + */ + if (sk->err) + { + int err; + + err = -sk->err; + sk->err = 0; + return(err); + } - if (len == 0) - return(0); - if (len < 0) - return(-EINVAL); - - if (addr_len) { - er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); - if(er) - return(er); - put_fs_long(sizeof(*sin), addr_len); - } - if(sin) - { - er=verify_area(VERIFY_WRITE, sin, sizeof(*sin)); + if (len == 0) + return(0); + if (len < 0) + return(-EINVAL); + + if (addr_len) + { + er=verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); + if(er) + return(er); + put_fs_long(sizeof(*sin), addr_len); + } + if(sin) + { + er=verify_area(VERIFY_WRITE, sin, sizeof(*sin)); + if(er) + return(er); + } + er=verify_area(VERIFY_WRITE,to,len); if(er) - return(er); - } - er=verify_area(VERIFY_WRITE,to,len); - if(er) - return er; - skb=skb_recv_datagram(sk,flags,noblock,&er); - if(skb==NULL) - return er; - copied = min(len, skb->len); - + return er; + skb=skb_recv_datagram(sk,flags,noblock,&er); + if(skb==NULL) + return er; + copied = min(len, skb->len); + /* FIXME : should use udp header size info value */ - skb_copy_datagram(skb,sizeof(struct udphdr),to,copied); + skb_copy_datagram(skb,sizeof(struct udphdr),to,copied); /* Copy the address. */ - if (sin) { - struct sockaddr_in addr; - - addr.sin_family = AF_INET; - addr.sin_port = skb->h.uh->source; - addr.sin_addr.s_addr = skb->daddr; - memcpy_tofs(sin, &addr, sizeof(*sin)); - } + if (sin) + { + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = skb->h.uh->source; + addr.sin_addr.s_addr = skb->daddr; + memcpy_tofs(sin, &addr, sizeof(*sin)); + } - skb_free_datagram(skb); - release_sock(sk); - return(copied); + skb_free_datagram(skb); + release_sock(sk); + return(copied); } -int -udp_read(struct sock *sk, unsigned char *buff, int len, int noblock, +int udp_read(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags) { - return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); + return(udp_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); } -int -udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) +int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) { - struct sockaddr_in sin; - int er; + struct sockaddr_in sin; + int er; - if (addr_len < sizeof(sin)) - return(-EINVAL); + if (addr_len < sizeof(sin)) + return(-EINVAL); - er=verify_area(VERIFY_READ, usin, sizeof(sin)); - if(er) - return er; + er=verify_area(VERIFY_READ, usin, sizeof(sin)); + if(er) + return er; - memcpy_fromfs(&sin, usin, sizeof(sin)); - if (sin.sin_family && sin.sin_family != AF_INET) - return(-EAFNOSUPPORT); + memcpy_fromfs(&sin, usin, sizeof(sin)); + if (sin.sin_family && sin.sin_family != AF_INET) + return(-EAFNOSUPPORT); - if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) - return -ENETUNREACH; /* Must turn broadcast on first */ + if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) + return -EACCES; /* Must turn broadcast on first */ - sk->daddr = sin.sin_addr.s_addr; - sk->dummy_th.dest = sin.sin_port; - sk->state = TCP_ESTABLISHED; - return(0); + sk->daddr = sin.sin_addr.s_addr; + sk->dummy_th.dest = sin.sin_port; + sk->state = TCP_ESTABLISHED; + return(0); } -static void -udp_close(struct sock *sk, int timeout) +static void udp_close(struct sock *sk, int timeout) { - sk->inuse = 1; - sk->state = TCP_CLOSE; - if (sk->dead) destroy_sock(sk); - else release_sock(sk); + sk->inuse = 1; + sk->state = TCP_CLOSE; + if (sk->dead) + destroy_sock(sk); + else + release_sock(sk); } -/* All we need to do is get the socket, and then do a checksum. */ -int -udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, +/* + * All we need to do is get the socket, and then do a checksum. + */ + +int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol) { - struct sock *sk; - struct udphdr *uh; - - uh = (struct udphdr *) skb->h.uh; - sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr); - if (sk == NULL) - { - if (chk_addr(daddr) == IS_MYADDR) - { - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev); + struct sock *sk; + struct udphdr *uh; + + uh = (struct udphdr *) skb->h.uh; + sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr); + if (sk == NULL) + { + if (chk_addr(daddr) == IS_MYADDR) + { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev); + } + /* + * Hmm. We got an UDP broadcast to a port to which we + * don't wanna listen. The only thing we can do now is + * to ignore the packet... -FvK + */ + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + return(0); } - /* - * Hmm. We got an UDP broadcast to a port to which we - * don't wanna listen. The only thing we can do now is - * to ignore the packet... -FvK - */ - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - return(0); - } - - if (uh->check && udp_check(uh, len, saddr, daddr)) { - DPRINTF((DBG_UDP, "UDP: bad checksum\n")); - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - return(0); - } - - skb->sk = sk; - skb->dev = dev; - skb->len = len; - -/* These are supposed to be switched. */ - skb->daddr = saddr; - skb->saddr = daddr; - - - /* Charge it to the socket. */ - if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) - { - skb->sk = NULL; - kfree_skb(skb, FREE_WRITE); - release_sock(sk); - return(0); - } - sk->rmem_alloc += skb->mem_len; - /* At this point we should print the thing out. */ - DPRINTF((DBG_UDP, "<< \n")); - print_udp(uh); + if (uh->check && udp_check(uh, len, saddr, daddr)) + { + DPRINTF((DBG_UDP, "UDP: bad checksum\n")); + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + return(0); + } + + skb->sk = sk; + skb->dev = dev; + skb->len = len; + + /* These are supposed to be switched. */ + skb->daddr = saddr; + skb->saddr = daddr; + + + /* Charge it to the socket. */ + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) + { + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + release_sock(sk); + return(0); + } + sk->rmem_alloc += skb->mem_len; + + /* At this point we should print the thing out. */ + DPRINTF((DBG_UDP, "<< \n")); + print_udp(uh); - /* Now add it to the data chain and wake things up. */ + /* Now add it to the data chain and wake things up. */ - skb_queue_tail(&sk->rqueue,skb); + skb_queue_tail(&sk->rqueue,skb); - skb->len = len - sizeof(*uh); + skb->len = len - sizeof(*uh); - if (!sk->dead) wake_up(sk->sleep); + release_sock(sk); - release_sock(sk); - return(0); + if (!sk->dead) + sk->data_ready(sk,skb->len); + + return(0); } -struct proto udp_prot = { - sock_wmalloc, - sock_rmalloc, - sock_wfree, - sock_rfree, - sock_rspace, - sock_wspace, - udp_close, - udp_read, - udp_write, - udp_sendto, - udp_recvfrom, - ip_build_header, - udp_connect, - NULL, - ip_queue_xmit, - ip_retransmit, - NULL, - NULL, - udp_rcv, - datagram_select, - udp_ioctl, - NULL, - NULL, - 128, - 0, - {NULL,}, - "UDP" +struct proto udp_prot = +{ + sock_wmalloc, + sock_rmalloc, + sock_wfree, + sock_rfree, + sock_rspace, + sock_wspace, + udp_close, + udp_read, + udp_write, + udp_sendto, + udp_recvfrom, + ip_build_header, + udp_connect, + NULL, + ip_queue_xmit, + ip_retransmit, + NULL, + NULL, + udp_rcv, + datagram_select, + udp_ioctl, + NULL, + NULL, + ip_setsockopt, + ip_getsockopt, + 128, + 0, + {NULL,}, + "UDP" }; diff --git a/net/inet/utils.c b/net/inet/utils.c index f9e81e3..821fbe8 100644 --- a/net/inet/utils.c +++ b/net/inet/utils.c @@ -6,12 +6,13 @@ * Various kernel-resident INET utility functions; mainly * for format conversion and debugging output. * - * Version: @(#)utils.c 1.0.7 05/18/93 + * Version: @(#)utils.c 1.28 20/12/93 * * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * * Fixes: * Alan Cox : verify_area check. + * Alan Cox : Clean up to match code style * * * This program is free software; you can redistribute it and/or @@ -32,7 +33,7 @@ #include <linux/stat.h> #include <stdarg.h> #include "inet.h" -#include "dev.h" +#include "devinet.h" #include "eth.h" #include "ip.h" #include "protocol.h" @@ -41,107 +42,118 @@ #include "arp.h" -/* Display an IP address in readable format. */ +/* + * Display an IP address in readable format. + */ + char *in_ntoa(unsigned long in) { - static char buff[18]; - register char *p; + static char buff[18]; + register char *p; - p = (char *) ∈ - sprintf(buff, "%d.%d.%d.%d", - (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255)); - return(buff); + p = (char *) ∈ + sprintf(buff, "%d.%d.%d.%d", + (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255)); + return(buff); } -/* Convert an ASCII string to binary IP. */ -unsigned long -in_aton(char *str) +/* + * Convert an ASCII string to binary IP. + */ + +unsigned long in_aton(char *str) { - unsigned long l; - unsigned int val; - int i; - - l = 0; - for (i = 0; i < 4; i++) { - l <<= 8; - if (*str != '\0') { - val = 0; - while (*str != '\0' && *str != '.') { - val *= 10; - val += *str - '0'; - str++; + unsigned long l; + unsigned int val; + int i; + + l = 0; + for (i = 0; i < 4; i++) + { + l <<= 8; + if (*str != '\0') + { + val = 0; + while (*str != '\0' && *str != '.') + { + val *= 10; + val += *str - '0'; + str++; + } + l |= val; + if (*str != '\0') + str++; } - l |= val; - if (*str != '\0') str++; - } - } - return(htonl(l)); + } + return(htonl(l)); } -void -dprintf(int level, char *fmt, ...) +void dprintf(int level, char *fmt, ...) { - va_list args; - char *buff; - extern int vsprintf(char * buf, const char * fmt, va_list args); - - if (level != inet_debug) return; - - buff = (char *) kmalloc(256, GFP_ATOMIC); - if (buff != NULL) { - va_start(args, fmt); - vsprintf(buff, fmt, args); - va_end(args); - printk(buff); - kfree(buff); - } + va_list args; + char *buff; + extern int vsprintf(char * buf, const char * fmt, va_list args); + + if (level != inet_debug) + return; + + buff = (char *) kmalloc(256, GFP_ATOMIC); + if (buff != NULL) + { + va_start(args, fmt); + vsprintf(buff, fmt, args); + va_end(args); + printk(buff); + kfree(buff); + } } -int -dbg_ioctl(void *arg, int level) +int dbg_ioctl(void *arg, int level) { - int val; - int err; + int val; + int err; - if (!suser()) return(-EPERM); - err=verify_area(VERIFY_READ, (void *)arg, sizeof(int)); - if(err) - return err; - val = get_fs_long((int *)arg); - switch(val) { - case 0: /* OFF */ - inet_debug = DBG_OFF; - break; - case 1: /* ON, INET */ - inet_debug = level; - break; - - case DBG_RT: /* modules */ - case DBG_DEV: - case DBG_ETH: - case DBG_PROTO: - case DBG_TMR: - case DBG_PKT: - case DBG_RAW: - - case DBG_LOOPB: /* drivers */ - case DBG_SLIP: - - case DBG_ARP: /* protocols */ - case DBG_IP: - case DBG_ICMP: - case DBG_TCP: - case DBG_UDP: - - inet_debug = val; - break; - - default: - return(-EINVAL); - } - - return(0); + if (!suser()) + return(-EPERM); + err=verify_area(VERIFY_READ, (void *)arg, sizeof(int)); + if(err) + return err; + val = get_fs_long((int *)arg); + switch(val) + { + case 0: /* OFF */ + inet_debug = DBG_OFF; + break; + case 1: /* ON, INET */ + inet_debug = level; + break; + + case DBG_RT: /* modules */ + case DBG_DEV: + case DBG_ETH: + case DBG_PROTO: + case DBG_TMR: + case DBG_PKT: + case DBG_RAW: + + case DBG_LOOPB: /* drivers */ + case DBG_SLIP: + + case DBG_ARP: /* protocols */ + case DBG_IP: + case DBG_ICMP: + case DBG_TCP: + case DBG_UDP: + + inet_debug = val; + break; + + default: + return(-EINVAL); + } + + return(0); } diff --git a/net/socket/Makefile b/net/socket/Makefile new file mode 100644 index 0000000..b3d5b42 --- /dev/null +++ b/net/socket/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for the Linux socket support layer. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +CFLAGS := $(CFLAGS) -I../inet -I.. +CPP := $(CPP) -I../inet -I.. + +.c.o: + $(CC) $(CFLAGS) -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S -o $*.s $< + + +OBJS = datagram.o dev.o skbuff.o sock.o + +socket.o: $(OBJS) + $(LD) -r -o socket.o $(OBJS) + +dep: + $(CPP) -M *.c > .depend + +tar: + tar -cvf /dev/f1 . + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/net/socket/datagram.c b/net/socket/datagram.c new file mode 100644 index 0000000..b7ebe91 --- /dev/null +++ b/net/socket/datagram.c @@ -0,0 +1,213 @@ +/* + * SUCS NET2 Debugged. + * + * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top + * of these would make sense. Not tonight however 8-). + * This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly + * identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it. + * + * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code) + * + * Fixes: + * Alan Cox : NULL return from skb_peek_copy() understood + * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff. + * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but + * AX.25 now works right, and SPX is feasible. + * Alan Cox : Tidied up ready for the big day. + * Alan Cox : Fixed write select of no IP protocol crash. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/segment.h> +#include <asm/system.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include "inet.h" +#include "dev.h" +#include "ip.h" +#include "protocol.h" +#include "arp.h" +#include "route.h" +#include "tcp.h" +#include "udp.h" +#include "skbuff.h" +#include "sock.h" + + +/* + * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible + * races. This replaces identical code in packet,raw and udp, as well as the yet to + * be released IPX support. It also finally fixes the long standing peek and read + * race for datagram sockets. If you alter this routine remember it must be + * re-entrant. + */ + +struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) +{ + struct sk_buff *skb; + + /* Socket is inuse - so the timer doesn't attack it */ +restart: + sk->inuse = 1; + while(sk->rqueue == NULL) /* No data */ + { + /* If we are shutdown then no more data is going to appear. We are done */ + if (sk->shutdown & RCV_SHUTDOWN) + { + release_sock(sk); + *err=0; + return NULL; + } + + if(sk->err) + { + release_sock(sk); + *err=-sk->err; + sk->err=0; + return NULL; + } + + /* Sequenced packets can come disconnected. If so we report the problem */ + if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED) + { + release_sock(sk); + *err=-ENOTCONN; + return NULL; + } + + /* User doesn't want to wait */ + if (noblock) + { + release_sock(sk); + *err=-EAGAIN; + return NULL; + } + release_sock(sk); + + /* Interrupts off so that no packet arrives before we begin sleeping. + Otherwise we might miss our wake up */ + cli(); + if (sk->rqueue == NULL) + { + interruptible_sleep_on(sk->sleep); + /* Signals may need a restart of the syscall */ + if (current->signal & ~current->blocked) + { + sti(); + *err=-ERESTARTSYS; + return(NULL); + } + if(sk->err != 0) /* Error while waiting for packet + eg an icmp sent earlier by the + peer has finaly turned up now */ + { + *err = -sk->err; + sti(); + sk->err=0; + return NULL; + } + } + sk->inuse = 1; + sti(); + } + /* Again only user level code calls this function, so nothing interrupt level + will suddenely eat the rqueue */ + if (!(flags & MSG_PEEK)) + { + skb=skb_dequeue(&sk->rqueue); + if(skb!=NULL) + skb->users++; + else + goto restart; /* Avoid race if someone beats us to the data */ + } + else + { + cli(); + skb=skb_peek(&sk->rqueue); + if(skb!=NULL) + skb->users++; + sti(); + if(skb==NULL) /* shouldn't happen but .. */ + *err=-EAGAIN; + } + return skb; +} + +/* + * Free a datagram buffer. This has some conditions to watch. We keep + * a user count so the last user may free the buffer. If however the + * buffer has a valid next pointer it was never removed and all the + * users PEEKed at it - so don't free it yet. + */ + +void skb_free_datagram(struct sk_buff *skb) +{ + unsigned long flags; + + save_flags(flags); + cli(); + skb->users--; + if(skb->users>0) + { + restore_flags(flags); + return; + } + /* See if it needs destroying */ + if(skb->list == NULL) /* Been dequeued by someone - ie its read */ + kfree_skb(skb,FREE_READ); + restore_flags(flags); +} + +void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size) +{ + /* We will know all about the fraglist options to allow >4K receives + but not this release */ + memcpy_tofs(to,skb->h.raw+offset,size); +} + +/* + * Datagram select: Again totally generic. Moved from udp.c + * Now does seqpacket. + */ + +int datagram_select(struct sock *sk, int sel_type, select_table *wait) +{ + select_wait(sk->sleep, wait); + switch(sel_type) + { + case SEL_IN: + if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE) + { + /* Connection closed: Wake up */ + return(1); + } + if (sk->rqueue != NULL || sk->err != 0) + { /* This appears to be consistent + with other stacks */ + return(1); + } + return(0); + + case SEL_OUT: + if(sk->prot && sk->prot->wspace(sk) >= MIN_WRITE_SPACE) + { + return(1); + } + if(sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE) + { + return(1); + } + return(0); + + case SEL_EX: + if (sk->err) + return(1); /* Socket has gone into error state (eg icmp error) */ + return(0); + } + return(0); +} diff --git a/net/socket/dev.c b/net/socket/dev.c new file mode 100644 index 0000000..20ce0f6 --- /dev/null +++ b/net/socket/dev.c @@ -0,0 +1,980 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Interface (streams) handling functions. + * + * Version: @(#)dev.c 1.28 20/12/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * + * Fixes: + * Alan Cox: check_addr returns a value for a wrong subnet + * ie not us but don't forward this! + * Alan Cox: block timer if the inet_bh handler is running + * Alan Cox: generic queue code added. A lot neater now + * C.E.Hawkins: SIOCGIFCONF only reports 'upped' interfaces + * C.E.Hawkins: IFF_PROMISC support + * Alan Cox: Supports Donald Beckers new hardware + * multicast layer, but not yet multicast lists. + * Alan Cox: ip_addr_match problems with class A/B nets. + * C.E.Hawkins IP 0.0.0.0 and also same net route fix. [FIXME: Ought to cause ICMP_REDIRECT] + * Alan Cox: Removed bogus subnet check now the subnet code + * a) actually works for all A/B nets + * b) doesn't forward off the same interface. + * Alan Cox: Multiple extra protocols + * Alan Cox: A Couple more escaped verify_area calls die + * Alan Cox: IP_SET_DEV is gone (forever) as per Fred's comment. + * Alan Cox: Grand tidy up ready for the big day. + * Alan Cox: Handles dev_open errors correctly. + * Alan Cox: IP part split from main section + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/if_ether.h> +#include "inet.h" +#include "dev.h" +#include "eth.h" +#include "ip.h" +#include "route.h" +#include "protocol.h" +#include "tcp.h" +#include "skbuff.h" +#include "sock.h" +#include "arp.h" +#ifdef CONFIG_AX25 +#include "ax25/ax25.h" +#endif +#ifdef CONFIG_IPX +#include "ipx/ipx.h" +#endif + + +#ifdef CONFIG_IPX + +/* + * These describe how each lowest level protocol is processed. + */ + +static struct packet_type ipx_8023_type = +{ + NET16(ETH_P_802_3), + 0, + ipx_rcv, + NULL, + NULL +}; + +static struct packet_type ipx_packet_type = +{ + NET16(ETH_P_IPX), /* IPX over DIX - eg PDIPX */ + 0, + ipx_rcv, + NULL, + &ipx_8023_type +}; + +#endif + +#ifdef CONFIG_AX25 + +static struct packet_type ax25_packet_type = +{ + NET16(ETH_P_AX25), + 0, + ax25_rcv, + NULL, +#ifdef CONFIG_IPX + &ipx_packet_type +#else + NULL +#endif +}; +#endif + + +static struct packet_type arp_packet_type = +{ + NET16(ETH_P_ARP), + 0, /* copy */ + arp_rcv, + NULL, +#ifdef CONFIG_IPX +#ifndef CONFIG_AX25 + &ipx_packet_type +#else + &ax25_packet_type +#endif +#else + NULL /* next */ +#endif +}; + + +static struct packet_type ip_packet_type = +{ + NET16(ETH_P_IP), + 0, /* copy */ + ip_rcv, + NULL, + &arp_packet_type +}; + +/* + * The list of known protocols. Note that + * SOCK_PACKET sockets dynamically alter this. + */ + +#ifdef CONFIG_INET +struct packet_type *ptype_base = &ip_packet_type; +#else +#ifdef CONFIG_AX25 +struct packet_type *ptype_base = &ax25_packet_type; +#else +struct packet_type *ptype_base = &ipx_packet_type; +#endif +#endif + +/* A queue of all the packets we have to deal with */ +static struct sk_buff *volatile backlog = NULL; + + + +/* + * Return the lesser of the two values. + */ + +static unsigned long +min(unsigned long a, unsigned long b) +{ + if (a < b) + return(a); + return(b); +} + + + + +/* + * Add a protocol ID to the list of known protocols + * (see SOCK_PACKET code) + */ + +void dev_add_pack(struct packet_type *pt) +{ + struct packet_type *p1; + + pt->next = ptype_base; + + /* See if we need to copy it. */ + for (p1 = ptype_base; p1 != NULL; p1 = p1->next) + { + if (p1->type == pt->type) + { + pt->copy = 1; + break; + } + } + ptype_base = pt; +} + + +/* + * Remove a protocol ID from the list. Also used + * for SOCK_PACKET. Maybe one day we will do loadable + * protocol layers too. + */ + +void dev_remove_pack(struct packet_type *pt) +{ + struct packet_type *lpt, *pt1; + + if (pt == ptype_base) + { + ptype_base = pt->next; + return; + } + + lpt = NULL; + for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) + { + if (pt1->next == pt ) + { + cli(); + if (!pt->copy && lpt) + lpt->copy = 0; + pt1->next = pt->next; + sti(); + return; + } + + if (pt1->next -> type == pt ->type) + { + lpt = pt1->next; + } + } +} + + +/* + * Find an interface in the list. + */ + +struct device *dev_get(char *name) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (strcmp(dev->name, name) == 0) + return(dev); + } + return(NULL); +} + + +/* + * Prepare an interface for use. + */ + +int dev_open(struct device *dev) +{ + int ret = 0; + + if (dev->open) + ret = dev->open(dev); + if (ret == 0) + dev->flags |= (IFF_UP | IFF_RUNNING); + + return(ret); +} + + +/* + * Completely shutdown an interface. + */ + +int dev_close(struct device *dev) +{ + if (dev->flags != 0) + { + int ct=0; + dev->flags = 0; + if (dev->stop) + dev->stop(dev); + rt_flush(dev); + dev->pa_addr = 0; + dev->pa_dstaddr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + /* Purge any queued packets when we down the link */ + while(ct<DEV_NUMBUFFS) + { + struct sk_buff *skb; + while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL) + if(skb->free) + kfree_skb(skb,FREE_WRITE); + ct++; + } + } + + return(0); +} + + +/* + * Send (or queue for sending) a packet. + */ + +void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) +{ + int where = 0; /* used to say if the packet should go */ + /* at the front or the back of the */ + /* queue. */ + + DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n", + skb, dev, pri)); + + if (dev == NULL) + { + printk("dev.c: dev_queue_xmit: dev = NULL\n"); + return; + } + + IS_SKB(skb); + + skb->dev = dev; + if (skb->next != NULL) + { + /* Make sure we haven't missed an interrupt. */ + dev->hard_start_xmit(NULL, dev); + return; + } + + if (pri < 0) + { + pri = -pri-1; + where = 1; + } + + if (pri >= DEV_NUMBUFFS) + { + printk("bad priority in dev_queue_xmit.\n"); + pri = 1; + } + + if (dev->hard_start_xmit(skb, dev) == 0) + { + /* It went out without fuss */ + return; + } + + /* The driver was busy.. */ + /* Put skb into a bidirectional circular linked list. */ + DPRINTF((DBG_DEV, "dev_queue_xmit dev->buffs[%d]=%X\n", + pri, dev->buffs[pri])); + + /* Interrupts should already be cleared by hard_start_xmit. */ + cli(); + skb->magic = DEV_QUEUE_MAGIC; + if(where) + skb_queue_head(&dev->buffs[pri],skb); + else + skb_queue_tail(&dev->buffs[pri],skb); + skb->magic = DEV_QUEUE_MAGIC; + sti(); +} + +/* + * Receive a packet from a device driver and queue it for the upper + * (protocol) levels. It always succeeds. + */ + +void netif_rx(struct sk_buff *skb) +{ + /* Set any necessary flags. */ + skb->sk = NULL; + skb->free = 1; + + /* and add it to the "backlog" queue. */ + IS_SKB(skb); + skb_queue_tail(&backlog,skb); + + /* If any packet arrived, mark it for processing. */ + if (backlog != NULL) + mark_bh(INET_BH); + + return; +} + + +/* + * The old interface to fetch a packet from a device driver. + * This function is the base level entry point for all drivers that + * want to send a packet to the upper (protocol) levels. It takes + * care of de-multiplexing the packet to the various modules based + * on their protocol ID. + * + * Return values: 1 <- exit I can't do any more + * 0 <- feed me more (i.e. "done", "OK"). + * + * THIS FUNCTION IS OBSOLETE: DO NOT USE IT ANY MORE!!!! + */ +int dev_rint(unsigned char *buff, long len, int flags, struct device *dev) +{ + static int dropping = 0; + struct sk_buff *skb = NULL; + unsigned char *to; + int amount, left; + int len2; + + if (dev == NULL || buff == NULL || len <= 0) + return(1); + if (flags & IN_SKBUFF) + { + skb = (struct sk_buff *) buff; + } + else + { + if (dropping) + { + if (backlog != NULL) + return(1); + printk("INET: dev_rint: no longer dropping packets.\n"); + dropping = 0; + } + + skb = alloc_skb(sizeof(*skb) + len, GFP_ATOMIC); + if (skb == NULL) + { + printk("dev_rint: packet dropped on %s (no memory) !\n", + dev->name); + dropping = 1; + return(1); + } + + /* First we copy the packet into a buffer, and save it for later. */ + to = (unsigned char *) (skb + 1); + left = len; + len2 = len; + while (len2 > 0) + { + amount = min(len2, (unsigned long) dev->rmem_end - + (unsigned long) buff); + memcpy(to, buff, amount); + len2 -= amount; + left -= amount; + buff += amount; + to += amount; + if ((unsigned long) buff == dev->rmem_end) + buff = (unsigned char *) dev->rmem_start; + } + } + skb->len = len; + skb->dev = dev; + skb->free = 1; + + netif_rx(skb); + /* OK, all done. */ + return(0); +} + + +/* + * This routine causes all interfaces to try to send some data. + */ + +void dev_transmit(void) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (!dev->tbusy) + { + dev_tint(dev); + } + } +} + + +/* We need to know if we are re-entering the bottom level handler + for the network */ + +static volatile char in_bh = 0; + +int in_inet_bh() /* Used by timer.c */ +{ + return(in_bh==0?0:1); +} + +/* + * This function gets called periodically, to see if we can + * process any data that came in from some interface. + * + */ +void inet_bh(void *tmp) +{ + struct sk_buff *skb; + struct packet_type *ptype; + unsigned short type; + unsigned char flag = 0; + + + /* Atomically check and mark our BUSY state. */ + if (set_bit(1, (void*)&in_bh)) + return; + + /* Can we send anything now? */ + dev_transmit(); + + /* Any data left to process? */ + while((skb=skb_dequeue(&backlog))!=NULL) + { + flag=0; + sti(); + /* + * Bump the pointer to the next structure. + * This assumes that the basic 'skb' pointer points to + * the MAC header, if any (as indicated by its "length" + * field). Take care now! + */ + skb->h.raw = (unsigned char *) (skb + 1) + skb->dev->hard_header_len; + skb->len -= skb->dev->hard_header_len; + + /* + * Fetch the packet protocol ID. This is also quite ugly, as + * it depends on the protocol driver (the interface itself) to + * know what the type is, or where to get it from. The Ethernet + * interfaces fetch the ID from the two bytes in the Ethernet MAC + * header (the h_proto field in struct ethhdr), but drivers like + * SLIP and PLIP have no alternative but to force the type to be + * IP or something like that. Sigh- FvK + */ + type = skb->dev->type_trans(skb, skb->dev); + + /* + * We got a packet ID. Now loop over the "known protocols" + * table (which is actually a linked list, but this will + * change soon if I get my way- FvK), and forward the packet + * to anyone who wants it. + */ + for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) + { + if (ptype->type == type) + { + struct sk_buff *skb2; + + if (ptype->copy) + { /* copy if we need to */ + skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC); + if (skb2 == NULL) + continue; + memcpy(skb2, (const void *) skb, skb->mem_len); + skb2->mem_addr=skb2; + skb2->h.raw = (unsigned char *) + ( + (unsigned long) skb2 + + (unsigned long) skb->h.raw - + (unsigned long) skb + ); + skb2->free = 1; + } + else + { + skb2 = skb; + } + + /* This used to be in the 'else' part, but then + * we don't have this flag set when we get a + * protocol that *does* require copying... -FvK + */ + flag = 1; + + /* Kick the protocol handler. */ + ptype->func(skb2, skb->dev, ptype); + } + } + + /* + * That's odd. We got an unknown packet. Who's using + * stuff like Novell or Amoeba on this network?? + */ + if (!flag) + { + DPRINTF((DBG_DEV, + "INET: unknown packet type 0x%04X (ignored)\n", type)); + skb->sk = NULL; + kfree_skb(skb, FREE_WRITE); + } + + /* Again, see if we can transmit anything now. */ + dev_transmit(); + cli(); + } + in_bh = 0; + sti(); + dev_transmit(); /* Try and kick anything this processing produced */ +} + + +/* + * This routine is called when an device driver (i.e. an + * interface) is * ready to transmit a packet. + */ + +void dev_tint(struct device *dev) +{ + int i; + struct sk_buff *skb; + + for(i = 0;i < DEV_NUMBUFFS; i++) + { + while((skb=skb_dequeue(&dev->buffs[i]))!=NULL) + { + skb->magic = 0; + dev->queue_xmit(skb,dev,-i - 1); + if (dev->tbusy) + return; + } + } +} + + +/* + * Perform a SIOCGIFCONF call. + */ + +static int dev_ifconf(char *arg) +{ + struct ifconf ifc; + struct ifreq ifr; + struct device *dev; + char *pos; + int len; + int err; + + /* Fetch the caller's info block. */ + err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); + if(err) + return -err; + memcpy_fromfs(&ifc, arg, sizeof(struct ifconf)); + len = ifc.ifc_len; + pos = ifc.ifc_buf; + + /* Loop over the interfaces, and write an info block for each. */ + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if(!(dev->flags & IFF_UP)) + continue; + memset(&ifr, 0, sizeof(struct ifreq)); + strcpy(ifr.ifr_name, dev->name); + (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family; + (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; + + /* Write this block to the caller's space. */ + memcpy_tofs(pos, &ifr, sizeof(struct ifreq)); + pos += sizeof(struct ifreq); + len -= sizeof(struct ifreq); + if (len < sizeof(struct ifreq)) break; + } + + /* All done. Write the updated control block back to the caller. */ + ifc.ifc_len = (pos - ifc.ifc_buf); + ifc.ifc_req = (struct ifreq *) ifc.ifc_buf; + memcpy_tofs(arg, &ifc, sizeof(struct ifconf)); + return(pos - arg); +} + +/* + * Print device statistics. + */ + +char *sprintf_stats(char *buffer, struct device *dev) +{ + char *pos = buffer; + struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL); + + if (stats) + pos += sprintf(pos, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n", + dev->name, + stats->rx_packets, stats->rx_errors, + stats->rx_dropped + stats->rx_missed_errors, + stats->rx_fifo_errors, + stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors, + stats->tx_packets, stats->tx_errors, stats->tx_dropped, + stats->tx_fifo_errors, stats->collisions, + stats->tx_carrier_errors + stats->tx_aborted_errors + + stats->tx_window_errors + stats->tx_heartbeat_errors); + else + pos += sprintf(pos, "%6s: No statistics available.\n", dev->name); + + return pos; +} + +/* + * Called from the PROCfs module (/proc/net/dev). + */ + +int dev_get_info(char *buffer) +{ + char *pos = buffer; + struct device *dev; + + pos += + sprintf(pos, + "Inter-| Receive | Transmit\n" + " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n"); + for (dev = dev_base; dev != NULL; dev = dev->next) + { + pos = sprintf_stats(pos, dev); + } + return pos - buffer; +} + +/* + * Perform the SIOCxIFxxx calls. + */ + +static int dev_ifsioc(void *arg, unsigned int getset) +{ + struct ifreq ifr; + struct device *dev; + int ret; + int err; + + /* Fetch the caller's info block. */ + err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); + if(err) + return -err; + memcpy_fromfs(&ifr, arg, sizeof(struct ifreq)); + + /* See which interface the caller is talking about. */ + if ((dev = dev_get(ifr.ifr_name)) == NULL) + return(-EINVAL); + + switch(getset) + { + case SIOCGIFFLAGS: + ifr.ifr_flags = dev->flags; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFFLAGS: + { + int old_flags = dev->flags; + dev->flags = ifr.ifr_flags & ( + IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | + IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | + IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI); + + if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0)) + dev->set_multicast_list(dev,0,NULL); + if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0)) + dev->set_multicast_list(dev,-1,NULL); + if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) + { + ret = dev_close(dev); + } + else + { + ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP)) + ? dev_open(dev) : 0; + if(ret!=0) + dev->flags&=~IFF_UP; /* Failed to come up so go down again */ + } + } + break; + case SIOCGIFADDR: + (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; + (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFADDR: + dev->pa_addr = (*(struct sockaddr_in *) + &ifr.ifr_addr).sin_addr.s_addr; + dev->family = ifr.ifr_addr.sa_family; + dev->pa_mask = ip_get_mask(dev->pa_addr); + dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; + ret = 0; + break; + case SIOCGIFBRDADDR: + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFBRDADDR: + dev->pa_brdaddr = (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_addr.s_addr; + ret = 0; + break; + case SIOCGIFDSTADDR: + (*(struct sockaddr_in *) + &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_broadaddr).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFDSTADDR: + dev->pa_dstaddr = (*(struct sockaddr_in *) + &ifr.ifr_dstaddr).sin_addr.s_addr; + ret = 0; + break; + case SIOCGIFNETMASK: + (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask; + (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_family = dev->family; + (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_port = 0; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFNETMASK: + dev->pa_mask = (*(struct sockaddr_in *) + &ifr.ifr_netmask).sin_addr.s_addr; + ret = 0; + break; + case SIOCGIFMETRIC: + ifr.ifr_metric = dev->metric; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFMETRIC: + dev->metric = ifr.ifr_metric; + ret = 0; + break; + case SIOCGIFMTU: + ifr.ifr_mtu = dev->mtu; + memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); + ret = 0; + break; + case SIOCSIFMTU: + dev->mtu = ifr.ifr_mtu; + ret = 0; + break; + case SIOCGIFMEM: + printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg); + ret = -EINVAL; + break; + case SIOCSIFMEM: + printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg); + ret = -EINVAL; + break; + case SIOCGIFHWADDR: + memcpy(ifr.ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN); + memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); + ret=0; + break; + default: + ret = -EINVAL; + } + return(ret); +} + + +/* + * This function handles all "interface"-type I/O control requests. + */ + +int dev_ioctl(unsigned int cmd, void *arg) +{ + int ret; + + switch(cmd) + { + case IP_SET_DEV: + printk("IP_SET_DEV is obsolete. You need newer network tools.\n"); + ret= -EINVAL; + case SIOCGIFCONF: + (void) dev_ifconf((char *) arg); + ret = 0; + break; + case SIOCGIFFLAGS: + case SIOCSIFFLAGS: + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + case SIOCGIFMTU: + case SIOCSIFMTU: + case SIOCGIFMEM: + case SIOCSIFMEM: + case SIOCGIFHWADDR: + if (!suser()) + return(-EPERM); + ret = dev_ifsioc(arg, cmd); + break; + case SIOCSIFLINK: + if (!suser()) + return(-EPERM); + default: + ret = -EINVAL; + } + + return(ret); +} + +/* + * Setup an ethernet type interface + */ + +void eth_setup(char *str, int *ints) +{ + struct device *d = dev_base; + + if (!str || !*str) + return; + + /* Walk the device list */ + while (d) + { + if (!strcmp(str,d->name)) + { + if (ints[0] > 0) + d->irq=ints[1]; + if (ints[0] > 1) + d->base_addr=ints[2]; + if (ints[0] > 2) + d->mem_start=ints[3]; + if (ints[0] > 3) + d->mem_end=ints[4]; + break; + } + d=d->next; + } +} + + +/* + * Initialize the DEV module. + */ + +void dev_init(void) +{ + struct device *dev, *dev2; + + /* Add the devices. + * If the call to dev->init fails, the dev is removed + * from the chain disconnecting the device until the + * next reboot. + */ + dev2 = NULL; + for (dev = dev_base; dev != NULL; dev=dev->next) + { + if (dev->init && dev->init(dev)) + { + if (dev2 == NULL) + dev_base = dev->next; + else + dev2->next = dev->next; + } + else + { + dev2 = dev; + } + } + +} + diff --git a/net/socket/dev.h b/net/socket/dev.h new file mode 100644 index 0000000..4cb829b --- /dev/null +++ b/net/socket/dev.h @@ -0,0 +1,187 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the Interfaces handler. + * + * Version: @(#)dev.h 1.0.10 08/12/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * Donald J. Becker, <becker@super.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _DEV_H +#define _DEV_H + +#include <linux/if.h> +#include <linux/if_ether.h> + + +/* for future expansion when we will have different priorities. */ +#define DEV_NUMBUFFS 3 +#define MAX_ADDR_LEN 7 +#define MAX_HEADER 18 + +#define IS_MYADDR 1 /* address is (one of) our own */ +#define IS_LOOPBACK 2 /* address is for LOOPBACK */ +#define IS_BROADCAST 3 /* address is a valid broadcast */ +#define IS_INVBCAST 4 /* Wrong netmask bcast not for us */ + +/* + * The DEVICE structure. + * Actually, this whole structure is a big mistake. It mixes I/O + * data with strictly "high-level" data, and it has to know about + * almost every data structure used in the INET module. We will + * gradually phase out this structure, and replace it with the + * more general (but stolen :-) BSD "ifnet" structure. -FvK + */ +struct device { + + /* + * This is the first field of the "visible" part of this structure + * (i.e. as seen by users in the "Space.c" file). It is the name + * the interface. + */ + char *name; + + /* I/O specific fields. These will be moved to DDI soon. */ + unsigned long rmem_end; /* shmem "recv" end */ + unsigned long rmem_start; /* shmem "recv" start */ + unsigned long mem_end; /* sahared mem end */ + unsigned long mem_start; /* shared mem start */ + unsigned short base_addr; /* device I/O address */ + unsigned char irq; /* device IRQ number */ + + /* Low-level status flags. */ + volatile unsigned char start, /* start an operation */ + tbusy, /* transmitter busy */ + interrupt; /* interrupt arrived */ + + /* + * Another mistake. + * This points to the next device in the "dev" chain. It will + * be moved to the "invisible" part of the structure as soon as + * it has been cleaned up. -FvK + */ + struct device *next; + + /* The device initialization function. Called only once. */ + int (*init)(struct device *dev); + + /* Some hardware also needs these fields, but they are not part of the + usual set specified in Space.c. */ + unsigned char if_port; /* Selectable AUI, TP,..*/ + unsigned char dma; /* DMA channel */ + + struct enet_statistics* (*get_stats)(struct device *dev); + + /* + * This marks the end of the "visible" part of the structure. All + * fields hereafter are internal to the system, and may change at + * will (read: may be cleaned up at will). + */ + + /* These may be needed for future network-power-down code. */ + unsigned long trans_start; /* Time (in jiffies) of last Tx */ + unsigned long last_rx; /* Time of last Rx */ + + unsigned short flags; /* interface flags (a la BSD) */ + unsigned short family; /* address family ID (AF_INET) */ + unsigned short metric; /* routing metric (not used) */ + unsigned short mtu; /* interface MTU value */ + unsigned short type; /* interface hardware type */ + unsigned short hard_header_len; /* hardware hdr length */ + void *priv; /* pointer to private data */ + + /* Interface address info. */ + unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ + unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */ + unsigned char addr_len; /* harfware address length */ + unsigned long pa_addr; /* protocol address */ + unsigned long pa_brdaddr; /* protocol broadcast addr */ + unsigned long pa_dstaddr; /* protocol P-P other side addr */ + unsigned long pa_mask; /* protocol netmask */ + unsigned short pa_alen; /* protocol address length */ + + /* Pointer to the interface buffers. */ + struct sk_buff *volatile buffs[DEV_NUMBUFFS]; + + /* Pointers to interface service routines. */ + int (*open)(struct device *dev); + int (*stop)(struct device *dev); + int (*hard_start_xmit) (struct sk_buff *skb, + struct device *dev); + int (*hard_header) (unsigned char *buff, + struct device *dev, + unsigned short type, + unsigned long daddr, + unsigned long saddr, + unsigned len); + void (*add_arp) (unsigned long addr, + struct sk_buff *skb, + struct device *dev); + void (*queue_xmit)(struct sk_buff *skb, + struct device *dev, int pri); + int (*rebuild_header)(void *eth, struct device *dev); + unsigned short (*type_trans) (struct sk_buff *skb, + struct device *dev); +#define HAVE_MULTICAST + void (*set_multicast_list)(struct device *dev, + int num_addrs, void *addrs); +#define HAVE_SET_MAC_ADDR + int (*set_mac_address)(struct device *dev, void *addr); +}; + + +struct packet_type { + unsigned short type; /* This is really NET16(ether_type) other + * devices will have to translate + * appropriately. + */ + unsigned short copy:1; + int (*func) (struct sk_buff *, struct device *, + struct packet_type *); + void *data; + struct packet_type *next; +}; + + +/* Used by dev_rint */ +#define IN_SKBUFF 1 +#define DEV_QUEUE_MAGIC 0x17432895 + + +extern struct device *dev_base; +extern struct packet_type *ptype_base; + + +extern unsigned long ip_get_mask(unsigned long); +extern void dev_add_pack(struct packet_type *pt); +extern void dev_remove_pack(struct packet_type *pt); +extern struct device *dev_get(char *name); +extern int dev_open(struct device *dev); +extern int dev_close(struct device *dev); +extern void dev_queue_xmit(struct sk_buff *skb, struct device *dev, + int pri); +#define HAVE_NETIF_RX 1 +extern void netif_rx(struct sk_buff *skb); +/* The old interface to netif_rx(). */ +extern int dev_rint(unsigned char *buff, long len, int flags, + struct device * dev); +extern void dev_transmit(void); +extern int in_inet_bh(void); +extern void inet_bh(void *tmp); +extern void dev_tint(struct device *dev); +extern int dev_get_info(char *buffer); +extern int dev_ioctl(unsigned int cmd, void *); + +extern void dev_init(void); + +#endif /* _DEV_H */ diff --git a/net/socket/skbuff.c b/net/socket/skbuff.c new file mode 100644 index 0000000..1b9ab93 --- /dev/null +++ b/net/socket/skbuff.c @@ -0,0 +1,461 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * A saner implementation of the skbuff stuff scattered everywhere + * in the old NET2D code. + * + * Authors: Alan Cox <iiitac@pyr.swan.ac.uk> + * + * Fixes: + * Alan Cox : Tracks memory and number of buffers for kernel memory report + * and memory leak hunting. + * Alan Cox : More generic kfree handler + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/segment.h> +#include <asm/system.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include "inet.h" +#include "dev.h" +#include "ip.h" +#include "protocol.h" +#include "arp.h" +#include "route.h" +#include "tcp.h" +#include "udp.h" +#include "skbuff.h" +#include "sock.h" + + +/* Socket buffer operations. Ideally much of this list swap stuff ought to be using + exch instructions on the 386, and CAS/CAS2 on a 68K. This is the boring generic + slow C version. No doubt when Linus sees this comment he'll do horrible things + to this code 8-) +*/ + +/* + * Resource tracking variables + */ + +volatile unsigned long net_memory=0; +volatile unsigned long net_skbcount=0; + +/* + * Debugging paranoia. Can go later when this crud stack works + */ + + + +void skb_check(struct sk_buff *skb, int line, char *file) +{ + if(skb->magic_debug_cookie==SK_FREED_SKB) + { + printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n", + file,line); + printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n", + skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free); + } + if(skb->magic_debug_cookie!=SK_GOOD_SKB) + { + printk("File: %s Line %d, passed a non skb!\n", file,line); + printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p, free=%d\n", + skb,skb->truesize,skb->mem_len,skb->magic,skb->list,skb->free); + } + if(skb->mem_len!=skb->truesize) + { + printk("File: %s Line %d, Dubious size setting!\n",file,line); + printk("skb=%p, real size=%ld, claimed size=%ld, magic=%d, list=%p\n", + skb,skb->truesize,skb->mem_len,skb->magic,skb->list); + } + /* Guess it might be acceptable then */ +} + +/* + * Insert an sk_buff at the start of a list. + */ + +void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk) +{ + unsigned long flags; + + IS_SKB(newsk); + if(newsk->list) + printk("Suspicious queue head: sk_buff on list!\n"); + save_flags(flags); + cli(); + newsk->list=list; + + newsk->next=*list; + + if(*list) + newsk->prev=(*list)->prev; + else + newsk->prev=newsk; + newsk->prev->next=newsk; + newsk->next->prev=newsk; + IS_SKB(newsk->prev); + IS_SKB(newsk->next); + *list=newsk; + restore_flags(flags); +} + +/* + * Insert an sk_buff at the end of a list. + */ + +void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk) +{ + unsigned long flags; + + if(newsk->list) + printk("Suspicious queue tail: sk_buff on list!\n"); + + IS_SKB(newsk); + save_flags(flags); + cli(); + + newsk->list=list; + if(*list) + { + (*list)->prev->next=newsk; + newsk->prev=(*list)->prev; + newsk->next=*list; + (*list)->prev=newsk; + } + else + { + newsk->next=newsk; + newsk->prev=newsk; + *list=newsk; + } + IS_SKB(newsk->prev); + IS_SKB(newsk->next); + restore_flags(flags); + +} + +/* + * Remove an sk_buff from a list. This routine is also interrupt safe + * so you can grab read and free buffers as another process adds them. + */ + +struct sk_buff *skb_dequeue(struct sk_buff *volatile* list) +{ + long flags; + struct sk_buff *result; + + save_flags(flags); + cli(); + + if(*list==NULL) + { + restore_flags(flags); + return(NULL); + } + + result=*list; + if(result->next==result) + *list=NULL; + else + { + result->next->prev=result->prev; + result->prev->next=result->next; + *list=result->next; + } + + IS_SKB(result); + restore_flags(flags); + + if(result->list!=list) + printk("Dequeued packet has invalid list pointer\n"); + + result->list=0; + result->next=0; + result->prev=0; + return(result); +} + +/* + * Insert a packet before another one in a list. + */ + +void skb_insert(struct sk_buff *old, struct sk_buff *newsk) +{ + unsigned long flags; + + IS_SKB(old); + IS_SKB(newsk); + + if(!old->list) + printk("insert before unlisted item!\n"); + if(newsk->list) + printk("inserted item is already on a list.\n"); + + save_flags(flags); + cli(); + newsk->list=old->list; + newsk->next=old; + newsk->prev=old->prev; + newsk->next->prev=newsk; + newsk->prev->next=newsk; + + restore_flags(flags); +} + +/* + * Place a packet after a given packet in a list. + */ + +void skb_append(struct sk_buff *old, struct sk_buff *newsk) +{ + unsigned long flags; + + IS_SKB(old); + IS_SKB(newsk); + + if(!old->list) + printk("append before unlisted item!\n"); + if(newsk->list) + printk("append item is already on a list.\n"); + + save_flags(flags); + cli(); + newsk->list=old->list; + newsk->prev=old; + newsk->next=old->next; + newsk->next->prev=newsk; + newsk->prev->next=newsk; + + restore_flags(flags); +} + +/* + * Remove an sk_buff from its list. Works even without knowing the list it + * is sitting on, which can be handy at times. It also means that THE LIST + * MUST EXIST when you unlink. Thus a list must have its contents unlinked + * _FIRST_. + */ + +void skb_unlink(struct sk_buff *skb) +{ + unsigned long flags; + save_flags(flags); + cli(); + + IS_SKB(skb); + + if(skb->list) + { + skb->next->prev=skb->prev; + skb->prev->next=skb->next; + if(*skb->list==skb) + { + if(skb->next==skb) + *skb->list=NULL; + else + *skb->list=skb->next; + } + skb->next=0; + skb->prev=0; + skb->list=0; + } + restore_flags(flags); +} + +/* + * An skbuff list has had its head reassigned. Move all the list + * pointers. Must be called with ints off during the whole head + * shifting + */ + +void skb_new_list_head(struct sk_buff *volatile* list) +{ + struct sk_buff *skb=skb_peek(list); + if(skb!=NULL) + { + do + { + IS_SKB(skb); + skb->list=list; + skb=skb->next; + } + while(skb!=*list); + } +} + +/* + * Peek an sk_buff. Unlike most other operations you _MUST_ + * be careful with this one. A peek leaves the buffer on the + * list and someone else may run off with it. For an interrupt + * type system cli() peek the buffer copy the data and sti(); + */ + +struct sk_buff *skb_peek(struct sk_buff *volatile* list) +{ + return *list; +} + + +#ifdef UNUSED_NOW + +/* + * Get a clone of an sk_buff. This is the safe way to peek at + * a socket queue without accidents. Its a bit long but most + * of it acutally ends up as tiny bits of inline assembler + * anyway. Only the memcpy of upto 4K with ints off is not + * as nice as I'd like. + */ + +struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) +{ + struct sk_buff *orig,*newsk; + unsigned long flags; + unsigned int len; + /* Now for some games to avoid races */ + + do + { + save_flags(flags); + cli(); + orig=skb_peek(list); + if(orig==NULL) + { + restore_flags(flags); + return NULL; + } + IS_SKB(orig); + len=orig->truesize; + restore_flags(flags); + + newsk=alloc_skb(len,GFP_KERNEL); /* May sleep */ + + if(newsk==NULL) /* Oh dear... not to worry */ + return NULL; + + save_flags(flags); + cli(); + if(skb_peek(list)!=orig) /* List changed go around another time */ + { + restore_flags(flags); + newsk->sk=NULL; + newsk->free=1; + newsk->mem_addr=newsk; + newsk->mem_len=len; + kfree_skb(newsk, FREE_WRITE); + continue; + } + + IS_SKB(orig); + IS_SKB(newsk); + memcpy(newsk,orig,len); + newsk->list=NULL; + newsk->magic=0; + newsk->next=NULL; + newsk->prev=NULL; + newsk->mem_addr=newsk; + newsk->h.raw+=((char *)newsk-(char *)orig); + newsk->link3=NULL; + newsk->sk=NULL; + newsk->free=1; + } + while(0); + + restore_flags(flags); + return(newsk); +} + +#endif + +/* + * Free an sk_buff. This still knows about things it should + * not need to like protocols and sockets. + */ + +void kfree_skb(struct sk_buff *skb, int rw) +{ + if (skb == NULL) { + printk("kfree_skb: skb = NULL\n"); + return; + } + IS_SKB(skb); + if(skb->free == 2) + printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n"); + if(skb->list) + printk("Warning: kfree_skb passed an skb still on a list.\n"); + skb->magic = 0; + if (skb->sk) + { + if(skb->sk->prot!=NULL) + { + if (rw) + skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len); + else + skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len); + + } + else + { + /* Non INET - default wmalloc/rmalloc handler */ + if (rw) + skb->sk->rmem_alloc-=skb->mem_len; + else + skb->sk->wmem_alloc-=skb->mem_len; + if(!skb->sk->dead) + skb->sk->write_space(skb->sk); + kfree_skbmem(skb->mem_addr,skb->mem_len); + } + } + else + { + kfree_skbmem(skb->mem_addr, skb->mem_len); + } +} + +/* + * Allocate a new skbuff. We do this ourselves so we can fill in a few 'private' + * fields and also do memory statistics to find all the [BEEP] leaks. + */ + + struct sk_buff *alloc_skb(unsigned int size,int priority) + { + struct sk_buff *skb=(struct sk_buff *)kmalloc(size,priority); + if(skb==NULL) + return NULL; + skb->free= 2; /* Invalid so we pick up forgetful users */ + skb->list= 0; /* Not on a list */ + skb->truesize=size; + skb->mem_len=size; + skb->mem_addr=skb; + skb->fraglist=NULL; + net_memory+=size; + net_skbcount++; + skb->magic_debug_cookie=SK_GOOD_SKB; + skb->users=0; + return skb; + } + +/* + * Free an skbuff by memory + */ + +void kfree_skbmem(void *mem,unsigned size) +{ + struct sk_buff *x=mem; + IS_SKB(x); + if(x->magic_debug_cookie==SK_GOOD_SKB) + { + x->magic_debug_cookie=SK_FREED_SKB; + kfree_s(mem,size); + net_skbcount--; + net_memory-=size; + } +} + diff --git a/net/socket/skbuff.h b/net/socket/skbuff.h new file mode 100644 index 0000000..ef04197 --- /dev/null +++ b/net/socket/skbuff.h @@ -0,0 +1,111 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the 'struct sk_buff' memory handlers. + * + * Version: @(#)skbuff.h 1.0.4 05/20/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * + * Fixes: + * Alan Cox : Volatiles (this makes me unhappy - we want proper asm linked list stuff) + * Alan Cox : Declaration for new primitives + * Alan Cox : Fraglist support (idea by Donald Becker) + * Alan Cox : 'users' counter. Combines with datagram changes to avoid skb_peek_copy + * being used. + * Alan Cox : Extra fields for RAW fixes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _SKBUFF_H +#define _SKBUFF_H +#include <linux/malloc.h> + +#ifdef CONFIG_IPX +#include "ipx/ipx.h" +#endif + +#define HAVE_ALLOC_SKB /* For the drivers to know */ + + +#define FREE_READ 1 +#define FREE_WRITE 0 + + +struct sk_buff +{ + unsigned long magic_debug_cookie; + struct sk_buff *volatile next; + struct sk_buff *volatile prev; + struct sk_buff *volatile link3; + struct sk_buff *volatile* list; + struct sock *sk; + volatile unsigned long when; /* used to compute rtt's */ + struct device *dev; + void *mem_addr; + union + { + struct tcphdr *th; + struct ethhdr *eth; + struct iphdr *iph; + struct udphdr *uh; + struct arphdr *arp; + unsigned char *raw; + unsigned long seq; +#ifdef CONFIG_IPX + ipx_packet *ipx; +#endif + } h; + struct iphdr * ip_hdr; + unsigned long mem_len; + unsigned long len; + unsigned long fraglen; + struct sk_buff *fraglist; /* Fragment list */ + unsigned long truesize; + unsigned long saddr; + unsigned long daddr; + int magic; + volatile char acked, + used, + free, + arp, + urg_used; + unsigned char tries,lock; /* Lock is now unused */ + unsigned short users; /* User count - see datagram.c (and soon seqpacket.c/stream.c) */ +}; + +#define SK_WMEM_MAX 8192 +#define SK_RMEM_MAX 32767 + +#define SK_FREED_SKB 0x0DE2C0DE +#define SK_GOOD_SKB 0xDEC0DED1 + +extern void print_skb(struct sk_buff *); +extern void kfree_skb(struct sk_buff *skb, int rw); +extern void skb_queue_head(struct sk_buff * volatile *list,struct sk_buff *buf); +extern void skb_queue_tail(struct sk_buff * volatile *list,struct sk_buff *buf); +extern struct sk_buff * skb_dequeue(struct sk_buff * volatile *list); +extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk); +extern void skb_append(struct sk_buff *old,struct sk_buff *newsk); +extern void skb_unlink(struct sk_buff *buf); +extern void skb_new_list_head(struct sk_buff *volatile* list); +extern struct sk_buff * skb_peek(struct sk_buff * volatile *list); +extern struct sk_buff * skb_peek_copy(struct sk_buff * volatile *list); +extern struct sk_buff * alloc_skb(unsigned int size, int priority); +extern void kfree_skbmem(void *mem, unsigned size); + +extern void skb_check(struct sk_buff *skb,int, char *); +#define IS_SKB(skb) skb_check((skb),__LINE__,__FILE__) + +extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err); +extern int datagram_select(struct sock *sk, int sel_type, select_table *wait); +extern void skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size); +extern void skb_free_datagram(struct sk_buff *skb); +#endif /* _SKBUFF_H */ diff --git a/net/socket/sock.c b/net/socket/sock.c new file mode 100644 index 0000000..1316be3 --- /dev/null +++ b/net/socket/sock.c @@ -0,0 +1,418 @@ +/* + * NET2Debugged, generic socket properties. + * + * This module provides the generic option control and memory handling + * for a socket of any kind. + * + * Version: @(#)sock.c 1.28 26/12/93 + * + * Authors: Alan Cox <iiitac@pyr.swan.ac.uk> + * + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/interrupt.h> + +#include <asm/segment.h> +#include <asm/system.h> + +#include "inet.h" +#include "dev.h" +#include "ip.h" +#include "protocol.h" +#include "arp.h" +#include "route.h" +#include "tcp.h" +#include "udp.h" +#include "skbuff.h" +#include "sock.h" +#include "raw.h" +#include "icmp.h" + +static __inline__ int +min(unsigned int a, unsigned int b) +{ + if (a < b) + return(a); + return(b); +} + +#ifdef SOCK_DEBUG + +void print_sk(struct sock *sk) +{ + if (!sk) + { + printk(" print_sk(NULL)\n"); + return; + } + printk(" wmem_alloc = %lu\n", sk->wmem_alloc); + printk(" rmem_alloc = %lu\n", sk->rmem_alloc); + printk(" send_head = %p\n", sk->send_head); + printk(" state = %d\n",sk->state); + printk(" wback = %p, rqueue = %p\n", sk->wback, sk->rqueue); + printk(" wfront = %p\n", sk->wfront); + printk(" daddr = %lX, saddr = %lX\n", sk->daddr,sk->saddr); + printk(" num = %d", sk->num); + printk(" next = %p\n", sk->next); + printk(" send_seq = %ld, acked_seq = %ld, copied_seq = %ld\n", + sk->send_seq, sk->acked_seq, sk->copied_seq); + printk(" rcv_ack_seq = %ld, window_seq = %ld, fin_seq = %ld\n", + sk->rcv_ack_seq, sk->window_seq, sk->fin_seq); + printk(" prot = %p\n", sk->prot); + printk(" pair = %p, back_log = %p\n", sk->pair,sk->back_log); + printk(" inuse = %d , blog = %d\n", sk->inuse, sk->blog); + printk(" dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks); + printk(" retransmits = %ld, timeout = %d\n", sk->retransmits, sk->timeout); + printk(" cong_window = %d, packets_out = %d\n", sk->cong_window, + sk->packets_out); + printk(" urg = %d shutdown=%d\n", sk->urg, sk->shutdown); +} + + +void print_skb(struct sk_buff *skb) +{ + if (!skb) + { + printk(" print_skb(NULL)\n"); + return; + } + printk(" prev = %p, next = %p\n", skb->prev, skb->next); + printk(" sk = %p link3 = %p\n", skb->sk, skb->link3); + printk(" mem_addr = %p, mem_len = %lu\n", skb->mem_addr, skb->mem_len); + printk(" used = %d free = %d\n", skb->used,skb->free); +} + + +#endif + +/* + * This is meant for all protocols to use and covers goings on + * at the socket level. Everything here is generic. + */ + +int sock_setsockopt(struct sock *sk, int level, int optname, + char *optval, int optlen) +{ + int val; + int err; + struct linger ling; + + if (optval == NULL) + return(-EINVAL); + + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + switch(optname) + { + case SO_TYPE: + case SO_ERROR: + return(-ENOPROTOOPT); + + case SO_DEBUG: + sk->debug=val?1:0; + case SO_DONTROUTE: /* Still to be implemented */ + return(0); + case SO_BROADCAST: + sk->broadcast=val?1:0; + return 0; + case SO_SNDBUF: + if(val>32767) + val=32767; + if(val<256) + val=256; + sk->sndbuf=val; + return 0; + case SO_LINGER: + err=verify_area(VERIFY_READ,optval,sizeof(ling)); + if(err) + return err; + memcpy_fromfs(&ling,optval,sizeof(ling)); + if(ling.l_onoff==0) + sk->linger=0; + else + { + sk->lingertime=ling.l_linger; + sk->linger=1; + } + return 0; + case SO_RCVBUF: + if(val>32767) + val=32767; + if(val<256) + val=256; + sk->rcvbuf=val; + return(0); + + case SO_REUSEADDR: + if (val) + sk->reuse = 1; + else + sk->reuse = 0; + return(0); + + case SO_KEEPALIVE: + if (val) + sk->keepopen = 1; + else + sk->keepopen = 0; + return(0); + + case SO_OOBINLINE: + if (val) + sk->urginline = 1; + else + sk->urginline = 0; + return(0); + + case SO_NO_CHECK: + if (val) + sk->no_check = 1; + else + sk->no_check = 0; + return(0); + + case SO_PRIORITY: + if (val >= 0 && val < DEV_NUMBUFFS) + { + sk->priority = val; + } + else + { + return(-EINVAL); + } + return(0); + + default: + return(-ENOPROTOOPT); + } +} + + +int sock_getsockopt(struct sock *sk, int level, int optname, + char *optval, int *optlen) +{ + int val; + int err; + struct linger ling; + + switch(optname) + { + case SO_DEBUG: + val = sk->debug; + break; + + case SO_DONTROUTE: /* One last option to implement */ + val = 0; + break; + + case SO_BROADCAST: + val= sk->broadcast; + break; + + case SO_LINGER: + err=verify_area(VERIFY_WRITE,optval,sizeof(ling)); + if(err) + return err; + err=verify_area(VERIFY_WRITE,optlen,sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(ling),(unsigned long *)optlen); + ling.l_onoff=sk->linger; + ling.l_linger=sk->lingertime; + memcpy_tofs(optval,&ling,sizeof(ling)); + return 0; + + case SO_SNDBUF: + val=sk->sndbuf; + break; + + case SO_RCVBUF: + val =sk->rcvbuf; + break; + + case SO_REUSEADDR: + val = sk->reuse; + break; + + case SO_KEEPALIVE: + val = sk->keepopen; + break; + + case SO_TYPE: + if (sk->prot == &tcp_prot) + val = SOCK_STREAM; + else + val = SOCK_DGRAM; + break; + + case SO_ERROR: + val = sk->err; + sk->err = 0; + break; + + case SO_OOBINLINE: + val = sk->urginline; + break; + + case SO_NO_CHECK: + val = sk->no_check; + break; + + case SO_PRIORITY: + val = sk->priority; + break; + + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); + + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); + + return(0); +} + + +void *sock_wmalloc(struct sock *sk, unsigned long size, int force, + int priority) +{ + if (sk) + { + if (sk->wmem_alloc + size < sk->sndbuf || force) + { + cli(); + sk->wmem_alloc+= size; + sti(); + return(alloc_skb(size, priority)); + } + DPRINTF((DBG_INET, "sock_wmalloc(%X,%d,%d,%d) returning NULL\n", + sk, size, force, priority)); + return(NULL); + } + return(alloc_skb(size, priority)); +} + + +void *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority) +{ + if (sk) + { + if (sk->rmem_alloc + size < sk->rcvbuf || force) + { + void *c = alloc_skb(size, priority); + cli(); + if (c) + sk->rmem_alloc += size; + sti(); + return(c); + } + DPRINTF((DBG_INET, "sock_rmalloc(%X,%d,%d,%d) returning NULL\n", + sk,size,force, priority)); + return(NULL); + } + return(alloc_skb(size, priority)); +} + + +unsigned long sock_rspace(struct sock *sk) +{ + int amt; + + if (sk != NULL) + { + if (sk->rmem_alloc >= sk->rcvbuf-2*MIN_WINDOW) + return(0); + amt = min((sk->rcvbuf-sk->rmem_alloc)/2-MIN_WINDOW, MAX_WINDOW); + if (amt < 0) + return(0); + return(amt); + } + return(0); +} + + +unsigned long sock_wspace(struct sock *sk) +{ + if (sk != NULL) + { + if (sk->shutdown & SEND_SHUTDOWN) + return(0); + if (sk->wmem_alloc >= sk->sndbuf) + return(0); + return(sk->sndbuf-sk->wmem_alloc ); + } + return(0); +} + + +void sock_wfree(struct sock *sk, void *mem, unsigned long size) +{ + struct sk_buff *skb; + DPRINTF((DBG_INET, "sock_wfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size)); + + IS_SKB(mem); + + skb=mem; + + kfree_skbmem(mem, size); + if (sk) + { + sk->wmem_alloc -= size; + + /* In case it might be waiting for more memory. */ + if (!sk->dead) + sk->write_space(sk); + if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) + { + DPRINTF((DBG_INET, + "recovered lost memory, sock = %X\n", sk)); + } + return; + } +} + + +void sock_rfree(struct sock *sk, void *mem, unsigned long size) +{ + struct sk_buff *skb; + + DPRINTF((DBG_INET, "sock_rfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size)); + + IS_SKB(mem); + skb=mem; + + kfree_skbmem(mem, size); + + if (sk) + { + sk->rmem_alloc -= size; + if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) + { + DPRINTF((DBG_INET,"recovered lot memory, sock = %X\n", sk)); + } + } +} + diff --git a/net/socket/sock.h b/net/socket/sock.h new file mode 100644 index 0000000..6f0b1d8 --- /dev/null +++ b/net/socket/sock.h @@ -0,0 +1,191 @@ +/* + * Definitions for the socket handler + * + * Version: @(#)sock.h 1.28 26/12/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * Florian La Roche <flla@stud.uni-sb.de> + * + * Fixes: + * Alan Cox : Volatiles in skbuff pointers. See + * skbuff comments. May be overdone, + * better to prove they can be removed + * than the reverse. + * Alan Cox : Added a zapped field for tcp to note + * a socket is reset and must stay shut up + * Alan Cox : New fields for options + * Pauline Middelink : identd support + * Alan Cox : Split into sock.h and sockinet.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _SOCK_H +#define _SOCK_H + +#include <linux/timer.h> +#include <linux/ip.h> /* struct options */ +#include <linux/tcp.h> /* struct tcphdr */ + +#include "skbuff.h" /* struct sk_buff */ +#ifdef CONFIG_AX25 +#include "ax25/ax25.h" +#endif +#ifdef CONFIG_IPX +#include "ipx/ipx.h" +#endif + +#define SOCK_ARRAY_SIZE 64 + + +/* + * This structure really needs to be cleaned up. + * Most of it is for TCP, and not used by any of + * the other protocols. + */ +struct sock { + struct options *opt; + struct options *rcv_opt; + volatile unsigned long wmem_alloc; + volatile unsigned long rmem_alloc; + unsigned long send_seq; + unsigned long acked_seq; + unsigned long copied_seq; + unsigned long rcv_ack_seq; + unsigned long window_seq; + unsigned long fin_seq; + + /* + * Not all are volatile, but some are, so we + * might as well say they all are. + */ + volatile char inuse, + dead, + urginline, + intr, + blog, + done, + reuse, + keepopen, + linger, + delay_acks, + destroy, + ack_timed, + no_check, + exp_growth, + zapped, /* In ax25 & ipx means not linked */ + broadcast; + unsigned long lingertime; + int proc; + struct sock *next; + struct sock *pair; + struct sk_buff *volatile send_tail; + struct sk_buff *volatile send_head; + struct sk_buff *volatile back_log; + struct sk_buff *send_tmp; + long retransmits; + struct sk_buff *volatile wback, + *volatile wfront, + *volatile rqueue; + struct proto *prot; + struct wait_queue **sleep; + unsigned long daddr; + unsigned long saddr; + unsigned short max_unacked; + unsigned short window; + unsigned short bytes_rcv; + unsigned short mtu; + unsigned short num; + volatile unsigned short cong_window; + volatile unsigned short packets_out; + volatile unsigned short urg; + volatile unsigned short shutdown; + unsigned short mss; + volatile unsigned long rtt; + volatile unsigned long mdev; + volatile unsigned short backoff; + volatile short err; + unsigned char protocol; + volatile unsigned char state; + volatile unsigned char ack_backlog; + unsigned char max_ack_backlog; + unsigned char priority; + unsigned char debug; + unsigned short rcvbuf; + unsigned short sndbuf; + unsigned short type; +#ifdef CONFIG_IPX + ipx_address ipx_source_addr,ipx_dest_addr; + unsigned short ipx_type; +#endif +#ifdef CONFIG_AX25 +/* Really we want to add a per protocol private area */ + ax25_address ax25_source_addr,ax25_dest_addr; + struct sk_buff *volatile ax25_retxq[8]; + char ax25_state,ax25_vs,ax25_vr,ax25_lastrxnr,ax25_lasttxnr; + char ax25_condition; + char ax25_retxcnt; + char ax25_xx; + char ax25_retxqi; + char ax25_rrtimer; + char ax25_timer; +#endif +/* IP 'private area' or will be eventually */ + int ip_ttl; /* TTL setting */ + int ip_tos; /* TOS */ + + struct tcphdr dummy_th; + + /* This part is used for the timeout functions (timer.c). */ + int timeout; /* What are we waiting for? */ + struct timer_list timer; + + /* identd */ + struct socket *socket; + + /* Event callbacks */ + + void (*state_change)(struct sock *sk); + void (*data_ready)(struct sock *sk,int bytes); + void (*write_space)(struct sock *sk); + void (*error_report)(struct sock *sk); +}; + + +#define TIME_WRITE 1 +#define TIME_CLOSE 2 +#define TIME_KEEPOPEN 3 +#define TIME_DESTROY 4 +#define TIME_DONE 5 /* used to absorb those last few packets */ +#define SOCK_DESTROY_TIME 1000 /* about 10 seconds */ + +#define PROT_SOCK 1024 /* Sockets 0-1023 can't be bound too unless you are superuser */ + +#define SHUTDOWN_MASK 3 +#define RCV_SHUTDOWN 1 +#define SEND_SHUTDOWN 2 + +extern void print_sk(struct sock *); +extern void *sock_wmalloc(struct sock *sk, + unsigned long size, int force, + int priority); +extern void *sock_rmalloc(struct sock *sk, + unsigned long size, int force, + int priority); +extern void sock_wfree(struct sock *sk, void *mem, + unsigned long size); +extern void sock_rfree(struct sock *sk, void *mem, + unsigned long size); +extern unsigned long sock_rspace(struct sock *sk); +extern unsigned long sock_wspace(struct sock *sk); + +extern int sock_setsockopt(struct sock *sk, int level, int optname, char *optval, + int optlen); +extern int sock_getsockopt(struct sock *sk, int level, int optname, char *optval, + int *optlen); + +#endif /* _SOCK_H */ diff --git a/net/unix/proc.c b/net/unix/proc.c index 8a80ad5..4e23976 100644 --- a/net/unix/proc.c +++ b/net/unix/proc.c @@ -8,7 +8,7 @@ * the PROC file system and the "unix" family of networking * protocols. It is mainly used for debugging and statistics. * - * Version: @(#)proc.c 1.0.4 05/23/93 + * Version: @(#)proc.c 1.28 25/12/93 * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -16,12 +16,14 @@ * Fred Baumgarten, <dc6iq@insu1.etec.uni-kalrsruhe.de> * * Fixes: + * Anonymous : Comment errors * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + #include <linux/autoconf.h> #include <linux/sched.h> #include <linux/string.h> @@ -33,45 +35,55 @@ #include "unix.h" -/* Called from PROCfs. */ +/* + * Called from PROCfs. + */ + int unix_get_info(char *buffer) { - char *pos; - int i; + char *pos; + int i; - pos = buffer; - pos += sprintf(pos, "Num RefCount Protocol Flags Type St Path\n"); + pos = buffer; + pos += sprintf(pos, "Num RefCount Protocol Flags Type St Path\n"); - for(i = 0; i < NSOCKETS; i++) { - if (unix_datas[i].refcnt) { - pos += sprintf(pos, "%2d: %08X %08X %08lX %04X %02X", i, - unix_datas[i].refcnt, - unix_datas[i].protocol, - unix_datas[i].socket->flags, - unix_datas[i].socket->type, - unix_datas[i].socket->state - ); - - /* If socket is bound to a filename, we'll print it. */ - if(unix_datas[i].sockaddr_len>0) { - pos += sprintf(pos, " %s\n", - unix_datas[i].sockaddr_un.sun_path); - } else { /* just add a newline */ - *pos='\n'; - pos++; - *pos='\0'; - } + for(i = 0; i < NSOCKETS; i++) + { + if (unix_datas[i].refcnt) + { + pos += sprintf(pos, "%2d: %08X %08X %08lX %04X %02X", i, + unix_datas[i].refcnt, + unix_datas[i].protocol, + unix_datas[i].socket->flags, + unix_datas[i].socket->type, + unix_datas[i].socket->state + ); + + /* If socket is bound to a filename, we'll print it. */ + if(unix_datas[i].sockaddr_len>0) + { + pos += sprintf(pos, " %s\n", + unix_datas[i].sockaddr_un.sun_path); + } + else + { /* just add a newline */ + *pos='\n'; + pos++; + *pos='\0'; + } - /* - * Check whether buffer _may_ overflow in the next loop. - * Since sockets may have very very long paths, we make - * PATH_MAX+80 the minimum space left for a new line. - */ - if (pos > buffer+PAGE_SIZE-80-PATH_MAX) { - printk("UNIX: netinfo: oops, too many sockets.\n"); - return(pos - buffer); + /* + * Check whether buffer _may_ overflow in the next loop. + * Since sockets may have very very long paths, we make + * PATH_MAX+80 the minimum space left for a new line. + */ + + if (pos > buffer+PAGE_SIZE-80-PATH_MAX) + { + printk("UNIX: netinfo: oops, too many sockets.\n"); + return(pos - buffer); + } } - } - } - return(pos - buffer); + } + return(pos - buffer); } diff --git a/net/unix/sock.c b/net/unix/sock.c index f4de8a8..7e68b24 100644 --- a/net/unix/sock.c +++ b/net/unix/sock.c @@ -4,7 +4,7 @@ * BSD Socket interface as the means of communication with * the user level. * - * Version: @(#)sock.c 1.0.5 05/25/93 + * Version: @(#)sock.c 1.28 25/12/93 * * Authors: Orest Zborowski, <obz@Kodak.COM> * Ross Biro, <bir7@leland.Stanford.Edu> @@ -12,6 +12,7 @@ * * Fixes: * Alan Cox : Verify Area + * Alan Cox : Tidy up ready for release * * BUGS * Page faults on read while another process reads could lose data. @@ -93,252 +94,268 @@ static int unix_proto_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen); -static void -dprintf(int level, char *fmt, ...) +static void dprintf(int level, char *fmt, ...) { - va_list args; - char *buff; - extern int vsprintf(char * buf, const char * fmt, va_list args); - - if (level != unix_debug) return; - - buff = (char *) kmalloc(256, GFP_KERNEL); - if (buff != NULL) { - va_start(args, fmt); - vsprintf(buff, fmt, args); - va_end(args); - printk(buff); - kfree(buff); - } + va_list args; + char *buff; + extern int vsprintf(char * buf, const char * fmt, va_list args); + + if (level != unix_debug) + return; + + buff = (char *) kmalloc(256, GFP_KERNEL); + if (buff != NULL) + { + va_start(args, fmt); + vsprintf(buff, fmt, args); + va_end(args); + printk(buff); + kfree(buff); + } } -static inline int -min(int a, int b) +static inline int min(int a, int b) { - if (a < b) return(a); - return(b); + if (a < b) + return(a); + return(b); } -void -sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len) +void sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len) { - char buf[sizeof(sockun->sun_path) + 1]; - - if (unix_debug == 0) return; - - sockaddr_len -= UN_PATH_OFFSET; - if (sockun->sun_family != AF_UNIX) - printk("UNIX: Badd addr family %d>\n", sockun->sun_family); - else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf)) - printk("UNIX: Bad addr len %d>\n", sockaddr_len); - else { - memcpy(buf, sockun->sun_path, sockaddr_len); - buf[sockaddr_len] = '\0'; - printk("\"%s\"[%lu]\n", buf, sockaddr_len + UN_PATH_OFFSET); - } + char buf[sizeof(sockun->sun_path) + 1]; + + if (unix_debug == 0) + return; + + sockaddr_len -= UN_PATH_OFFSET; + if (sockun->sun_family != AF_UNIX) + printk("UNIX: Badd addr family %d>\n", sockun->sun_family); + else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf)) + printk("UNIX: Bad addr len %d>\n", sockaddr_len); + else + { + memcpy(buf, sockun->sun_path, sockaddr_len); + buf[sockaddr_len] = '\0'; + printk("\"%s\"[%lu]\n", buf, sockaddr_len + UN_PATH_OFFSET); + } } -/* don't have to do anything. */ -static int -unix_proto_listen(struct socket *sock, int backlog) +/* + * Don't have to do anything. + */ + +static int unix_proto_listen(struct socket *sock, int backlog) { - return(0); + return(0); } -static int -unix_proto_setsockopt(struct socket *sock, int level, int optname, +static int unix_proto_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { - return(-EOPNOTSUPP); + return(-EOPNOTSUPP); } -static int -unix_proto_getsockopt(struct socket *sock, int level, int optname, +static int unix_proto_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { - return(-EOPNOTSUPP); + return(-EOPNOTSUPP); } -static int -unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock, +static int unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock, unsigned flags, struct sockaddr *addr, int addr_len) { - return(-EOPNOTSUPP); + return(-EOPNOTSUPP); } -static int -unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock, +static int unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock, unsigned flags, struct sockaddr *addr, int *addr_len) { - return(-EOPNOTSUPP); + return(-EOPNOTSUPP); } -static int -unix_proto_shutdown(struct socket *sock, int how) +static int unix_proto_shutdown(struct socket *sock, int how) { - return(-EOPNOTSUPP); + return(-EOPNOTSUPP); } -/* This error needs to be checked. */ -static int -unix_proto_send(struct socket *sock, void *buff, int len, int nonblock, +/* + * This error needs to be checked. + */ + +static int unix_proto_send(struct socket *sock, void *buff, int len, int nonblock, unsigned flags) { - if (flags != 0) return(-EINVAL); - return(unix_proto_write(sock, (char *) buff, len, nonblock)); + if (flags != 0) + return(-EINVAL); + return(unix_proto_write(sock, (char *) buff, len, nonblock)); } -/* This error needs to be checked. */ -static int -unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock, +/* + * This error needs to be checked. + */ + +static int unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock, unsigned flags) { - if (flags != 0) return(-EINVAL); - return(unix_proto_read(sock, (char *) buff, len, nonblock)); + if (flags != 0) + return(-EINVAL); + return(unix_proto_read(sock, (char *) buff, len, nonblock)); } -static struct unix_proto_data * -unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len, +static struct unix_proto_data * unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len, struct inode *inode) { - struct unix_proto_data *upd; - - for(upd = unix_datas; upd <= last_unix_data; ++upd) { - if (upd->refcnt && upd->socket && - upd->socket->state == SS_UNCONNECTED && - upd->sockaddr_un.sun_family == sockun->sun_family && - upd->inode == inode) return(upd); - } - return(NULL); + struct unix_proto_data *upd; + + for(upd = unix_datas; upd <= last_unix_data; ++upd) + { + if (upd->refcnt && upd->socket && + upd->socket->state == SS_UNCONNECTED && + upd->sockaddr_un.sun_family == sockun->sun_family && + upd->inode == inode) + { + return(upd); + } + } + return(NULL); } -static struct unix_proto_data * -unix_data_alloc(void) +static struct unix_proto_data *unix_data_alloc(void) { - struct unix_proto_data *upd; - - cli(); - for(upd = unix_datas; upd <= last_unix_data; ++upd) { - if (!upd->refcnt) { - upd->refcnt = 1; - sti(); - upd->socket = NULL; - upd->sockaddr_len = 0; - upd->sockaddr_un.sun_family = 0; - upd->buf = NULL; - upd->bp_head = upd->bp_tail = 0; - upd->inode = NULL; - upd->peerupd = NULL; - return(upd); - } - } - sti(); - return(NULL); + struct unix_proto_data *upd; + + cli(); + for(upd = unix_datas; upd <= last_unix_data; ++upd) + { + if (!upd->refcnt) + { + upd->refcnt = 1; + sti(); + upd->socket = NULL; + upd->sockaddr_len = 0; + upd->sockaddr_un.sun_family = 0; + upd->buf = NULL; + upd->bp_head = upd->bp_tail = 0; + upd->inode = NULL; + upd->peerupd = NULL; + return(upd); + } + } + sti(); + return(NULL); } -static inline void -unix_data_ref(struct unix_proto_data *upd) +static inline void unix_data_ref(struct unix_proto_data *upd) { - if (!upd) { - dprintf(1, "UNIX: data_ref: upd = NULL\n"); - return; - } - ++upd->refcnt; - dprintf(1, "UNIX: data_ref: refing data 0x%x(%d)\n", upd, upd->refcnt); + if (!upd) + { + dprintf(1, "UNIX: data_ref: upd = NULL\n"); + return; + } + ++upd->refcnt; + dprintf(1, "UNIX: data_ref: refing data 0x%x(%d)\n", upd, upd->refcnt); } -static void -unix_data_deref(struct unix_proto_data *upd) +static void unix_data_deref(struct unix_proto_data *upd) { - if (!upd) { - dprintf(1, "UNIX: data_deref: upd = NULL\n"); - return; - } - if (upd->refcnt == 1) { - dprintf(1, "UNIX: data_deref: releasing data 0x%x\n", upd); - if (upd->buf) { - free_page((unsigned long)upd->buf); - upd->buf = NULL; - upd->bp_head = upd->bp_tail = 0; - } - } - --upd->refcnt; + if (!upd) + { + dprintf(1, "UNIX: data_deref: upd = NULL\n"); + return; + } + if (upd->refcnt == 1) + { + dprintf(1, "UNIX: data_deref: releasing data 0x%x\n", upd); + if (upd->buf) + { + free_page((unsigned long)upd->buf); + upd->buf = NULL; + upd->bp_head = upd->bp_tail = 0; + } + } + --upd->refcnt; } /* - * Upon a create, we allocate an empty protocol data, - * and grab a page to buffer writes. + * Upon a create, we allocate an empty protocol data, + * and grab a page to buffer writes. */ -static int -unix_proto_create(struct socket *sock, int protocol) -{ - struct unix_proto_data *upd; - - dprintf(1, "UNIX: create: socket 0x%x, proto %d\n", sock, protocol); - if (protocol != 0) { - dprintf(1, "UNIX: create: protocol != 0\n"); - return(-EINVAL); - } - if (!(upd = unix_data_alloc())) { - printk("UNIX: create: can't allocate buffer\n"); - return(-ENOMEM); - } - if (!(upd->buf = (char*) get_free_page(GFP_USER))) { - printk("UNIX: create: can't get page!\n"); - unix_data_deref(upd); - return(-ENOMEM); - } - upd->protocol = protocol; - upd->socket = sock; - UN_DATA(sock) = upd; - dprintf(1, "UNIX: create: allocated data 0x%x\n", upd); - return(0); + +static int unix_proto_create(struct socket *sock, int protocol) +{ + struct unix_proto_data *upd; + + dprintf(1, "UNIX: create: socket 0x%x, proto %d\n", sock, protocol); + if (protocol != 0) + { + dprintf(1, "UNIX: create: protocol != 0\n"); + return(-EINVAL); + } + if (!(upd = unix_data_alloc())) + { + printk("UNIX: create: can't allocate buffer\n"); + return(-ENOMEM); + } + if (!(upd->buf = (char*) get_free_page(GFP_USER))) + { + printk("UNIX: create: can't get page!\n"); + unix_data_deref(upd); + return(-ENOMEM); + } + upd->protocol = protocol; + upd->socket = sock; + UN_DATA(sock) = upd; + dprintf(1, "UNIX: create: allocated data 0x%x\n", upd); + return(0); } -static int -unix_proto_dup(struct socket *newsock, struct socket *oldsock) +static int unix_proto_dup(struct socket *newsock, struct socket *oldsock) { - struct unix_proto_data *upd = UN_DATA(oldsock); + struct unix_proto_data *upd = UN_DATA(oldsock); - return(unix_proto_create(newsock, upd->protocol)); + return(unix_proto_create(newsock, upd->protocol)); } -static int -unix_proto_release(struct socket *sock, struct socket *peer) -{ - struct unix_proto_data *upd = UN_DATA(sock); - - dprintf(1, "UNIX: release: socket 0x%x, unix_data 0x%x\n", sock, upd); - if (!upd) return(0); - if (upd->socket != sock) { - printk("UNIX: release: socket link mismatch!\n"); - return(-EINVAL); - } - if (upd->inode) { - dprintf(1, "UNIX: release: releasing inode 0x%x\n", upd->inode); - iput(upd->inode); - upd->inode = NULL; - } - UN_DATA(sock) = NULL; - upd->socket = NULL; - if (upd->peerupd) unix_data_deref(upd->peerupd); - unix_data_deref(upd); - return(0); +static int unix_proto_release(struct socket *sock, struct socket *peer) +{ + struct unix_proto_data *upd = UN_DATA(sock); + + dprintf(1, "UNIX: release: socket 0x%x, unix_data 0x%x\n", sock, upd); + if (!upd) + return(0); + if (upd->socket != sock) + { + printk("UNIX: release: socket link mismatch!\n"); + return(-EINVAL); + } + if (upd->inode) + { + dprintf(1, "UNIX: release: releasing inode 0x%x\n", upd->inode); + iput(upd->inode); + upd->inode = NULL; + } + UN_DATA(sock) = NULL; + upd->socket = NULL; + if (upd->peerupd) + unix_data_deref(upd->peerupd); + unix_data_deref(upd); + return(0); } @@ -351,54 +368,57 @@ unix_proto_release(struct socket *sock, struct socket *peer) * Here we return EINVAL, but it may be necessary to re-bind. * I think thats what BSD does in the case of datagram sockets... */ -static int -unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, +static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len) { - char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; - struct unix_proto_data *upd = UN_DATA(sock); - unsigned long old_fs; - int i; - int er; - - dprintf(1, "UNIX: bind: socket 0x%x, len=%d\n", sock, sockaddr_len); - if (sockaddr_len <= UN_PATH_OFFSET || - sockaddr_len > sizeof(struct sockaddr_un)) { - dprintf(1, "UNIX: bind: bad length %d\n", sockaddr_len); - return(-EINVAL); - } - if (upd->sockaddr_len || upd->inode) { - printk("UNIX: bind: already bound!\n"); - return(-EINVAL); - } - er=verify_area(VERIFY_WRITE, umyaddr, sockaddr_len); - if(er) - return er; - memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len); - upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; - if (upd->sockaddr_un.sun_family != AF_UNIX) { - dprintf(1, "UNIX: bind: family is %d, not AF_UNIX(%d)\n", - upd->sockaddr_un.sun_family, AF_UNIX); - return(-EINVAL); - } - - memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET); - fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; - old_fs = get_fs(); - set_fs(get_ds()); - i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0); - if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL); - set_fs(old_fs); - if (i < 0) { - printk("UNIX: bind: can't open socket %s\n", fname); - return(i); - } - upd->sockaddr_len = sockaddr_len; /* now its legal */ - - dprintf(1, "UNIX: bind: bound socket address: "); - sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len); - dprintf(1, "to inode 0x%x\n", upd->inode); - return(0); + char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; + struct unix_proto_data *upd = UN_DATA(sock); + unsigned long old_fs; + int i; + int er; + + dprintf(1, "UNIX: bind: socket 0x%x, len=%d\n", sock, sockaddr_len); + if (sockaddr_len <= UN_PATH_OFFSET || sockaddr_len > sizeof(struct sockaddr_un)) + { + dprintf(1, "UNIX: bind: bad length %d\n", sockaddr_len); + return(-EINVAL); + } + if (upd->sockaddr_len || upd->inode) + { + printk("UNIX: bind: already bound!\n"); + return(-EINVAL); + } + er=verify_area(VERIFY_WRITE, umyaddr, sockaddr_len); + if(er) + return er; + memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len); + upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; + if (upd->sockaddr_un.sun_family != AF_UNIX) + { + dprintf(1, "UNIX: bind: family is %d, not AF_UNIX(%d)\n", + upd->sockaddr_un.sun_family, AF_UNIX); + return(-EINVAL); + } + + memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET); + fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; + old_fs = get_fs(); + set_fs(get_ds()); + i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0); + if (i == 0) + i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL); + set_fs(old_fs); + if (i < 0) + { + printk("UNIX: bind: can't open socket %s\n", fname); + return(i); + } + upd->sockaddr_len = sockaddr_len; /* now its legal */ + + dprintf(1, "UNIX: bind: bound socket address: "); + sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len); + dprintf(1, "to inode 0x%x\n", upd->inode); + return(0); } @@ -407,71 +427,79 @@ unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, * (I can't for the life of me find an application where that * wouldn't be the case!) */ -static int -unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, - int sockaddr_len, int flags) -{ - char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; - struct sockaddr_un sockun; - struct unix_proto_data *serv_upd; - struct inode *inode; - unsigned long old_fs; - int i; - int er; - - dprintf(1, "UNIX: connect: socket 0x%x, servlen=%d\n", sock, sockaddr_len); - - if (sockaddr_len <= UN_PATH_OFFSET || - sockaddr_len > sizeof(struct sockaddr_un)) { - dprintf(1, "UNIX: connect: bad length %d\n", sockaddr_len); - return(-EINVAL); - } - if (sock->state == SS_CONNECTING) return(-EINPROGRESS); - if (sock->state == SS_CONNECTED) return(-EISCONN); - - er=verify_area(VERIFY_READ, uservaddr, sockaddr_len); - if(er) - return er; - memcpy_fromfs(&sockun, uservaddr, sockaddr_len); - sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; - if (sockun.sun_family != AF_UNIX) { - dprintf(1, "UNIX: connect: family is %d, not AF_UNIX(%d)\n", - sockun.sun_family, AF_UNIX); - return(-EINVAL); - } - - /* - * Try to open the name in the filesystem - this is how we - * identify ourselves and our server. Note that we don't - * hold onto the inode that long, just enough to find our - * server. When we're connected, we mooch off the server. - */ - memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET); - fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; - old_fs = get_fs(); - set_fs(get_ds()); - i = open_namei(fname, 0, S_IFSOCK, &inode, NULL); - set_fs(old_fs); - if (i < 0) { - dprintf(1, "UNIX: connect: can't open socket %s\n", fname); - return(i); - } - serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode); - iput(inode); - if (!serv_upd) { - dprintf(1, "UNIX: connect: can't locate peer %s at inode 0x%x\n", - fname, inode); - return(-EINVAL); - } - if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) { - dprintf(1, "UNIX: connect: can't await connection\n"); - return(i); - } - if (sock->conn) { - unix_data_ref(UN_DATA(sock->conn)); - UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */ - } - return(0); + +static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len, int flags) +{ + char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; + struct sockaddr_un sockun; + struct unix_proto_data *serv_upd; + struct inode *inode; + unsigned long old_fs; + int i; + int er; + + dprintf(1, "UNIX: connect: socket 0x%x, servlen=%d\n", sock, sockaddr_len); + + if (sockaddr_len <= UN_PATH_OFFSET || sockaddr_len > sizeof(struct sockaddr_un)) + { + dprintf(1, "UNIX: connect: bad length %d\n", sockaddr_len); + return(-EINVAL); + } + if (sock->state == SS_CONNECTING) + return(-EINPROGRESS); + if (sock->state == SS_CONNECTED) + return(-EISCONN); + + er=verify_area(VERIFY_READ, uservaddr, sockaddr_len); + if(er) + return er; + memcpy_fromfs(&sockun, uservaddr, sockaddr_len); + sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; + if (sockun.sun_family != AF_UNIX) + { + dprintf(1, "UNIX: connect: family is %d, not AF_UNIX(%d)\n", + sockun.sun_family, AF_UNIX); + return(-EINVAL); + } + + /* + * Try to open the name in the filesystem - this is how we + * identify ourselves and our server. Note that we don't + * hold onto the inode that long, just enough to find our + * server. When we're connected, we mooch off the server. + */ + + memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET); + fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; + old_fs = get_fs(); + set_fs(get_ds()); + i = open_namei(fname, 0, S_IFSOCK, &inode, NULL); + set_fs(old_fs); + if (i < 0) + { + dprintf(1, "UNIX: connect: can't open socket %s\n", fname); + return(i); + } + serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode); + iput(inode); + if (!serv_upd) + { + dprintf(1, "UNIX: connect: can't locate peer %s at inode 0x%x\n", + fname, inode); + return(-EINVAL); + } + if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) + { + dprintf(1, "UNIX: connect: can't await connection\n"); + return(i); + } + if (sock->conn) + { + unix_data_ref(UN_DATA(sock->conn)); + UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */ + } + return(0); } @@ -481,146 +509,173 @@ unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, * for a wait area, and deadlock prevention in the case of a process * writing to itself is, ignored, in true unix fashion! */ -static int -unix_proto_socketpair(struct socket *sock1, struct socket *sock2) + +static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2) { - struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2); + struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2); - unix_data_ref(upd1); - unix_data_ref(upd2); - upd1->peerupd = upd2; - upd2->peerupd = upd1; - return(0); + unix_data_ref(upd1); + unix_data_ref(upd2); + upd1->peerupd = upd2; + upd2->peerupd = upd1; + return(0); } /* On accept, we ref the peer's data for safe writes. */ -static int -unix_proto_accept(struct socket *sock, struct socket *newsock, int flags) -{ - struct socket *clientsock; - - dprintf(1, "UNIX: accept: socket 0x%x accepted via socket 0x%x\n", - sock, newsock); - - /* - * If there aren't any sockets awaiting connection, - * then wait for one, unless nonblocking. - */ - while(!(clientsock = sock->iconn)) { - if (flags & O_NONBLOCK) return(-EAGAIN); - interruptible_sleep_on(sock->wait); - if (current->signal & ~current->blocked) { - dprintf(1, "UNIX: accept: sleep was interrupted\n"); - return(-ERESTARTSYS); - } - } - - /* - * Great. Finish the connection relative to server and client, - * wake up the client and return the new fd to the server. - */ - sock->iconn = clientsock->next; - clientsock->next = NULL; - newsock->conn = clientsock; - clientsock->conn = newsock; - clientsock->state = SS_CONNECTED; - newsock->state = SS_CONNECTED; - unix_data_ref(UN_DATA(clientsock)); - UN_DATA(newsock)->peerupd = UN_DATA(clientsock); - UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un; - UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len; - wake_up(clientsock->wait); - return(0); + +static int unix_proto_accept(struct socket *sock, struct socket *newsock, int flags) +{ + struct socket *clientsock; + + dprintf(1, "UNIX: accept: socket 0x%x accepted via socket 0x%x\n", + sock, newsock); + + /* + * If there aren't any sockets awaiting connection, + * then wait for one, unless nonblocking. + */ + while(!(clientsock = sock->iconn)) + { + if (flags & O_NONBLOCK) + return(-EAGAIN); + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) + { + dprintf(1, "UNIX: accept: sleep was interrupted\n"); + return(-ERESTARTSYS); + } + } + + /* + * Great. Finish the connection relative to server and client, + * wake up the client and return the new fd to the server. + */ + sock->iconn = clientsock->next; + clientsock->next = NULL; + newsock->conn = clientsock; + clientsock->conn = newsock; + clientsock->state = SS_CONNECTED; + newsock->state = SS_CONNECTED; + unix_data_ref(UN_DATA(clientsock)); + UN_DATA(newsock)->peerupd = UN_DATA(clientsock); + UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un; + UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len; + wake_up(clientsock->wait); + return(0); } -/* Gets the current name or the name of the connected socket. */ -static int -unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, - int *usockaddr_len, int peer) +/* + * Gets the current name or the name of the connected socket. + */ + +static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, + int *usockaddr_len, int peer) { - struct unix_proto_data *upd; - int len; - int er; - - dprintf(1, "UNIX: getname: socket 0x%x for %s\n", sock, peer?"peer":"self"); - if (peer) { - if (sock->state != SS_CONNECTED) { - dprintf(1, "UNIX: getname: socket not connected\n"); - return(-EINVAL); - } - upd = UN_DATA(sock->conn); - } else - upd = UN_DATA(sock); - - er=verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len)); - if(er) - return er; - if ((len = get_fs_long(usockaddr_len)) <= 0) return(-EINVAL); - if (len > upd->sockaddr_len) len = upd->sockaddr_len; - if (len) { - er=verify_area(VERIFY_WRITE, usockaddr, len); - if(er) - return er; - memcpy_tofs(usockaddr, &upd->sockaddr_un, len); - } - put_fs_long(len, usockaddr_len); - return(0); + struct unix_proto_data *upd; + int len; + int er; + + dprintf(1, "UNIX: getname: socket 0x%x for %s\n", sock, peer?"peer":"self"); + if (peer) + { + if (sock->state != SS_CONNECTED) + { + dprintf(1, "UNIX: getname: socket not connected\n"); + return(-EINVAL); + } + upd = UN_DATA(sock->conn); + } + else + upd = UN_DATA(sock); + + er=verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len)); + if(er) + return er; + if ((len = get_fs_long(usockaddr_len)) <= 0) + return(-EINVAL); + if (len > upd->sockaddr_len) + len = upd->sockaddr_len; + if (len) + { + er=verify_area(VERIFY_WRITE, usockaddr, len); + if(er) + return er; + memcpy_tofs(usockaddr, &upd->sockaddr_un, len); + } + put_fs_long(len, usockaddr_len); + return(0); } /* We read from our own buf. */ -static int -unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) -{ - struct unix_proto_data *upd; - int todo, avail; - int er; - - if ((todo = size) <= 0) return(0); - upd = UN_DATA(sock); - while(!(avail = UN_BUF_AVAIL(upd))) { - if (sock->state != SS_CONNECTED) { - dprintf(1, "UNIX: read: socket not connected\n"); - return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL); - } - dprintf(1, "UNIX: read: no data available...\n"); - if (nonblock) return(-EAGAIN); - interruptible_sleep_on(sock->wait); - if (current->signal & ~current->blocked) { - dprintf(1, "UNIX: read: interrupted\n"); - return(-ERESTARTSYS); - } - } - - /* - * Copy from the read buffer into the user's buffer, - * watching for wraparound. Then we wake up the writer. - */ - do { - int part, cando; - - if (avail <= 0) { - printk("UNIX: read: AVAIL IS NEGATIVE!!!\n"); - send_sig(SIGKILL, current, 1); - return(-EPIPE); - } - if ((cando = todo) > avail) cando = avail; - if (cando >(part = BUF_SIZE - upd->bp_tail)) cando = part; - dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n", - avail, todo, cando); - if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0) - return er; - memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando); - upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1); - ubuf += cando; - todo -= cando; - if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait); - avail = UN_BUF_AVAIL(upd); - } while(todo && avail); - return(size - todo); +static int unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) +{ + struct unix_proto_data *upd; + int todo, avail; + int er; + + if ((todo = size) <= 0) + return(0); + upd = UN_DATA(sock); + while(!(avail = UN_BUF_AVAIL(upd))) + { + if (sock->state != SS_CONNECTED) + { + dprintf(1, "UNIX: read: socket not connected\n"); + return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL); + } + dprintf(1, "UNIX: read: no data available...\n"); + if (nonblock) + return(-EAGAIN); + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) + { + dprintf(1, "UNIX: read: interrupted\n"); + return(-ERESTARTSYS); + } + } + + /* + * Copy from the read buffer into the user's buffer, + * watching for wraparound. Then we wake up the writer. + */ + + do + { + int part, cando; + + if (avail <= 0) + { + printk("UNIX: read: AVAIL IS NEGATIVE!!!\n"); + send_sig(SIGKILL, current, 1); + return(-EPIPE); + } + + if ((cando = todo) > avail) + cando = avail; + if (cando >(part = BUF_SIZE - upd->bp_tail)) + cando = part; + + dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n", + avail, todo, cando); + + if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0) + return er; + + memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando); + upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1); + ubuf += cando; + todo -= cando; + + if (sock->state == SS_CONNECTED) + wake_up(sock->conn->wait); + avail = UN_BUF_AVAIL(upd); + } + while(todo && avail); + return(size - todo); } @@ -629,274 +684,308 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) * peer so we are safe that the buffer remains, even after the * peer has disconnected, which we check other ways. */ -static int -unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) -{ - struct unix_proto_data *pupd; - int todo, space; - int er; - - if ((todo = size) <= 0) return(0); - if (sock->state != SS_CONNECTED) { - dprintf(1, "UNIX: write: socket not connected\n"); - if (sock->state == SS_DISCONNECTING) { - send_sig(SIGPIPE, current, 1); - return(-EPIPE); - } - return(-EINVAL); - } - pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */ - - while(!(space = UN_BUF_SPACE(pupd))) { - dprintf(1, "UNIX: write: no space left...\n"); - if (nonblock) return(-EAGAIN); - interruptible_sleep_on(sock->wait); - if (current->signal & ~current->blocked) { - dprintf(1, "UNIX: write: interrupted\n"); - return(-ERESTARTSYS); - } - if (sock->state == SS_DISCONNECTING) { - dprintf(1, "UNIX: write: disconnected(SIGPIPE)\n"); - send_sig(SIGPIPE, current, 1); - return(-EPIPE); - } - } - - /* - * Copy from the user's buffer to the write buffer, - * watching for wraparound. Then we wake up the reader. - */ - do { - int part, cando; - - if (space <= 0) { - printk("UNIX: write: SPACE IS NEGATIVE!!!\n"); - send_sig(SIGKILL, current, 1); - return(-EPIPE); - } +static int unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) +{ + struct unix_proto_data *pupd; + int todo, space; + int er; + + if ((todo = size) <= 0) + return(0); + if (sock->state != SS_CONNECTED) + { + dprintf(1, "UNIX: write: socket not connected\n"); + if (sock->state == SS_DISCONNECTING) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + return(-EINVAL); + } + pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */ + + while(!(space = UN_BUF_SPACE(pupd))) + { + dprintf(1, "UNIX: write: no space left...\n"); + if (nonblock) + return(-EAGAIN); + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) + { + dprintf(1, "UNIX: write: interrupted\n"); + return(-ERESTARTSYS); + } + if (sock->state == SS_DISCONNECTING) + { + dprintf(1, "UNIX: write: disconnected(SIGPIPE)\n"); + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + } - /* - * We may become disconnected inside this loop, so watch - * for it (peerupd is safe until we close). - */ - if (sock->state == SS_DISCONNECTING) { - send_sig(SIGPIPE, current, 1); - return(-EPIPE); - } - if ((cando = todo) > space) cando = space; - if (cando >(part = BUF_SIZE - pupd->bp_head)) cando = part; - dprintf(1, "UNIX: write: space=%d, todo=%d, cando=%d\n", - space, todo, cando); - er=verify_area(VERIFY_READ, ubuf, cando); - if(er) - return er; - memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando); - pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1); - ubuf += cando; - todo -= cando; - if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait); - space = UN_BUF_SPACE(pupd); - } while(todo && space); - return(size - todo); + /* + * Copy from the user's buffer to the write buffer, + * watching for wraparound. Then we wake up the reader. + */ + + do + { + int part, cando; + + if (space <= 0) + { + printk("UNIX: write: SPACE IS NEGATIVE!!!\n"); + send_sig(SIGKILL, current, 1); + return(-EPIPE); + } + + /* + * We may become disconnected inside this loop, so watch + * for it (peerupd is safe until we close). + */ + + if (sock->state == SS_DISCONNECTING) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + if ((cando = todo) > space) + cando = space; + + if (cando >(part = BUF_SIZE - pupd->bp_head)) + cando = part; + + dprintf(1, "UNIX: write: space=%d, todo=%d, cando=%d\n", + space, todo, cando); + + er=verify_area(VERIFY_READ, ubuf, cando); + if(er) + return er; + memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando); + pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1); + + ubuf += cando; + todo -= cando; + + if (sock->state == SS_CONNECTED) + wake_up(sock->conn->wait); + space = UN_BUF_SPACE(pupd); + } + while(todo && space); + return(size - todo); } -static int -unix_proto_select(struct socket *sock, int sel_type, select_table * wait) +static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait) { - struct unix_proto_data *upd, *peerupd; - - /* Handle server sockets specially. */ - if (sock->flags & SO_ACCEPTCON) { - if (sel_type == SEL_IN) { - dprintf(1, "UNIX: select: %sconnections pending\n", - sock->iconn ? "" : "no "); - if (sock->iconn) return(1); + struct unix_proto_data *upd, *peerupd; + + /* Handle server sockets specially. */ + if (sock->flags & SO_ACCEPTCON) + { + if (sel_type == SEL_IN) + { + dprintf(1, "UNIX: select: %sconnections pending\n", + sock->iconn ? "" : "no "); + if (sock->iconn) + return(1); + select_wait(sock->wait, wait); + return(sock->iconn ? 1 : 0); + } + dprintf(1, "UNIX: select: nothing else for server socket\n"); select_wait(sock->wait, wait); - return(sock->iconn ? 1 : 0); - } - dprintf(1, "UNIX: select: nothing else for server socket\n"); - select_wait(sock->wait, wait); - return(0); - } - - if (sel_type == SEL_IN) { - upd = UN_DATA(sock); - dprintf(1, "UNIX: select: there is%s data available\n", - UN_BUF_AVAIL(upd) ? "" : " no"); - if (UN_BUF_AVAIL(upd)) /* even if disconnected */ + return(0); + } + + if (sel_type == SEL_IN) + { + upd = UN_DATA(sock); + dprintf(1, "UNIX: select: there is%s data available\n", + UN_BUF_AVAIL(upd) ? "" : " no"); + if (UN_BUF_AVAIL(upd)) /* even if disconnected */ return(1); - else if (sock->state != SS_CONNECTED) { - dprintf(1, "UNIX: select: socket not connected(read EOF)\n"); - return(1); - } - select_wait(sock->wait,wait); - return(0); - } - if (sel_type == SEL_OUT) { - if (sock->state != SS_CONNECTED) { - dprintf(1, "UNIX: select: socket not connected(write EOF)\n"); - return(1); - } - peerupd = UN_DATA(sock->conn); - dprintf(1, "UNIX: select: there is%s space available\n", - UN_BUF_SPACE(peerupd) ? "" : " no"); - if (UN_BUF_SPACE(peerupd) > 0) return(1); - select_wait(sock->wait,wait); - return(0); - } - - /* SEL_EX */ - dprintf(1, "UNIX: select: there are no exceptions here?!\n"); - return(0); + else if (sock->state != SS_CONNECTED) + { + dprintf(1, "UNIX: select: socket not connected(read EOF)\n"); + return(1); + } + select_wait(sock->wait,wait); + return(0); + } + if (sel_type == SEL_OUT) + { + if (sock->state != SS_CONNECTED) + { + dprintf(1, "UNIX: select: socket not connected(write EOF)\n"); + return(1); + } + peerupd = UN_DATA(sock->conn); + dprintf(1, "UNIX: select: there is%s space available\n", + UN_BUF_SPACE(peerupd) ? "" : " no"); + if (UN_BUF_SPACE(peerupd) > 0) + return(1); + select_wait(sock->wait,wait); + return(0); + } + + /* SEL_EX */ + dprintf(1, "UNIX: select: there are no exceptions here?!\n"); + return(0); } -static int -unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +static int unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { - struct unix_proto_data *upd, *peerupd; - int er; - - upd = UN_DATA(sock); - peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL; - - switch(cmd) { - case TIOCINQ: - if (sock->flags & SO_ACCEPTCON) return(-EINVAL); - er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); - if(er) - return er; - if (UN_BUF_AVAIL(upd) || peerupd) - put_fs_long(UN_BUF_AVAIL(upd),(unsigned long *)arg); - else - put_fs_long(0,(unsigned long *)arg); - break; - case TIOCOUTQ: - if (sock->flags & SO_ACCEPTCON) return(-EINVAL); - er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); - if(er) - return er; - if (peerupd) put_fs_long(UN_BUF_SPACE(peerupd), - (unsigned long *)arg); - else - put_fs_long(0,(unsigned long *)arg); - break; - default: - return(-EINVAL); - } - return(0); + struct unix_proto_data *upd, *peerupd; + int er; + + upd = UN_DATA(sock); + peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL; + + switch(cmd) + { + case TIOCINQ: + if (sock->flags & SO_ACCEPTCON) + return(-EINVAL); + er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); + if(er) + return er; + if (UN_BUF_AVAIL(upd) || peerupd) + put_fs_long(UN_BUF_AVAIL(upd),(unsigned long *)arg); + else + put_fs_long(0,(unsigned long *)arg); + break; + case TIOCOUTQ: + if (sock->flags & SO_ACCEPTCON) + return(-EINVAL); + er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); + if(er) + return er; + if (peerupd) + put_fs_long(UN_BUF_SPACE(peerupd), (unsigned long *)arg); + else + put_fs_long(0,(unsigned long *)arg); + break; + default: + return(-EINVAL); + } + return(0); } -static int -unix_open(struct inode * inode, struct file * file) +static int unix_open(struct inode * inode, struct file * file) { - int minor; + int minor; - dprintf(1, "UNIX: open\n"); - minor = MINOR(inode->i_rdev); - if (minor != 0) return(-ENODEV); + dprintf(1, "UNIX: open\n"); + minor = MINOR(inode->i_rdev); + + if (minor != 0) + return(-ENODEV); - return(0); + return(0); } -static void -unix_close(struct inode * inode, struct file * file) +static void unix_close(struct inode * inode, struct file * file) { - dprintf(1, "UNIX: close\n"); + dprintf(1, "UNIX: close\n"); } -static int -unix_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int unix_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { - int minor, ret; - int er; - - dprintf(1, "UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg); - minor = MINOR(inode->i_rdev); - if (minor != 0) return(-ENODEV); - - ret = -EINVAL; - switch(cmd) { - case DDIOCSDBG: - er=verify_area(VERIFY_READ,(void *)arg, sizeof(int)); - if(er) - return er; - unix_debug = get_fs_long((int *)arg); - if (unix_debug != 0 && unix_debug != 1) { - unix_debug = 0; - return(-EINVAL); - } - return(0); - case SIOCSIFLINK: - printk("UNIX: cannot link streams!\n"); - break; - default: - break; - } - return(ret); + int minor, ret; + int er; + + dprintf(1, "UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg); + minor = MINOR(inode->i_rdev); + + if (minor != 0) + return(-ENODEV); + + ret = -EINVAL; + switch(cmd) + { + case DDIOCSDBG: + er=verify_area(VERIFY_READ,(void *)arg, sizeof(int)); + if(er) + return er; + unix_debug = get_fs_long((int *)arg); + if (unix_debug != 0 && unix_debug != 1) + { + unix_debug = 0; + return(-EINVAL); + } + return(0); + case SIOCSIFLINK: + printk("UNIX: cannot link streams!\n"); + break; + default: + break; + } + return(ret); } -static struct file_operations unix_fops = { - NULL, /* LSEEK */ - NULL, /* READ */ - NULL, /* WRITE */ - NULL, /* READDIR */ - NULL, /* SELECT */ - unix_ioctl, /* IOCTL */ - NULL, /* MMAP */ - unix_open, /* OPEN */ - unix_close /* CLOSE */ +static struct file_operations unix_fops = +{ + NULL, /* LSEEK */ + NULL, /* READ */ + NULL, /* WRITE */ + NULL, /* READDIR */ + NULL, /* SELECT */ + unix_ioctl, /* IOCTL */ + NULL, /* MMAP */ + unix_open, /* OPEN */ + unix_close /* CLOSE */ }; static struct proto_ops unix_proto_ops = { - AF_UNIX, - unix_proto_create, - unix_proto_dup, - unix_proto_release, - unix_proto_bind, - unix_proto_connect, - unix_proto_socketpair, - unix_proto_accept, - unix_proto_getname, - unix_proto_read, - unix_proto_write, - unix_proto_select, - unix_proto_ioctl, - unix_proto_listen, - unix_proto_send, - unix_proto_recv, - unix_proto_sendto, - unix_proto_recvfrom, - unix_proto_shutdown, - unix_proto_setsockopt, - unix_proto_getsockopt, - NULL /* unix_proto_fcntl */ + AF_UNIX, + unix_proto_create, + unix_proto_dup, + unix_proto_release, + unix_proto_bind, + unix_proto_connect, + unix_proto_socketpair, + unix_proto_accept, + unix_proto_getname, + unix_proto_read, + unix_proto_write, + unix_proto_select, + unix_proto_ioctl, + unix_proto_listen, + unix_proto_send, + unix_proto_recv, + unix_proto_sendto, + unix_proto_recvfrom, + unix_proto_shutdown, + unix_proto_setsockopt, + unix_proto_getsockopt, + NULL /* unix_proto_fcntl */ }; -void -unix_proto_init(struct ddi_proto *pro) +void unix_proto_init(struct ddi_proto *pro) { - struct unix_proto_data *upd; - - dprintf(1, "%s: init: initializing...\n", pro->name); - if (register_chrdev(AF_UNIX_MAJOR, "af_unix", &unix_fops) < 0) { - printk("%s: cannot register major device %d!\n", - pro->name, AF_UNIX_MAJOR); - return; - } - - /* Tell SOCKET that we are alive... */ - (void) sock_register(unix_proto_ops.family, &unix_proto_ops); - - for(upd = unix_datas; upd <= last_unix_data; ++upd) { - upd->refcnt = 0; - } + struct unix_proto_data *upd; + + dprintf(1, "%s: init: initializing...\n", pro->name); + if (register_chrdev(AF_UNIX_MAJOR, "af_unix", &unix_fops) < 0) + { + printk("%s: cannot register major device %d!\n", + pro->name, AF_UNIX_MAJOR); + return; + } + + /* Tell SOCKET that we are alive... */ + (void) sock_register(unix_proto_ops.family, &unix_proto_ops); + + for(upd = unix_datas; upd <= last_unix_data; ++upd) + { + upd->refcnt = 0; + } } |