diff options
| author | Linus Torvalds <torvalds@home.osdl.org> | 2004-01-19 07:39:54 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2004-01-19 07:39:54 -0800 |
| commit | 3940ed85276e69985e25ad687cc7dcb952e5d8a5 (patch) | |
| tree | 8273ec807ddcdde1811a820c1e1d405489197117 | |
| parent | 4d53a003f12e93c181dff573ea866a912e758b7c (diff) | |
| parent | 4705cae7950a8eec85a9370ef42a1037212e9069 (diff) | |
Merge bk://kernel.bkbits.net/davem/net-2.6
into home.osdl.org:/home/torvalds/v2.5/linux
36 files changed, 1171 insertions, 300 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 21092ca98652..65f32758780e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1697,7 +1697,7 @@ config NET_POCKET <file:Documentation/Changes>) and you can say N here. Laptop users should read the Linux Laptop home page at - <http://www.cs.utexas.edu/users/kharker/linux-laptop/>. + <http://www.linux-on-laptops.com/>. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all diff --git a/include/linux/sctp.h b/include/linux/sctp.h index a022f9f629f6..9ddc3bd5cd81 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -439,12 +439,13 @@ typedef enum { * 0x0101 Operation Refused Due to Resource Shortage. * 0x0102 Request to Delete Source IP Address. * 0x0103 Association Aborted due to illegal ASCONF-ACK + * 0x0104 Request refused - no authorization. */ SCTP_ERROR_DEL_LAST_IP = __constant_htons(0x0100), SCTP_ERROR_RSRC_LOW = __constant_htons(0x0101), SCTP_ERROR_DEL_SRC_IP = __constant_htons(0x0102), SCTP_ERROR_ASCONF_ACK = __constant_htons(0x0103), - + SCTP_ERROR_REQ_REFUSED = __constant_htons(0x0104) } sctp_error_t; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index b8be51acff95..0d5b121f54a0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -580,6 +580,7 @@ enum { NET_SCTP_HB_INTERVAL = 10, NET_SCTP_PRESERVE_ENABLE = 11, NET_SCTP_MAX_BURST = 12, + NET_SCTP_ADDIP_ENABLE = 13, }; /* /proc/sys/net/bridge */ @@ -737,6 +738,8 @@ extern int proc_dointvec_minmax(ctl_table *, int, struct file *, void __user *, size_t *); extern int proc_dointvec_jiffies(ctl_table *, int, struct file *, void __user *, size_t *); +extern int proc_dointvec_userhz_jiffies(ctl_table *, int, struct file *, + void __user *, size_t *); extern int proc_doulongvec_minmax(ctl_table *, int, struct file *, void __user *, size_t *); extern int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int, diff --git a/include/net/neighbour.h b/include/net/neighbour.h index e016389694a8..857f03acaffd 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -47,9 +47,7 @@ #include <linux/skbuff.h> #include <linux/err.h> -#ifdef CONFIG_SYSCTL #include <linux/sysctl.h> -#endif #define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE) #define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 5e17983fffdc..f031313bb3ef 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -1,8 +1,8 @@ /* SCTP kernel reference Implementation + * (C) Copyright IBM Corp. 2001, 2003 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001 Intel Corp. - * Copyright (c) 2001-2002 International Business Machines Corp. * * This file is part of the SCTP kernel reference Implementation * @@ -75,8 +75,6 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; #define SCTP_NUM_BASE_CHUNK_TYPES (SCTP_CID_BASE_MAX + 1) #define SCTP_NUM_CHUNK_TYPES (SCTP_NUM_BASE_CHUNKTYPES + 2) -#define SCTP_CID_ADDIP_MIN SCTP_CID_ASCONF -#define SCTP_CID_ADDIP_MAX SCTP_CID_ASCONF_ACK #define SCTP_NUM_ADDIP_CHUNK_TYPES 2 /* These are the different flavours of event. */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 7edb15d6561a..3fa80e611ba1 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -115,8 +115,10 @@ #define SCTP_STATIC static #endif -#define MSECS_TO_JIFFIES(msec) (msec * HZ / 1000) -#define JIFFIES_TO_MSECS(jiff) (jiff * 1000 / HZ) +#define MSECS_TO_JIFFIES(msec) \ + (((msec / 1000) * HZ) + ((msec % 1000) * HZ) / 1000) +#define JIFFIES_TO_MSECS(jiff) \ + (((jiff / HZ) * 1000) + ((jiff % HZ) * 1000) / HZ) /* * Function declarations. diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2b62b1afef2a..60dea46dec21 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -268,15 +268,15 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, union sctp_addr *, struct sockaddr *, - int, int); + int, __u16); struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr); -struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, - int serial, int vparam_len); - +struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, + __u32 serial, int vparam_len); struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, - struct sctp_chunk *asconf, - int vparam_len); + struct sctp_chunk *asconf); +int sctp_process_asconf_ack(struct sctp_association *asoc, + struct sctp_chunk *asconf_ack); void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *); @@ -431,6 +431,21 @@ static inline int SSN_lte(__u16 s, __u16 t) return (((s) == (t)) || (((s) - (t)) & SSN_SIGN_BIT)); } +/* + * ADDIP 3.1.1 + * The valid range of Serial Number is from 0 to 4294967295 (2**32 - 1). Serial + * Numbers wrap back to 0 after reaching 4294967295. + */ +enum { + ADDIP_SERIAL_SIGN_BIT = (1<<31) +}; + +static inline int ADDIP_SERIAL_gte(__u16 s, __u16 t) +{ + return (((s) == (t)) || (((t) - (s)) & ADDIP_SERIAL_SIGN_BIT)); +} + + /* Run sctp_add_cmd() generating a BUG() if there is a failure. */ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) { diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index bf2745654d7a..92394f3de76a 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -190,6 +190,9 @@ extern struct sctp_globals { */ struct list_head local_addr_list; spinlock_t local_addr_lock; + + /* Flag to indicate if addip is enabled. */ + int addip_enable; } sctp_globals; #define sctp_rto_initial (sctp_globals.rto_initial) @@ -217,6 +220,7 @@ extern struct sctp_globals { #define sctp_port_hashtable (sctp_globals.port_hashtable) #define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_lock (sctp_globals.local_addr_lock) +#define sctp_addip_enable (sctp_globals.addip_enable) /* SCTP Socket type: UDP or TCP style. */ typedef enum { @@ -1397,6 +1401,11 @@ struct sctp_association { /* Does peer support ADDIP? */ __u8 asconf_capable; + /* This mask is used to disable sending the ASCONF chunk + * with specified parameter to peer. + */ + __u16 addip_disabled_mask; + struct sctp_inithdr i; int cookie_len; void *cookie; @@ -1708,6 +1717,8 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc, struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *, const union sctp_addr *address, const int gfp); +void sctp_assoc_del_peer(struct sctp_association *asoc, + const union sctp_addr *addr); void sctp_assoc_control_transport(struct sctp_association *, struct sctp_transport *, sctp_transport_cmd_t, sctp_sn_error_t); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 806c9de5ce08..059bd643f39f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -37,6 +37,7 @@ #include <linux/hugetlb.h> #include <linux/security.h> #include <linux/initrd.h> +#include <linux/times.h> #include <asm/uaccess.h> #ifdef CONFIG_ROOT_NFS @@ -1070,8 +1071,8 @@ int do_sysctl_strategy (ctl_table *table, * cover common cases - * * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), - * proc_dointvec_minmax(), proc_doulongvec_ms_jiffies_minmax(), - * proc_doulongvec_minmax() + * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(), + * proc_doulongvec_ms_jiffies_minmax(), proc_doulongvec_minmax() * * It is the handler's job to read the input buffer from user memory * and process it. The handler should return 0 on success. @@ -1342,19 +1343,36 @@ static int proc_doutsstring(ctl_table *table, int write, struct file *filp, return r; } -#define OP_SET 0 -#define OP_AND 1 -#define OP_OR 2 -#define OP_MAX 3 -#define OP_MIN 4 +static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, + int *valp, + int write, void *data) +{ + if (write) { + *valp = *negp ? -*lvalp : *lvalp; + } else { + int val = *valp; + if (val < 0) { + *negp = -1; + *lvalp = (unsigned long)-val; + } else { + *negp = 0; + *lvalp = (unsigned long)val; + } + } + return 0; +} static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, - void __user *buffer, size_t *lenp, int conv, int op) + void __user *buffer, size_t *lenp, + int (*conv)(int *negp, unsigned long *lvalp, int *valp, + int write, void *data), + void *data) { +#define TMPBUFLEN 20 int *i, vleft, first=1, neg, val; + unsigned long lval; size_t left, len; - #define TMPBUFLEN 20 char buf[TMPBUFLEN], *p; if (!table->data || !table->maxlen || !*lenp || @@ -1364,9 +1382,12 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, } i = (int *) table->data; - vleft = table->maxlen / sizeof(int); + vleft = table->maxlen / sizeof(*i); left = *lenp; - + + if (!conv) + conv = do_proc_dointvec_conv; + for (; left && vleft--; i++, first=0) { if (write) { while (left) { @@ -1382,8 +1403,8 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, break; neg = 0; len = left; - if (len > TMPBUFLEN-1) - len = TMPBUFLEN-1; + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; if(copy_from_user(buf, buffer, len)) return -EFAULT; buf[len] = 0; @@ -1394,7 +1415,9 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, } if (*p < '0' || *p > '9') break; - val = simple_strtoul(p, &p, 0) * conv; + + lval = simple_strtoul(p, &p, 0); + len = p-buf; if ((len < left) && *p && !isspace(*p)) break; @@ -1402,22 +1425,18 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, val = -val; buffer += len; left -= len; - switch(op) { - case OP_SET: *i = val; break; - case OP_AND: *i &= val; break; - case OP_OR: *i |= val; break; - case OP_MAX: if(*i < val) - *i = val; - break; - case OP_MIN: if(*i > val) - *i = val; - break; - } + + if (conv(&neg, &lval, i, 1, data)) + break; } else { p = buf; if (!first) *p++ = '\t'; - sprintf(p, "%d", (*i) / conv); + + if (conv(&neg, &lval, i, 0, data)) + break; + + sprintf(p, "%s%lu", neg ? "-" : "", lval); len = strlen(buf); if (len > left) len = left; @@ -1449,6 +1468,7 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, *lenp -= left; filp->f_pos += *lenp; return 0; +#undef TMPBUFLEN } /** @@ -1467,7 +1487,45 @@ static int do_proc_dointvec(ctl_table *table, int write, struct file *filp, int proc_dointvec(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp) { - return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET); + return do_proc_dointvec(table,write,filp,buffer,lenp, + NULL,NULL); +} + +#define OP_SET 0 +#define OP_AND 1 +#define OP_OR 2 +#define OP_MAX 3 +#define OP_MIN 4 + +static int do_proc_dointvec_bset_conv(int *negp, unsigned long *lvalp, + int *valp, + int write, void *data) +{ + int op = *(int *)data; + if (write) { + int val = *negp ? -*lvalp : *lvalp; + switch(op) { + case OP_SET: *valp = val; break; + case OP_AND: *valp &= val; break; + case OP_OR: *valp |= val; break; + case OP_MAX: if(*valp < val) + *valp = val; + break; + case OP_MIN: if(*valp > val) + *valp = val; + break; + } + } else { + int val = *valp; + if (val < 0) { + *negp = -1; + *lvalp = (unsigned long)-val; + } else { + *negp = 0; + *lvalp = (unsigned long)val; + } + } + return 0; } /* @@ -1477,11 +1535,44 @@ int proc_dointvec(ctl_table *table, int write, struct file *filp, int proc_dointvec_bset(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp) { + int op; + if (!capable(CAP_SYS_MODULE)) { return -EPERM; } - return do_proc_dointvec(table,write,filp,buffer,lenp,1, - (current->pid == 1) ? OP_SET : OP_AND); + + op = (current->pid == 1) ? OP_SET : OP_AND; + return do_proc_dointvec(table,write,filp,buffer,lenp, + do_proc_dointvec_bset_conv,&op); +} + +struct do_proc_dointvec_minmax_conv_param { + int *min; + int *max; +}; + +static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, + int *valp, + int write, void *data) +{ + struct do_proc_dointvec_minmax_conv_param *param = data; + if (write) { + int val = *negp ? -*lvalp : *lvalp; + if ((param->min && *param->min > val) || + (param->max && *param->max < val)) + return -EINVAL; + *valp = val; + } else { + int val = *valp; + if (val < 0) { + *negp = -1; + *lvalp = (unsigned long)-val; + } else { + *negp = 0; + *lvalp = (unsigned long)val; + } + } + return 0; } /** @@ -1503,98 +1594,12 @@ int proc_dointvec_bset(ctl_table *table, int write, struct file *filp, int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp) { - int *i, *min, *max, vleft, first=1, neg, val; - size_t len, left; - #define TMPBUFLEN 20 - char buf[TMPBUFLEN], *p; - - if (!table->data || !table->maxlen || !*lenp || - (filp->f_pos && !write)) { - *lenp = 0; - return 0; - } - - i = (int *) table->data; - min = (int *) table->extra1; - max = (int *) table->extra2; - vleft = table->maxlen / sizeof(int); - left = *lenp; - - for (; left && vleft--; i++, min++, max++, first=0) { - if (write) { - while (left) { - char c; - if(get_user(c, (char *) buffer)) - return -EFAULT; - if (!isspace(c)) - break; - left--; - buffer++; - } - if (!left) - break; - neg = 0; - len = left; - if (len > TMPBUFLEN-1) - len = TMPBUFLEN-1; - if(copy_from_user(buf, buffer, len)) - return -EFAULT; - buf[len] = 0; - p = buf; - if (*p == '-' && left > 1) { - neg = 1; - left--, p++; - } - if (*p < '0' || *p > '9') - break; - val = simple_strtoul(p, &p, 0); - len = p-buf; - if ((len < left) && *p && !isspace(*p)) - break; - if (neg) - val = -val; - buffer += len; - left -= len; - - if ((min && val < *min) || (max && val > *max)) - continue; - *i = val; - } else { - p = buf; - if (!first) - *p++ = '\t'; - sprintf(p, "%d", *i); - len = strlen(buf); - if (len > left) - len = left; - if(copy_to_user(buffer, buf, len)) - return -EFAULT; - left -= len; - buffer += len; - } - } - - if (!write && !first && left) { - if(put_user('\n', (char *) buffer)) - return -EFAULT; - left--, buffer++; - } - if (write) { - p = (char *) buffer; - while (left) { - char c; - if(get_user(c, p++)) - return -EFAULT; - if (!isspace(c)) - break; - left--; - } - } - if (write && first) - return -EINVAL; - *lenp -= left; - filp->f_pos += *lenp; - return 0; + struct do_proc_dointvec_minmax_conv_param param = { + .min = (int *) table->extra1, + .max = (int *) table->extra2, + }; + return do_proc_dointvec(table, write, filp, buffer, lenp, + do_proc_dointvec_minmax_conv, ¶m); } static int do_proc_doulongvec_minmax(ctl_table *table, int write, @@ -1749,6 +1754,48 @@ int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write, } +static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, + int *valp, + int write, void *data) +{ + if (write) { + *valp = *negp ? -(*lvalp*HZ) : (*lvalp*HZ); + } else { + int val = *valp; + unsigned long lval; + if (val < 0) { + *negp = -1; + lval = (unsigned long)-val; + } else { + *negp = 0; + lval = (unsigned long)val; + } + *lvalp = lval / HZ; + } + return 0; +} + +static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, + int *valp, + int write, void *data) +{ + if (write) { + *valp = clock_t_to_jiffies(*negp ? -*lvalp : *lvalp); + } else { + int val = *valp; + unsigned long lval; + if (val < 0) { + *negp = -1; + lval = (unsigned long)-val; + } else { + *negp = 0; + lval = (unsigned long)val; + } + *lvalp = jiffies_to_clock_t(lval); + } + return 0; +} + /** * proc_dointvec_jiffies - read a vector of integers as seconds * @table: the sysctl table @@ -1767,7 +1814,30 @@ int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write, int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp) { - return do_proc_dointvec(table,write,filp,buffer,lenp,HZ,OP_SET); + return do_proc_dointvec(table,write,filp,buffer,lenp, + do_proc_dointvec_jiffies_conv,NULL); +} + +/** + * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds + * @table: the sysctl table + * @write: %TRUE if this is a write to the sysctl file + * @filp: the file structure + * @buffer: the user buffer + * @lenp: the size of the user buffer + * + * Reads/writes up to table->maxlen/sizeof(unsigned int) integer + * values from/to the user buffer, treated as an ASCII string. + * The values read are assumed to be in 1/USER_HZ seconds, and + * are converted into jiffies. + * + * Returns 0 on success. + */ +int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp) +{ + return do_proc_dointvec(table,write,filp,buffer,lenp, + do_proc_dointvec_userhz_jiffies_conv,NULL); } #else /* CONFIG_PROC_FS */ @@ -1808,6 +1878,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, return -ENOSYS; } +int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp) { @@ -1995,6 +2071,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, return -ENOSYS; } +int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp, void __user *buffer, size_t *lenp) { @@ -2027,6 +2109,7 @@ void unregister_sysctl_table(struct ctl_table_header * table) EXPORT_SYMBOL(proc_dointvec); EXPORT_SYMBOL(proc_dointvec_jiffies); EXPORT_SYMBOL(proc_dointvec_minmax); +EXPORT_SYMBOL(proc_dointvec_userhz_jiffies); EXPORT_SYMBOL(proc_dostring); EXPORT_SYMBOL(proc_doulongvec_minmax); EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 43472c6fc039..75d2c4abc0ec 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1526,7 +1526,12 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock, SOCK_DEBUG(sk, "AX.25: Appending user data\n"); /* User data follows immediately after the AX.25 data */ - memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + err = -EFAULT; + kfree_skb(skb); + goto out; + } + skb->nh.raw = skb->data; /* Add the PID if one is not supplied by the user in the skb */ diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 9fbef2a32267..9651cdfa87cf 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -435,8 +435,10 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb, struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); struct net_device **d = (struct net_device **)(skb->cb); +#ifdef CONFIG_SYSCTL if (!brnf_call_arptables) return NF_ACCEPT; +#endif if (skb->protocol != __constant_htons(ETH_P_ARP)) { if (!IS_VLAN_ARP) diff --git a/net/core/dst.c b/net/core/dst.c index 9a37756109ec..ba4856ccd57b 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -40,7 +40,7 @@ static void dst_run_gc(unsigned long); static void ___dst_free(struct dst_entry * dst); static struct timer_list dst_gc_timer = - TIMER_INITIALIZER(dst_run_gc, 0, DST_GC_MIN); + TIMER_INITIALIZER(dst_run_gc, DST_GC_MIN, 0); static void dst_run_gc(unsigned long dummy) { diff --git a/net/core/neighbour.c b/net/core/neighbour.c index c1b4a4496309..e711fdcdc11d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -24,6 +24,7 @@ #ifdef CONFIG_SYSCTL #include <linux/sysctl.h> #endif +#include <linux/times.h> #include <net/neighbour.h> #include <net/dst.h> #include <net/sock.h> @@ -1510,7 +1511,7 @@ struct neigh_sysctl_table { .procname = "retrans_time", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = &proc_dointvec_userhz_jiffies, }, { .ctl_name = NET_NEIGH_REACHABLE_TIME, @@ -1555,21 +1556,21 @@ struct neigh_sysctl_table { .procname = "anycast_delay", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = &proc_dointvec_userhz_jiffies, }, { .ctl_name = NET_NEIGH_PROXY_DELAY, .procname = "proxy_delay", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = &proc_dointvec_userhz_jiffies, }, { .ctl_name = NET_NEIGH_LOCKTIME, .procname = "locktime", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec, + .proc_handler = &proc_dointvec_userhz_jiffies, }, { .ctl_name = NET_NEIGH_GC_INTERVAL, diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 0652fdaf9079..cfabfe1a1214 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -88,7 +88,7 @@ #define cycles() ((u32)get_cycles()) -#define VERSION "pktgen version 1.3" +#define VERSION "pktgen version 1.31" static char version[] __initdata = "pktgen.c: v1.3: Packet Generator for packet performance testing.\n"; @@ -720,8 +720,18 @@ static void inject(struct pktgen_info* info) { char *p = info->result; - __u64 pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000); - __u64 bps = pps * 8 * (info->pkt_size + 4); /* take 32bit ethernet CRC into account */ + __u64 bps, pps = 0; + + if (total > 1000) + pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000); + else if(total > 100) + pps = (__u32)(info->sofar * 10000) / ((__u32)(total) / 100); + else if(total > 10) + pps = (__u32)(info->sofar * 100000) / ((__u32)(total) / 10); + else if(total > 1) + pps = (__u32)(info->sofar * 1000000) / (__u32)total; + + bps = pps * 8 * (info->pkt_size + 4); /* take 32bit ethernet CRC into account */ p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags) %llupps %lluMb/sec (%llubps) errors: %llu", (unsigned long long) total, (unsigned long long) (total - idle), diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index cd82363b5897..6ee8011d0bc3 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -90,7 +90,7 @@ config IP_ROUTE_NAT destination addresses of packets that pass through it, in a manner you specify. General information about Network Address Translation can be gotten from the document - <http://www.csn.tu-chemnitz.de/~mha/linux-ip-nat/diplom/nat.html>. + <http://www.hasenstein.com/linux-ip-nat/diplom/nat.html>. config IP_ROUTE_MULTIPATH bool "IP: equal cost multipath" diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d20f6647bc70..7632e842174f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3039,7 +3039,7 @@ static int addrconf_sysctl_forward_strategy(ctl_table *table, static struct addrconf_sysctl_table { struct ctl_table_header *sysctl_header; - ctl_table addrconf_vars[16]; + ctl_table addrconf_vars[17]; ctl_table addrconf_dev[2]; ctl_table addrconf_conf_dir[2]; ctl_table addrconf_proto_dir[2]; @@ -3180,6 +3180,9 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = 0, /* sentinel */ + } }, .addrconf_dev = { { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 6d450f2909e9..4bde99b2257e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -294,6 +294,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) __u32 v4addr = 0; unsigned short snum; int addr_type = 0; + int err = 0; /* If the socket has its own bind function then use it. */ if (sk->sk_prot->bind) @@ -305,24 +306,6 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) return -EINVAL; - /* Check if the address belongs to the host. */ - if (addr_type == IPV6_ADDR_MAPPED) { - v4addr = addr->sin6_addr.s6_addr32[3]; - if (inet_addr_type(v4addr) != RTN_LOCAL) - return -EADDRNOTAVAIL; - } else { - if (addr_type != IPV6_ADDR_ANY) { - /* ipv4 addr of the socket is invalid. Only the - * unspecified and mapped address have a v4 equivalent. - */ - v4addr = LOOPBACK4_IPV6; - if (!(addr_type & IPV6_ADDR_MULTICAST)) { - if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) - return -EADDRNOTAVAIL; - } - } - } - snum = ntohs(addr->sin6_port); if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; @@ -331,23 +314,56 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* Check these errors (active socket, double bind). */ if (sk->sk_state != TCP_CLOSE || inet->num) { - release_sock(sk); - return -EINVAL; + err = -EINVAL; + goto out; } - if (addr_type & IPV6_ADDR_LINKLOCAL) { - if (addr_len >= sizeof(struct sockaddr_in6) && - addr->sin6_scope_id) { - /* Override any existing binding, if another one - * is supplied by user. - */ - sk->sk_bound_dev_if = addr->sin6_scope_id; + /* Check if the address belongs to the host. */ + if (addr_type == IPV6_ADDR_MAPPED) { + v4addr = addr->sin6_addr.s6_addr32[3]; + if (inet_addr_type(v4addr) != RTN_LOCAL) { + err = -EADDRNOTAVAIL; + goto out; } + } else { + if (addr_type != IPV6_ADDR_ANY) { + struct net_device *dev = NULL; + + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another one + * is supplied by user. + */ + sk->sk_bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (!sk->sk_bound_dev_if) { + err = -EINVAL; + goto out; + } + dev = dev_get_by_index(sk->sk_bound_dev_if); + if (!dev) { + err = -ENODEV; + goto out; + } + } - /* Binding to link-local address requires an interface */ - if (!sk->sk_bound_dev_if) { - release_sock(sk); - return -EINVAL; + /* ipv4 addr of the socket is invalid. Only the + * unspecified and mapped address have a v4 equivalent. + */ + v4addr = LOOPBACK4_IPV6; + if (!(addr_type & IPV6_ADDR_MULTICAST)) { + if (!ipv6_chk_addr(&addr->sin6_addr, dev)) { + if (dev) + dev_put(dev); + err = -EADDRNOTAVAIL; + goto out; + } + } + if (dev) + dev_put(dev); } } @@ -362,8 +378,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* Make sure we are allowed to bind here. */ if (sk->sk_prot->get_port(sk, snum)) { inet_reset_saddr(sk); - release_sock(sk); - return -EADDRINUSE; + err = -EADDRINUSE; + goto out; } if (addr_type != IPV6_ADDR_ANY) @@ -373,9 +389,9 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) inet->sport = ntohs(inet->num); inet->dport = 0; inet->daddr = 0; +out: release_sock(sk); - - return 0; + return err; } int inet6_release(struct socket *sock) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 6b2f90f02fbe..f0d26709c56f 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -265,6 +265,8 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, int err = 0; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + int addr_type; + struct net_device *dev = NULL; if (cmsg->cmsg_len < sizeof(struct cmsghdr) || (unsigned long)(((char*)cmsg - (char*)msg->msg_control) @@ -291,16 +293,30 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, fl->oif = src_info->ipi6_ifindex; } - if (!ipv6_addr_any(&src_info->ipi6_addr)) { - if (!ipv6_chk_addr(&src_info->ipi6_addr, NULL)) { - err = -EINVAL; - goto exit_f; - } + addr_type = ipv6_addr_type(&src_info->ipi6_addr); - ipv6_addr_copy(&fl->fl6_src, - &src_info->ipi6_addr); + if (ipv6_addr_type == IPV6_ADDR_ANY) + break; + + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (!src_info->ipi6_ifindex) + return -EINVAL; + else { + dev = dev_get_by_index(src_info->ipi6_ifindex); + if (!dev) + return -ENODEV; + } + } + if (!ipv6_chk_addr(&src_info->ipi6_addr, dev)) { + if (dev) + dev_put(dev); + err = -EINVAL; + goto exit_f; } + if (dev) + dev_put(dev); + ipv6_addr_copy(&fl->fl6_src, &src_info->ipi6_addr); break; case IPV6_FLOWINFO: diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 78e92ecc02a7..4f61e8da6bd0 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -197,31 +197,44 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (sk->sk_state != TCP_CLOSE) goto out; - if (addr_type & IPV6_ADDR_LINKLOCAL) { - if (addr_len >= sizeof(struct sockaddr_in6) && - addr->sin6_scope_id) { - /* Override any existing binding, if another one - * is supplied by user. - */ - sk->sk_bound_dev_if = addr->sin6_scope_id; - } - - /* Binding to link-local address requires an interface */ - if (!sk->sk_bound_dev_if) - goto out; - } - /* Check if the address belongs to the host. */ if (addr_type != IPV6_ADDR_ANY) { + struct net_device *dev = NULL; + + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another + * one is supplied by user. + */ + sk->sk_bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (!sk->sk_bound_dev_if) + goto out; + + dev = dev_get_by_index(sk->sk_bound_dev_if); + if (!dev) { + err = -ENODEV; + goto out; + } + } + /* ipv4 addr of the socket is invalid. Only the * unpecified and mapped address have a v4 equivalent. */ v4addr = LOOPBACK4_IPV6; if (!(addr_type & IPV6_ADDR_MULTICAST)) { err = -EADDRNOTAVAIL; - if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) + if (!ipv6_chk_addr(&addr->sin6_addr, dev)) { + if (dev) + dev_put(dev); goto out; + } } + if (dev) + dev_put(dev); } inet->rcv_saddr = inet->saddr = v4addr; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4c5e714a7da7..c6dbf252502e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1974,6 +1974,7 @@ ctl_table ipv6_route_table[] = { .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, + { .ctl_name = 0 } }; #endif diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 80baea496a54..13c993e164ab 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -485,7 +485,8 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { .daddr = dst, .saddr = tiph->saddr, .tos = RT_TOS(tos) } }, - .oif = tunnel->parms.link }; + .oif = tunnel->parms.link, + .proto = IPPROTO_IPV6 }; if (ip_route_output_key(&rt, &fl)) { tunnel->stat.tx_carrier_errors++; goto tx_error_icmp; @@ -757,7 +758,8 @@ static int ipip6_tunnel_init(struct net_device *dev) { .daddr = iph->daddr, .saddr = iph->saddr, .tos = RT_TOS(iph->tos) } }, - .oif = tunnel->parms.link }; + .oif = tunnel->parms.link, + .proto = IPPROTO_IPV6 }; struct rtable *rt; if (!ip_route_output_key(&rt, &fl)) { tdev = rt->u.dst.dev; diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 86bc800c26e0..4acddaa7726d 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -1307,7 +1307,11 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, skb_reserve(skb, self->max_header_size + 16); asmptr = skb->h.raw = skb_put(skb, len); - memcpy_fromiovec(asmptr, msg->msg_iov, len); + err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + if (err) { + kfree_skb(skb); + return err; + } /* * Just send the message to TinyTP, and let it deal with possible @@ -1550,7 +1554,11 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock, IRDA_DEBUG(4, "%s(), appending user data\n", __FUNCTION__); asmptr = skb->h.raw = skb_put(skb, len); - memcpy_fromiovec(asmptr, msg->msg_iov, len); + err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + if (err) { + kfree_skb(skb); + return err; + } /* * Just send the message to TinyTP, and let it deal with possible @@ -1613,7 +1621,11 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, IRDA_DEBUG(4, "%s(), appending user data\n", __FUNCTION__); asmptr = skb->h.raw = skb_put(skb, len); - memcpy_fromiovec(asmptr, msg->msg_iov, len); + err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + if (err) { + kfree_skb(skb); + return err; + } err = irlmp_connless_data_request(self->lsap, skb); if (err) { diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 5777a4b529c2..f94b3f7e6124 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1101,7 +1101,12 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock, SOCK_DEBUG(sk, "NET/ROM: Appending user data\n"); /* User data follows immediately after the NET/ROM transport header */ - memcpy_fromiovec(asmptr, msg->msg_iov, len); + if (memcpy_fromiovec(asmptr, msg->msg_iov, len)) { + kfree_skb(skb); + err = -EFAULT; + goto out; + } + SOCK_DEBUG(sk, "NET/ROM: Transmitting buffer\n"); if (sk->sk_state != TCP_ESTABLISHED) { diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index d880534183ee..f5ec0c83d045 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1083,7 +1083,11 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock, asmptr = skb->h.raw = skb_put(skb, len); - memcpy_fromiovec(asmptr, msg->msg_iov, len); + err = memcpy_fromiovec(asmptr, msg->msg_iov, len); + if (err) { + kfree_skb(skb); + return err; + } /* * If the Q BIT Include socket option is in force, the first @@ -1133,8 +1137,10 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock, frontlen = skb_headroom(skb); while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, &err)) == NULL) + if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, &err)) == NULL) { + kfree_skb(skb); return err; + } skbn->sk = sk; skbn->free = 1; @@ -1159,7 +1165,7 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock, } skb->free = 1; - kfree_skb(skb, FREE_WRITE); + kfree_skb(skb); } else { skb_queue_tail(&sk->sk_write_queue, skb); /* Throw it on the queue */ } diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 44b30efb17a9..5c15d829abba 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -362,6 +362,14 @@ void sctp_association_free(struct sctp_association *asoc) asoc->eyecatcher = 0; + /* Free any cached ASCONF_ACK chunk. */ + if (asoc->addip_last_asconf_ack) + sctp_chunk_free(asoc->addip_last_asconf_ack); + + /* Free any cached ASCONF chunk. */ + if (asoc->addip_last_asconf) + sctp_chunk_free(asoc->addip_last_asconf); + sctp_association_put(asoc); } @@ -525,6 +533,45 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, return peer; } +/* Delete a transport address from an association. */ +void sctp_assoc_del_peer(struct sctp_association *asoc, + const union sctp_addr *addr) +{ + struct list_head *pos; + struct list_head *temp; + struct sctp_transport *peer = NULL; + struct sctp_transport *transport; + + list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + if (sctp_cmp_addr_exact(addr, &transport->ipaddr)) { + peer = transport; + list_del(pos); + break; + } + } + + /* The address we want delete is not in the association. */ + if (!peer) + return; + + /* Get the first transport of asoc. */ + pos = asoc->peer.transport_addr_list.next; + transport = list_entry(pos, struct sctp_transport, transports); + + /* Update any entries that match the peer to be deleted. */ + if (asoc->peer.primary_path == peer) + sctp_assoc_set_primary(asoc, transport); + if (asoc->peer.active_path == peer) + asoc->peer.active_path = transport; + if (asoc->peer.retran_path == peer) + asoc->peer.retran_path = transport; + if (asoc->peer.last_data_from == peer) + asoc->peer.last_data_from = transport; + + sctp_transport_free(peer); +} + /* Lookup a transport by address. */ struct sctp_transport *sctp_assoc_lookup_paddr( const struct sctp_association *asoc, diff --git a/net/sctp/input.c b/net/sctp/input.c index cc3e9314dd91..8392d7f83d61 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -124,16 +124,16 @@ int sctp_rcv(struct sk_buff *skb) /* Pull up the IP and SCTP headers. */ __skb_pull(skb, skb->h.raw - skb->data); if (skb->len < sizeof(struct sctphdr)) - goto bad_packet; + goto discard_it; if (sctp_rcv_checksum(skb) < 0) - goto bad_packet; + goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); family = ipver2af(skb->nh.iph->version); af = sctp_get_af_specific(family); if (unlikely(!af)) - goto bad_packet; + goto discard_it; /* Initialize local addresses for lookups. */ af->from_skb(&src, skb, 1); @@ -223,9 +223,6 @@ int sctp_rcv(struct sk_buff *skb) sock_put(sk); return ret; -bad_packet: - SCTP_INC_STATS(SctpChecksumErrors); - discard_it: kfree_skb(skb); return ret; diff --git a/net/sctp/output.c b/net/sctp/output.c index 262a6aa06b49..ecd0c6edc6c0 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -1,7 +1,7 @@ /* SCTP kernel reference Implementation + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. - * Copyright (c) 2001-2003 International Business Machines, Corp. * * This file is part of the SCTP kernel reference Implementation * @@ -350,7 +350,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) */ SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) { - if (sctp_chunk_is_data(chunk)) { if (!chunk->has_tsn) { diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index dc4cb1a5a739..14816b5f8335 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -150,7 +150,7 @@ static inline int sctp_cacc_skip_3_1(struct sctp_transport *primary, if (!primary->cacc.cycling_changeover) { if (sctp_cacc_skip_3_1_d(primary, transport, count_of_newacks)) return 1; - if (sctp_cacc_skip_3_1_f(transport, count_of_newacks)); + if (sctp_cacc_skip_3_1_f(transport, count_of_newacks)) return 1; return 0; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 986460afaa6b..21f862329e1e 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1115,6 +1115,9 @@ __init int sctp_init(void) "(established %d bind %d)\n", sctp_assoc_hashsize, sctp_port_hashsize); + /* Disable ADDIP by default. */ + sctp_addip_enable = 0; + sctp_sysctl_register(); INIT_LIST_HEAD(&sctp_address_families); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a340e8978154..3c4bfc535fe5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1441,6 +1441,7 @@ no_hmac: retval->next_tsn = retval->c.initial_tsn; retval->ctsn_ack_point = retval->next_tsn - 1; + retval->addip_serial = retval->c.initial_tsn; /* The INIT stuff will be done by the side effects. */ return retval; @@ -2035,7 +2036,7 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, if (!retval) return NULL; - asconf.serial = asoc->addip_serial++; + asconf.serial = htonl(asoc->addip_serial++); retval->subh.addip_hdr = sctp_addto_chunk(retval, sizeof(asconf), &asconf); @@ -2073,7 +2074,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, union sctp_addr *laddr, struct sockaddr *addrs, int addrcnt, - int flags) + __u16 flags) { sctp_addip_param_t param; struct sctp_chunk *retval; @@ -2112,7 +2113,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, addr_param_len = af->to_addr_param(addr, &addr_param); param.param_hdr.type = flags; param.param_hdr.length = htons(paramlen + addr_param_len); - param.crr_id = htonl(i); + param.crr_id = i; sctp_addto_chunk(retval, paramlen, ¶m); sctp_addto_chunk(retval, addr_param_len, &addr_param); @@ -2185,8 +2186,8 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, * * Create an ASCONF_ACK chunk with enough space for the parameter responses. */ -struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, - int serial, int vparam_len) +struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, + __u32 serial, int vparam_len) { sctp_addiphdr_t asconf; struct sctp_chunk *retval; @@ -2197,7 +2198,7 @@ struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, if (!retval) return NULL; - asconf.serial = serial; + asconf.serial = htonl(serial); retval->subh.addip_hdr = sctp_addto_chunk(retval, sizeof(asconf), &asconf); @@ -2205,10 +2206,407 @@ struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, return retval; } +/* Add response parameters to an ASCONF_ACK chunk. */ +static void sctp_add_asconf_response(struct sctp_chunk *chunk, __u32 crr_id, + __u16 err_code, sctp_addip_param_t *asconf_param) +{ + sctp_addip_param_t ack_param; + sctp_errhdr_t err_param; + int asconf_param_len = 0; + int err_param_len = 0; + __u16 response_type; + + if (SCTP_ERROR_NO_ERROR == err_code) { + response_type = SCTP_PARAM_SUCCESS_REPORT; + } else { + response_type = SCTP_PARAM_ERR_CAUSE; + err_param_len = sizeof(err_param); + if (asconf_param) + asconf_param_len = + ntohs(asconf_param->param_hdr.length); + } + + /* Add Success Indication or Error Cause Indication parameter. */ + ack_param.param_hdr.type = response_type; + ack_param.param_hdr.length = htons(sizeof(ack_param) + + err_param_len + + asconf_param_len); + ack_param.crr_id = crr_id; + sctp_addto_chunk(chunk, sizeof(ack_param), &ack_param); + + if (SCTP_ERROR_NO_ERROR == err_code) + return; + + /* Add Error Cause parameter. */ + err_param.cause = err_code; + err_param.length = htons(err_param_len + asconf_param_len); + sctp_addto_chunk(chunk, err_param_len, &err_param); + + /* Add the failed TLV copied from ASCONF chunk. */ + if (asconf_param) + sctp_addto_chunk(chunk, asconf_param_len, asconf_param); +} + +/* Process a asconf parameter. */ +static __u16 sctp_process_asconf_param(struct sctp_association *asoc, + struct sctp_chunk *asconf, + sctp_addip_param_t *asconf_param) +{ + struct sctp_transport *peer; + struct sctp_af *af; + union sctp_addr addr; + struct list_head *pos; + union sctp_addr_param *addr_param; + + addr_param = (union sctp_addr_param *) + ((void *)asconf_param + sizeof(sctp_addip_param_t)); + + af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); + if (unlikely(!af)) + return SCTP_ERROR_INV_PARAM; + + af->from_addr_param(&addr, addr_param, asoc->peer.port, 0); + switch (asconf_param->param_hdr.type) { + case SCTP_PARAM_ADD_IP: + /* ADDIP 4.3 D9) If an endpoint receives an ADD IP address + * request and does not have the local resources to add this + * new address to the association, it MUST return an Error + * Cause TLV set to the new error code 'Operation Refused + * Due to Resource Shortage'. + */ + + peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC); + if (!peer) + return SCTP_ERROR_RSRC_LOW; + + /* Start the heartbeat timer. */ + if (!mod_timer(&peer->hb_timer, sctp_transport_timeout(peer))) + sctp_transport_hold(peer); + break; + case SCTP_PARAM_DEL_IP: + /* ADDIP 4.3 D7) If a request is received to delete the + * last remaining IP address of a peer endpoint, the receiver + * MUST send an Error Cause TLV with the error cause set to the + * new error code 'Request to Delete Last Remaining IP Address'. + */ + pos = asoc->peer.transport_addr_list.next; + if (pos->next == &asoc->peer.transport_addr_list) + return SCTP_ERROR_DEL_LAST_IP; + + /* ADDIP 4.3 D8) If a request is received to delete an IP + * address which is also the source address of the IP packet + * which contained the ASCONF chunk, the receiver MUST reject + * this request. To reject the request the receiver MUST send + * an Error Cause TLV set to the new error code 'Request to + * Delete Source IP Address' + */ + if (sctp_cmp_addr_exact(sctp_source(asconf), &addr)) + return SCTP_ERROR_DEL_SRC_IP; + + sctp_assoc_del_peer(asoc, &addr); + break; + case SCTP_PARAM_SET_PRIMARY: + peer = sctp_assoc_lookup_paddr(asoc, &addr); + if (!peer) + return SCTP_ERROR_INV_PARAM; + + sctp_assoc_set_primary(asoc, peer); + break; + default: + return SCTP_ERROR_INV_PARAM; + break; + } + + return SCTP_ERROR_NO_ERROR; +} + +/* Process an incoming ASCONF chunk with the next expected serial no. and + * return an ASCONF_ACK chunk to be sent in response. + */ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, - struct sctp_chunk *asconf, - int vparam_len) + struct sctp_chunk *asconf) { - // FIXME: process asconf chunk - return NULL; + sctp_addiphdr_t *hdr; + union sctp_addr_param *addr_param; + sctp_addip_param_t *asconf_param; + struct sctp_chunk *asconf_ack; + + __u16 err_code; + int length = 0; + int chunk_len = asconf->skb->len; + __u32 serial; + int all_param_pass = 1; + + hdr = (sctp_addiphdr_t *)asconf->skb->data; + serial = ntohl(hdr->serial); + + /* Skip the addiphdr and store a pointer to address parameter. */ + length = sizeof(sctp_addiphdr_t); + addr_param = (union sctp_addr_param *)(asconf->skb->data + length); + chunk_len -= length; + + /* Skip the address parameter and store a pointer to the first + * asconf paramter. + */ + length = ntohs(addr_param->v4.param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)addr_param + length); + chunk_len -= length; + + /* create an ASCONF_ACK chunk. + * Based on the definitions of parameters, we know that the size of + * ASCONF_ACK parameters are less than or equal to the twice of ASCONF + * paramters. + */ + asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 2); + if (!asconf_ack) + goto done; + + /* Process the TLVs contained within the ASCONF chunk. */ + while (chunk_len > 0) { + err_code = sctp_process_asconf_param(asoc, asconf, + asconf_param); + /* ADDIP 4.1 A7) + * If an error response is received for a TLV parameter, + * all TLVs with no response before the failed TLV are + * considered successful if not reported. All TLVs after + * the failed response are considered unsuccessful unless + * a specific success indication is present for the parameter. + */ + if (SCTP_ERROR_NO_ERROR != err_code) + all_param_pass = 0; + + if (!all_param_pass) + sctp_add_asconf_response(asconf_ack, + asconf_param->crr_id, err_code, + asconf_param); + + /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add + * an IP address sends an 'Out of Resource' in its response, it + * MUST also fail any subsequent add or delete requests bundled + * in the ASCONF. + */ + if (SCTP_ERROR_RSRC_LOW == err_code) + goto done; + + /* Move to the next ASCONF param. */ + length = ntohs(asconf_param->param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)asconf_param + + length); + chunk_len -= length; + } + +done: + asoc->peer.addip_serial++; + + /* If we are sending a new ASCONF_ACK hold a reference to it in assoc + * after freeing the reference to old asconf ack if any. + */ + if (asconf_ack) { + if (asoc->addip_last_asconf_ack) + sctp_chunk_free(asoc->addip_last_asconf_ack); + + sctp_chunk_hold(asconf_ack); + asoc->addip_last_asconf_ack = asconf_ack; + } + + return asconf_ack; +} + +/* Process a asconf parameter that is successfully acked. */ +static int sctp_asconf_param_success(struct sctp_association *asoc, + sctp_addip_param_t *asconf_param) +{ + struct sctp_af *af; + union sctp_addr addr; + struct sctp_bind_addr *bp = &asoc->base.bind_addr; + union sctp_addr_param *addr_param; + int retval = 0; + + addr_param = (union sctp_addr_param *) + ((void *)asconf_param + sizeof(sctp_addip_param_t)); + + /* We have checked the packet before, so we do not check again. */ + af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); + af->from_addr_param(&addr, addr_param, bp->port, 0); + + switch (asconf_param->param_hdr.type) { + case SCTP_PARAM_ADD_IP: + sctp_local_bh_disable(); + sctp_write_lock(&asoc->base.addr_lock); + retval = sctp_add_bind_addr(bp, &addr, GFP_ATOMIC); + sctp_write_unlock(&asoc->base.addr_lock); + sctp_local_bh_enable(); + break; + case SCTP_PARAM_DEL_IP: + sctp_local_bh_disable(); + sctp_write_lock(&asoc->base.addr_lock); + retval = sctp_del_bind_addr(bp, &addr); + sctp_write_unlock(&asoc->base.addr_lock); + sctp_local_bh_enable(); + break; + default: + break; + } + + return retval; +} + +/* Get the corresponding ASCONF response error code from the ASCONF_ACK chunk + * for the given asconf parameter. If there is no response for this parameter, + * return the error code based on the third argument 'no_err'. + * ADDIP 4.1 + * A7) If an error response is received for a TLV parameter, all TLVs with no + * response before the failed TLV are considered successful if not reported. + * All TLVs after the failed response are considered unsuccessful unless a + * specific success indication is present for the parameter. + */ +static __u16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, + sctp_addip_param_t *asconf_param, + int no_err) +{ + sctp_addip_param_t *asconf_ack_param; + sctp_errhdr_t *err_param; + int length; + int asconf_ack_len = asconf_ack->skb->len; + __u16 err_code; + + if (no_err) + err_code = SCTP_ERROR_NO_ERROR; + else + err_code = SCTP_ERROR_REQ_REFUSED; + + /* Skip the addiphdr from the asconf_ack chunk and store a pointer to + * the first asconf_ack parameter. + */ + length = sizeof(sctp_addiphdr_t); + asconf_ack_param = (sctp_addip_param_t *)(asconf_ack->skb->data + + length); + asconf_ack_len -= length; + + while (asconf_ack_len > 0) { + if (asconf_ack_param->crr_id == asconf_param->crr_id) { + switch(asconf_ack_param->param_hdr.type) { + case SCTP_PARAM_SUCCESS_REPORT: + return SCTP_ERROR_NO_ERROR; + case SCTP_PARAM_ERR_CAUSE: + length = sizeof(sctp_addip_param_t); + err_param = (sctp_errhdr_t *) + ((void *)asconf_ack_param + length); + asconf_ack_len -= length; + if (asconf_ack_len > 0) + return err_param->cause; + else + return SCTP_ERROR_INV_PARAM; + break; + default: + return SCTP_ERROR_INV_PARAM; + } + } + + length = ntohs(asconf_ack_param->param_hdr.length); + asconf_ack_param = (sctp_addip_param_t *) + ((void *)asconf_ack_param + length); + asconf_ack_len -= length; + } + + return err_code; +} + +/* Process an incoming ASCONF_ACK chunk against the cached last ASCONF chunk. */ +int sctp_process_asconf_ack(struct sctp_association *asoc, + struct sctp_chunk *asconf_ack) +{ + struct sctp_chunk *asconf = asoc->addip_last_asconf; + union sctp_addr_param *addr_param; + sctp_addip_param_t *asconf_param; + int length = 0; + int asconf_len = asconf->skb->len; + int all_param_pass = 0; + int no_err = 1; + int retval = 0; + __u16 err_code = SCTP_ERROR_NO_ERROR; + + /* Skip the chunkhdr and addiphdr from the last asconf sent and store + * a pointer to address parameter. + */ + length = sizeof(sctp_addip_chunk_t); + addr_param = (union sctp_addr_param *)(asconf->skb->data + length); + asconf_len -= length; + + /* Skip the address parameter in the last asconf sent and store a + * pointer to the first asconf paramter. + */ + length = ntohs(addr_param->v4.param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)addr_param + length); + asconf_len -= length; + + /* ADDIP 4.1 + * A8) If there is no response(s) to specific TLV parameter(s), and no + * failures are indicated, then all request(s) are considered + * successful. + */ + if (asconf_ack->skb->len == sizeof(sctp_addiphdr_t)) + all_param_pass = 1; + + /* Process the TLVs contained in the last sent ASCONF chunk. */ + while (asconf_len > 0) { + if (all_param_pass) + err_code = SCTP_ERROR_NO_ERROR; + else { + err_code = sctp_get_asconf_response(asconf_ack, + asconf_param, + no_err); + if (no_err && (SCTP_ERROR_NO_ERROR != err_code)) + no_err = 0; + } + + switch (err_code) { + case SCTP_ERROR_NO_ERROR: + retval = sctp_asconf_param_success(asoc, asconf_param); + break; + + case SCTP_ERROR_RSRC_LOW: + retval = 1; + break; + + case SCTP_ERROR_INV_PARAM: + /* Disable sending this type of asconf parameter in + * future. + */ + asoc->peer.addip_disabled_mask |= + asconf_param->param_hdr.type; + break; + + case SCTP_ERROR_REQ_REFUSED: + case SCTP_ERROR_DEL_LAST_IP: + case SCTP_ERROR_DEL_SRC_IP: + default: + break; + } + + /* Skip the processed asconf parameter and move to the next + * one. + */ + length = ntohs(asconf_param->param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)asconf_param + + length); + asconf_len -= length; + } + + /* Free the cached last sent asconf chunk. */ + sctp_chunk_free(asconf); + asoc->addip_last_asconf = NULL; + + /* Send the next asconf chunk from the addip chunk queue. */ + asconf = (struct sctp_chunk *)__skb_dequeue(&asoc->addip_chunks); + if (asconf) { + /* Hold the chunk until an ASCONF_ACK is received. */ + sctp_chunk_hold(asconf); + if (sctp_primitive_ASCONF(asoc, asconf)) + sctp_chunk_free(asconf); + else + asoc->addip_last_asconf = asconf; + } + + return retval; } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index b2eeb5d93acd..3041eaa330c9 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1,5 +1,5 @@ /* SCTP kernel reference Implementation - * (C) Copyright IBM Corp. 2001, 2003 + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * @@ -663,10 +663,11 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, struct sock *sk = asoc->base.sk; /* If it is a non-temporary association belonging to a TCP-style - * listening socket, do not free it so that accept() can pick it - * up later. + * listening socket that is not closed, do not free it so that accept() + * can pick it up later. */ - if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING) && (!asoc->temp)) + if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING) && + (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK)) return; sctp_unhash_established(asoc); @@ -676,8 +677,8 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, /* * ADDIP Section 4.1 ASCONF Chunk Procedures * A4) Start a T-4 RTO timer, using the RTO value of the selected - * destination address (normally the primary path; see RFC2960 - * section 6.4 for details). + * destination address (we use active path instead of primary path just + * because primary path may be inactive. */ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, @@ -685,7 +686,7 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, { struct sctp_transport *t; - t = asoc->peer.primary_path; + t = asoc->peer.active_path; asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = t->rto; chunk->transport = t; } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 0777e8e539b0..37d73a01097e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1,5 +1,5 @@ /* SCTP kernel reference Implementation - * (C) Copyright IBM Corp. 2001, 2003 + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2002 Intel Corp. @@ -46,6 +46,7 @@ * Daisy Chang <daisyc@us.ibm.com> * Ardelle Fan <ardelle.fan@intel.com> * Ryan Layer <rmlayer@us.ibm.com> + * Kevin Gao <kevin.gao@intel.com> * * Any bugs reported given to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. @@ -3066,19 +3067,57 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands); } -/* - * ADDIP Section 4.2 Upon reception of an ASCONF Chunk - * When an endpoint receive an ASCONF Chunk from the remote peer - * special procedures MAY be needed to identify the association the - * ASCONF Chunk is associated with. To properly find the association - * the following procedures should be L1 to L4 and C1 to C5 - */ +/* ADDIP Section 4.2 Upon reception of an ASCONF Chunk. */ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, - sctp_cmd_seq_t *commands) + const struct sctp_association *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) { - // FIXME: Handle the ASCONF chunk + struct sctp_chunk *chunk = arg; + struct sctp_chunk *asconf_ack = NULL; + sctp_addiphdr_t *hdr; + __u32 serial; + + hdr = (sctp_addiphdr_t *)chunk->skb->data; + serial = ntohl(hdr->serial); + + /* ADDIP 4.2 C1) Compare the value of the serial number to the value + * the endpoint stored in a new association variable + * 'Peer-Serial-Number'. + */ + if (serial == asoc->peer.addip_serial + 1) { + /* ADDIP 4.2 C2) If the value found in the serial number is + * equal to the ('Peer-Serial-Number' + 1), the endpoint MUST + * do V1-V5. + */ + asconf_ack = sctp_process_asconf((struct sctp_association *) + asoc, chunk); + if (!asconf_ack) + return SCTP_DISPOSITION_NOMEM; + } else if (serial == asoc->peer.addip_serial) { + /* ADDIP 4.2 C3) If the value found in the serial number is + * equal to the value stored in the 'Peer-Serial-Number' + * IMPLEMENTATION NOTE: As an optimization a receiver may wish + * to save the last ASCONF-ACK for some predetermined period of * time and instead of re-processing the ASCONF (with the same + * serial number) it may just re-transmit the ASCONF-ACK. + */ + if (asoc->addip_last_asconf_ack) + asconf_ack = asoc->addip_last_asconf_ack; + else + return SCTP_DISPOSITION_DISCARD; + } else { + /* ADDIP 4.2 C4) Otherwise, the ASCONF Chunk is discarded since + * it must be either a stale packet or from an attacker. + */ + return SCTP_DISPOSITION_DISCARD; + } + + /* ADDIP 4.2 C5) In both cases C2 and C3 the ASCONF-ACK MUST be sent + * back to the source address contained in the IP header of the ASCONF + * being responded to. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(asconf_ack)); + return SCTP_DISPOSITION_CONSUME; } @@ -3088,12 +3127,81 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, * delete IP addresses the D0 to D13 rules should be applied: */ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, - sctp_cmd_seq_t *commands) + const struct sctp_association *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) { - // FIXME: Handle the ASCONF-ACK chunk - return SCTP_DISPOSITION_CONSUME; + struct sctp_chunk *asconf_ack = arg; + struct sctp_chunk *last_asconf = asoc->addip_last_asconf; + struct sctp_chunk *abort; + sctp_addiphdr_t *addip_hdr; + __u32 sent_serial, rcvd_serial; + + addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data; + rcvd_serial = ntohl(addip_hdr->serial); + + if (last_asconf) { + addip_hdr = (sctp_addiphdr_t *)last_asconf->subh.addip_hdr; + sent_serial = ntohl(addip_hdr->serial); + } else { + sent_serial = asoc->addip_serial - 1; + } + + /* D0) If an endpoint receives an ASCONF-ACK that is greater than or + * equal to the next serial number to be used but no ASCONF chunk is + * outstanding the endpoint MUST ABORT the association. Note that a + * sequence number is greater than if it is no more than 2^^31-1 + * larger than the current sequence number (using serial arithmetic). + */ + if (ADDIP_SERIAL_gte(rcvd_serial, sent_serial + 1) && + !(asoc->addip_last_asconf)) { + abort = sctp_make_abort(asoc, asconf_ack, + sizeof(sctp_errhdr_t)); + if (abort) { + sctp_init_cause(abort, SCTP_ERROR_ASCONF_ACK, NULL, 0); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(abort)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_ASCONF_ACK)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_ABORT; + } + + if ((rcvd_serial == sent_serial) && asoc->addip_last_asconf) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + + if (!sctp_process_asconf_ack((struct sctp_association *)asoc, + asconf_ack)) + return SCTP_DISPOSITION_CONSUME; + + abort = sctp_make_abort(asoc, asconf_ack, + sizeof(sctp_errhdr_t)); + if (abort) { + sctp_init_cause(abort, SCTP_ERROR_RSRC_LOW, NULL, 0); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(abort)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_ASCONF_ACK)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_ABORT; + } + + return SCTP_DISPOSITION_DISCARD; } /* @@ -4269,7 +4377,7 @@ nomem: /* * ADDIP Section 4.1 ASCONF CHunk Procedures - * If the T-4 RTO timer expires the endpoint should do B1 to B5 + * If the T4 RTO timer expires the endpoint should do B1 to B5 */ sctp_disposition_t sctp_sf_t4_timer_expire( const struct sctp_endpoint *ep, @@ -4278,7 +4386,55 @@ sctp_disposition_t sctp_sf_t4_timer_expire( void *arg, sctp_cmd_seq_t *commands) { - // FIXME: need to handle t4 expire + struct sctp_chunk *chunk = asoc->addip_last_asconf; + struct sctp_transport *transport = chunk->transport; + + /* ADDIP 4.1 B1) Increment the error counters and perform path failure + * detection on the appropriate destination address as defined in + * RFC2960 [5] section 8.1 and 8.2. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport)); + + /* Reconfig T4 timer and transport. */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T4, SCTP_CHUNK(chunk)); + + /* ADDIP 4.1 B2) Increment the association error counters and perform + * endpoint failure detection on the association as defined in + * RFC2960 [5] section 8.1 and 8.2. + * association error counter is incremented in SCTP_CMD_STRIKE. + */ + if (asoc->overall_error_count >= asoc->max_retrans) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + SCTP_INC_STATS(SctpAborteds); + SCTP_INC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_ABORT; + } + + /* ADDIP 4.1 B3) Back-off the destination address RTO value to which + * the ASCONF chunk was sent by doubling the RTO timer value. + * This is done in SCTP_CMD_STRIKE. + */ + + /* ADDIP 4.1 B4) Re-transmit the ASCONF Chunk last sent and if possible + * choose an alternate destination address (please refer to RFC2960 + * [5] section 6.4.1). An endpoint MUST NOT add new parameters to this + * chunk, it MUST be the same (including its serial number) as the last + * ASCONF sent. + */ + sctp_chunk_hold(asoc->addip_last_asconf); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(asoc->addip_last_asconf)); + + /* ADDIP 4.1 B5) Restart the T-4 RTO timer. Note that if a different + * destination is selected, then the RTO used will be that of the new + * destination address. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + return SCTP_DISPOSITION_CONSUME; } diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index c73c3a2ff9a2..224b8d2fe888 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -1,7 +1,7 @@ /* SCTP kernel reference Implementation + * (C) Copyright IBM Corp. 2001, 2003 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. - * Copyright (c) 2001-2003 International Business Machines, Corp. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Nokia, Inc. * @@ -921,13 +921,15 @@ const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, if (state > SCTP_STATE_MAX) return &bug; - if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) { + if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) return &chunk_event_table[cid][state]; - } - if (cid >= SCTP_CID_ADDIP_MIN && cid <= SCTP_CID_ADDIP_MAX) { - return &addip_chunk_event_table - [cid - SCTP_CID_ADDIP_MIN][state]; + if (sctp_addip_enable) { + if (cid == SCTP_CID_ASCONF) + return &addip_chunk_event_table[0][state]; + + if (cid == SCTP_CID_ASCONF_ACK) + return &addip_chunk_event_table[1][state]; } return &chunk_event_table_unknown[state]; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 574efdb0cf42..4f7cbfe0e36c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1,5 +1,5 @@ /* SCTP kernel reference Implementation - * (C) Copyright IBM Corp. 2001, 2003 + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2003 Intel Corp. @@ -100,6 +100,8 @@ static int sctp_bindx_add(struct sock *, struct sockaddr *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr *, int); static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int); static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int); +static int sctp_send_asconf(struct sctp_association *asoc, + struct sctp_chunk *chunk); static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_autobind(struct sock *sk); static void sctp_sock_migrate(struct sock *, struct sock *, @@ -150,10 +152,14 @@ struct sctp_transport *sctp_addr_id2transport(struct sock *sk, { struct sctp_association *addr_asoc = NULL, *id_asoc = NULL; struct sctp_transport *transport; + union sctp_addr *laddr = (union sctp_addr *)addr; + laddr->v4.sin_port = ntohs(laddr->v4.sin_port); addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep, (union sctp_addr *)addr, &transport); + laddr->v4.sin_port = htons(laddr->v4.sin_port); + if (!addr_asoc) return NULL; @@ -300,6 +306,41 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) return ret; } + /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks + * + * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged + * at any one time. If a sender, after sending an ASCONF chunk, decides + * it needs to transfer another ASCONF Chunk, it MUST wait until the + * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a + * subsequent ASCONF. Note this restriction binds each side, so at any + * time two ASCONF may be in-transit on any given association (one sent + * from each endpoint). + */ +static int sctp_send_asconf(struct sctp_association *asoc, + struct sctp_chunk *chunk) +{ + int retval = 0; + + /* If there is an outstanding ASCONF chunk, queue it for later + * transmission. + */ + if (asoc->addip_last_asconf) { + __skb_queue_tail(&asoc->addip_chunks, (struct sk_buff *)chunk); + goto out; + } + + /* Hold the chunk until an ASCONF_ACK is received. */ + sctp_chunk_hold(chunk); + retval = sctp_primitive_ASCONF(asoc, chunk); + if (retval) + sctp_chunk_free(chunk); + else + asoc->addip_last_asconf = chunk; + +out: + return retval; +} + /* Add a list of addresses as bind addresses to local endpoint or * association. * @@ -380,6 +421,9 @@ static int sctp_send_asconf_add_ip(struct sock *sk, int i; int retval = 0; + if (!sctp_addip_enable) + return retval; + sp = sctp_sk(sk); ep = sp->ep; @@ -389,12 +433,15 @@ static int sctp_send_asconf_add_ip(struct sock *sk, list_for_each(pos, &ep->asocs) { asoc = list_entry(pos, struct sctp_association, asocs); - if (!sctp_state(asoc, ESTABLISHED)) + if (!asoc->peer.asconf_capable) continue; - if (!asoc->peer.asconf_capable) + if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP) continue; - + + if (!sctp_state(asoc, ESTABLISHED)) + continue; + /* Check if any address in the packed array of addresses is * in the bind address list of the association. If so, * do not send the asconf chunk to its peer, but continue with @@ -409,9 +456,9 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - if (sctp_assoc_lookup_laddr(asoc, addr)) + if (sctp_assoc_lookup_laddr(asoc, addr)) break; - + addr_buf += af->sockaddr_len; } if (i < addrcnt) @@ -433,14 +480,10 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - retval = sctp_primitive_ASCONF(asoc, chunk); - if (retval) { - sctp_chunk_free(chunk); - goto out; - } + retval = sctp_send_asconf(asoc, chunk); - /* FIXME: After sending the add address ASCONF chunk, we - * cannot append the address to the association's binding + /* FIXME: After sending the add address ASCONF chunk, we + * cannot append the address to the association's binding * address list, because the new address may be used as the * source of a message sent to the peer before the ASCONF * chunk is received by the peer. So we should wait until @@ -565,6 +608,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, int i; int retval = 0; + if (!sctp_addip_enable) + return retval; + sp = sctp_sk(sk); ep = sp->ep; @@ -574,10 +620,13 @@ static int sctp_send_asconf_del_ip(struct sock *sk, list_for_each(pos, &ep->asocs) { asoc = list_entry(pos, struct sctp_association, asocs); - if (!sctp_state(asoc, ESTABLISHED)) + if (!asoc->peer.asconf_capable) continue; - if (!asoc->peer.asconf_capable) + if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP) + continue; + + if (!sctp_state(asoc, ESTABLISHED)) continue; /* Check if any address in the packed array of addresses is @@ -594,9 +643,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, goto out; } - if (!sctp_assoc_lookup_laddr(asoc, laddr)) + if (!sctp_assoc_lookup_laddr(asoc, laddr)) break; - + addr_buf += af->sockaddr_len; } if (i < addrcnt) @@ -611,27 +660,23 @@ static int sctp_send_asconf_del_ip(struct sock *sk, bp = &asoc->base.bind_addr; laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, addrcnt, sp); - sctp_read_unlock(&asoc->base.addr_lock); + sctp_read_unlock(&asoc->base.addr_lock); if (!laddr) continue; - chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, + chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, SCTP_PARAM_DEL_IP); if (!chunk) { retval = -ENOMEM; goto out; } - retval = sctp_primitive_ASCONF(asoc, chunk); - if (retval) { - sctp_chunk_free(chunk); - goto out; - } + retval = sctp_send_asconf(asoc, chunk); /* FIXME: After sending the delete address ASCONF chunk, we * cannot remove the addresses from the association's bind * address list, because there maybe some packet send to - * the delete addresses, so we should wait until ASCONF_ACK + * the delete addresses, so we should wait until ASCONF_ACK * packet is received. */ } @@ -1920,6 +1965,9 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, sp = sctp_sk(sk); ep = sp->ep; + if (!sctp_addip_enable) + return -EPERM; + if (optlen != sizeof(struct sctp_setpeerprim)) return -EINVAL; @@ -1930,6 +1978,12 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, if (!asoc) return -EINVAL; + if (!asoc->peer.asconf_capable) + return -EPERM; + + if (asoc->peer.addip_disabled_mask & SCTP_PARAM_SET_PRIMARY) + return -EPERM; + if (!sctp_state(asoc, ESTABLISHED)) return -ENOTCONN; @@ -1942,15 +1996,11 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, if (!chunk) return -ENOMEM; - err = sctp_primitive_ASCONF(asoc, chunk); - if (err) { - sctp_chunk_free(chunk); - return err; - } + err = sctp_send_asconf(asoc, chunk); SCTP_DEBUG_PRINTK("We set peer primary addr primitively.\n"); - return 0; + return err; } @@ -2962,9 +3012,13 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len, if (!asoc->peer.primary_path) return -ENOTCONN; - + + asoc->peer.primary_path->ipaddr.v4.sin_port = + htons(asoc->peer.primary_path->ipaddr.v4.sin_port); memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr, sizeof(union sctp_addr)); + asoc->peer.primary_path->ipaddr.v4.sin_port = + ntohs(asoc->peer.primary_path->ipaddr.v4.sin_port); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, (union sctp_addr *)&prim.ssp_addr); diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 417c62061621..15c5f3bfecbf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -162,6 +162,14 @@ static ctl_table sctp_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, + { + .ctl_name = NET_SCTP_ADDIP_ENABLE, + .procname = "addip_enable", + .data = &sctp_addip_enable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .ctl_name = 0 } }; diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c index 9dd1231ae26f..b65a32b1c61f 100644 --- a/net/sunrpc/sysctl.c +++ b/net/sunrpc/sysctl.c @@ -81,7 +81,8 @@ proc_dodebug(ctl_table *table, int write, struct file *file, if (left > sizeof(tmpbuf) - 1) return -EINVAL; - copy_from_user(tmpbuf, p, left); + if (copy_from_user(tmpbuf, p, left)) + return -EFAULT; tmpbuf[left] = '\0'; for (p = tmpbuf, value = 0; '0' <= *p && *p <= '9'; p++, left--) @@ -101,9 +102,11 @@ proc_dodebug(ctl_table *table, int write, struct file *file, len = sprintf(tmpbuf, "%d", *(unsigned int *) table->data); if (len > left) len = left; - __copy_to_user(buffer, tmpbuf, len); + if (__copy_to_user(buffer, tmpbuf, len)) + return -EFAULT; if ((left -= len) > 0) { - put_user('\n', (char *)buffer + len); + if (put_user('\n', (char *)buffer + len)) + return -EFAULT; left--; } } |
