summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.osdl.org>2004-01-19 07:39:54 -0800
committerLinus Torvalds <torvalds@home.osdl.org>2004-01-19 07:39:54 -0800
commit3940ed85276e69985e25ad687cc7dcb952e5d8a5 (patch)
tree8273ec807ddcdde1811a820c1e1d405489197117
parent4d53a003f12e93c181dff573ea866a912e758b7c (diff)
parent4705cae7950a8eec85a9370ef42a1037212e9069 (diff)
Merge bk://kernel.bkbits.net/davem/net-2.6
into home.osdl.org:/home/torvalds/v2.5/linux
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--include/linux/sctp.h3
-rw-r--r--include/linux/sysctl.h3
-rw-r--r--include/net/neighbour.h2
-rw-r--r--include/net/sctp/constants.h4
-rw-r--r--include/net/sctp/sctp.h6
-rw-r--r--include/net/sctp/sm.h27
-rw-r--r--include/net/sctp/structs.h11
-rw-r--r--kernel/sysctl.c327
-rw-r--r--net/ax25/af_ax25.c7
-rw-r--r--net/bridge/br_netfilter.c2
-rw-r--r--net/core/dst.c2
-rw-r--r--net/core/neighbour.c9
-rw-r--r--net/core/pktgen.c16
-rw-r--r--net/ipv4/Kconfig2
-rw-r--r--net/ipv6/addrconf.c5
-rw-r--r--net/ipv6/af_inet6.c86
-rw-r--r--net/ipv6/datagram.c30
-rw-r--r--net/ipv6/raw.c43
-rw-r--r--net/ipv6/route.c1
-rw-r--r--net/ipv6/sit.c6
-rw-r--r--net/irda/af_irda.c18
-rw-r--r--net/netrom/af_netrom.c7
-rw-r--r--net/rose/af_rose.c12
-rw-r--r--net/sctp/associola.c47
-rw-r--r--net/sctp/input.c9
-rw-r--r--net/sctp/output.c3
-rw-r--r--net/sctp/outqueue.c2
-rw-r--r--net/sctp/protocol.c3
-rw-r--r--net/sctp/sm_make_chunk.c418
-rw-r--r--net/sctp/sm_sideeffect.c15
-rw-r--r--net/sctp/sm_statefuns.c194
-rw-r--r--net/sctp/sm_statetable.c14
-rw-r--r--net/sctp/socket.c118
-rw-r--r--net/sctp/sysctl.c8
-rw-r--r--net/sunrpc/sysctl.c9
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, &param);
}
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, &param);
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--;
}
}