summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-12-09 23:38:17 -0800
committerSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-12-09 23:38:17 -0800
commitd12add250df948e7e24068a62ba729daf4073ace (patch)
tree68dc47fbc0e5e8181216ecd0c46d3711dfc38b0f
parent8b4ad80b8236a20584f7956ff42c7a3bdf1d018e (diff)
parent8051ff93f063e1fc5808c72d5f49087f8c2eefe5 (diff)
Merge dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/linux-2.5.51
into dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/lksctp-2.5.51
-rw-r--r--include/linux/sysctl.h3
-rw-r--r--include/net/sctp/sctp.h4
-rw-r--r--include/net/sctp/sm.h13
-rw-r--r--include/net/sctp/structs.h26
-rw-r--r--net/sctp/associola.c7
-rw-r--r--net/sctp/bind_addr.c4
-rw-r--r--net/sctp/input.c9
-rw-r--r--net/sctp/ipv6.c188
-rw-r--r--net/sctp/protocol.c150
-rw-r--r--net/sctp/sm_make_chunk.c60
-rw-r--r--net/sctp/sm_sideeffect.c28
-rw-r--r--net/sctp/sm_statefuns.c194
-rw-r--r--net/sctp/sm_statetable.c2
-rw-r--r--net/sctp/socket.c346
-rw-r--r--net/sctp/sysctl.c5
-rw-r--r--net/sctp/transport.c2
16 files changed, 608 insertions, 433 deletions
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index e70fc2ef0856..47d74c442191 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -543,7 +543,8 @@ enum {
NET_SCTP_PATH_MAX_RETRANS = 8,
NET_SCTP_MAX_INIT_RETRANSMITS = 9,
NET_SCTP_HB_INTERVAL = 10,
- NET_SCTP_MAX_BURST = 11,
+ NET_SCTP_PRESERVE_ENABLE = 11,
+ NET_SCTP_MAX_BURST = 12,
};
/* CTL_PROC names: */
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 73c948813e72..f12e54cd4919 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -123,8 +123,8 @@ extern sctp_protocol_t sctp_proto;
extern struct sock *sctp_get_ctl_sock(void);
extern int sctp_copy_local_addr_list(sctp_protocol_t *, sctp_bind_addr_t *,
sctp_scope_t, int priority, int flags);
-extern sctp_pf_t *sctp_get_pf_specific(int family);
-extern void sctp_set_pf_specific(int family, sctp_pf_t *);
+extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
+extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
/*
* sctp_socket.c
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index e811bf37e4ca..68af144ef56c 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -140,6 +140,8 @@ sctp_state_fn_t sctp_sf_do_5_2_2_dupinit;
sctp_state_fn_t sctp_sf_do_5_2_4_dupcook;
sctp_state_fn_t sctp_sf_unk_chunk;
sctp_state_fn_t sctp_sf_do_8_5_1_E_sa;
+sctp_state_fn_t sctp_sf_cookie_echoed_err;
+sctp_state_fn_t sctp_sf_do_5_2_6_stale;
/* Prototypes for primitive event state functions. */
sctp_state_fn_t sctp_sf_do_prm_asoc;
@@ -175,7 +177,6 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire;
*/
/* Prototypes for chunk state functions. Not in use. */
-sctp_state_fn_t sctp_sf_do_5_2_6_stale;
sctp_state_fn_t sctp_sf_do_9_2_reshutack;
sctp_state_fn_t sctp_sf_do_9_2_reshut;
sctp_state_fn_t sctp_sf_do_9_2_shutack;
@@ -211,7 +212,7 @@ void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag);
/* Prototypes for chunk-building functions. */
sctp_chunk_t *sctp_make_init(const sctp_association_t *,
const sctp_bind_addr_t *,
- int priority);
+ int priority, int vparam_len);
sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *,
const sctp_chunk_t *,
const int priority,
@@ -322,9 +323,15 @@ sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *,
const __u8 *, int addrs_len);
sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *,
const sctp_association_t *,
- sctp_chunk_t *, int priority, int *err);
+ sctp_chunk_t *, int priority, int *err,
+ sctp_chunk_t **err_chk_p);
int sctp_addip_addr_config(sctp_association_t *, sctp_param_t,
struct sockaddr_storage*, int);
+void sctp_send_stale_cookie_err(const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_chunk_t *chunk,
+ sctp_cmd_seq_t *commands,
+ sctp_chunk_t *err_chunk);
/* 3rd level prototypes */
__u32 sctp_generate_tag(const sctp_endpoint_t *);
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index d7b5674bdbdc..fc4a3f1a3b4c 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -42,6 +42,7 @@
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
* Dajiang Zhang <dajiang.zhang@nokia.com>
+ * Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
@@ -182,6 +183,9 @@ struct SCTP_protocol {
/* Valid.Cookie.Life - 60 seconds */
int valid_cookie_life;
+
+ /* Whether Cookie Preservative is enabled(1) or not(0) */
+ int cookie_preserve_enable;
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
@@ -234,7 +238,7 @@ struct SCTP_protocol {
* Pointers to address related SCTP functions.
* (i.e. things that depend on the address family.)
*/
-typedef struct sctp_func {
+struct sctp_af {
int (*queue_xmit) (struct sk_buff *skb);
int (*setsockopt) (struct sock *sk,
int level,
@@ -259,27 +263,34 @@ typedef struct sctp_func {
void (*from_skb) (union sctp_addr *,
struct sk_buff *skb,
int saddr);
+ void (*from_sk) (union sctp_addr *,
+ struct sock *sk);
+ void (*to_sk) (union sctp_addr *,
+ struct sock *sk);
int (*addr_valid) (union sctp_addr *);
sctp_scope_t (*scope) (union sctp_addr *);
void (*inaddr_any) (union sctp_addr *, unsigned short);
int (*is_any) (const union sctp_addr *);
+ int (*available) (const union sctp_addr *);
__u16 net_header_len;
int sockaddr_len;
sa_family_t sa_family;
struct list_head list;
-} sctp_func_t;
+};
-sctp_func_t *sctp_get_af_specific(sa_family_t);
+struct sctp_af *sctp_get_af_specific(sa_family_t);
+int sctp_register_af(struct sctp_af *);
/* Protocol family functions. */
typedef struct sctp_pf {
void (*event_msgname)(sctp_ulpevent_t *, char *, int *);
- void (*skb_msgname)(struct sk_buff *, char *, int *);
- int (*af_supported)(sa_family_t);
+ void (*skb_msgname) (struct sk_buff *, char *, int *);
+ int (*af_supported) (sa_family_t);
int (*cmp_addr) (const union sctp_addr *,
const union sctp_addr *,
struct sctp_opt *);
- struct sctp_func *af;
+ int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
+ struct sctp_af *af;
} sctp_pf_t;
/* SCTP Socket type: UDP or TCP style. */
@@ -623,7 +634,7 @@ struct SCTP_transport {
union sctp_addr ipaddr;
/* These are the functions we call to handle LLP stuff. */
- sctp_func_t *af_specific;
+ struct sctp_af *af_specific;
/* Which association do we belong to? */
sctp_association_t *asoc;
@@ -1271,7 +1282,6 @@ struct SCTP_association {
/* The cookie life I award for any cookie. */
struct timeval cookie_life;
- __u32 cookie_preserve;
/* Overall : The overall association error count.
* Error Count : [Clear this any time I get something.]
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 7736677f2d22..e61cf97204f3 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -128,8 +128,9 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc->state_timestamp = jiffies;
/* Set things that have constant value. */
- asoc->cookie_life.tv_sec = SCTP_DEFAULT_COOKIE_LIFE_SEC;
- asoc->cookie_life.tv_usec = SCTP_DEFAULT_COOKIE_LIFE_USEC;
+ asoc->cookie_life.tv_sec = sctp_proto.valid_cookie_life / HZ;
+ asoc->cookie_life.tv_usec = (sctp_proto.valid_cookie_life % HZ) *
+ 1000000L / HZ;
asoc->pmtu = 0;
asoc->frag_point = 0;
@@ -642,7 +643,7 @@ __u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid)
int sctp_cmp_addr_exact(const union sctp_addr *ss1,
const union sctp_addr *ss2)
{
- struct sctp_func *af;
+ struct sctp_af *af;
af = sctp_get_af_specific(ss1->sa.sa_family);
if (!af)
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index 7a78fdb5d7ff..2ae655f2c775 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -327,7 +327,7 @@ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr,
/* Is this a wildcard address? */
int sctp_is_any(const union sctp_addr *addr)
{
- struct sctp_func *af = sctp_get_af_specific(addr->sa.sa_family);
+ struct sctp_af *af = sctp_get_af_specific(addr->sa.sa_family);
if (!af)
return 0;
return af->is_any(addr);
@@ -362,7 +362,7 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
/* What is the scope of 'addr'? */
sctp_scope_t sctp_scope(const union sctp_addr *addr)
{
- struct sctp_func *af;
+ struct sctp_af *af;
af = sctp_get_af_specific(addr->sa.sa_family);
if (!af)
diff --git a/net/sctp/input.c b/net/sctp/input.c
index cf9e54b99283..d6e64da75733 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -42,6 +42,7 @@
* Hui Huang <hui.huang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
+ * Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
@@ -96,7 +97,7 @@ int sctp_rcv(struct sk_buff *skb)
struct sctphdr *sh;
union sctp_addr src;
union sctp_addr dest;
- struct sctp_func *af;
+ struct sctp_af *af;
int ret = 0;
if (skb->pkt_type!=PACKET_HOST)
@@ -279,6 +280,7 @@ int sctp_rcv_ootb(struct sk_buff *skb)
{
sctp_chunkhdr_t *ch;
__u8 *ch_end;
+ sctp_errhdr_t *err;
ch = (sctp_chunkhdr_t *) skb->data;
@@ -308,8 +310,9 @@ int sctp_rcv_ootb(struct sk_buff *skb)
goto discard;
if (ch->type == SCTP_CID_ERROR) {
- /* FIXME - Need to check the "Stale cookie" ERROR. */
- goto discard;
+ err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t));
+ if (SCTP_ERROR_STALE_COOKIE == err->cause)
+ goto discard;
}
ch = (sctp_chunkhdr_t *) ch_end;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 1391167e9709..6c48dcc3ef50 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -76,8 +76,19 @@
#include <asm/uaccess.h>
-/* FIXME: Cleanup so we don't need TEST_FRAME here. */
-#ifndef TEST_FRAME
+extern struct notifier_block sctp_inetaddr_notifier;
+
+/* FIXME: This macro needs to be moved to a common header file. */
+#define NIP6(addr) \
+ ntohs((addr)->s6_addr16[0]), \
+ ntohs((addr)->s6_addr16[1]), \
+ ntohs((addr)->s6_addr16[2]), \
+ ntohs((addr)->s6_addr16[3]), \
+ ntohs((addr)->s6_addr16[4]), \
+ ntohs((addr)->s6_addr16[5]), \
+ ntohs((addr)->s6_addr16[6]), \
+ ntohs((addr)->s6_addr16[7])
+
/* FIXME: Comments. */
static inline void sctp_v6_err(struct sk_buff *skb,
struct inet6_skb_parm *opt,
@@ -92,13 +103,38 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
struct sock *sk = skb->sk;
struct ipv6_pinfo *np = inet6_sk(sk);
struct flowi fl;
- struct dst_entry *dst;
+ struct dst_entry *dst = skb->dst;
+ struct rt6_info *rt6 = (struct rt6_info *)dst;
struct in6_addr saddr;
- int err = 0;
+ int err;
fl.proto = sk->protocol;
- fl.fl6_dst = &np->daddr;
- fl.fl6_src = NULL;
+ fl.fl6_dst = &rt6->rt6i_dst.addr;
+
+ /* FIXME: Currently, ip6_route_output() doesn't fill in the source
+ * address in the returned route entry. So we call ipv6_get_saddr()
+ * to get an appropriate source address. It is possible that this address
+ * may not be part of the bind address list of the association.
+ * Once ip6_route_ouput() is fixed so that it returns a route entry
+ * with an appropriate source address, the following if condition can
+ * be removed. With ip6_route_output() returning a source address filled
+ * route entry, sctp_transport_route() can do real source address
+ * selection for v6.
+ */
+ if (ipv6_addr_any(&rt6->rt6i_src.addr)) {
+ err = ipv6_get_saddr(dst, fl.fl6_dst, &saddr);
+
+ if (err) {
+ printk(KERN_ERR "%s: No saddr available for "
+ "DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ __FUNCTION__, NIP6(fl.fl6_src));
+ return err;
+ }
+
+ fl.fl6_src = &saddr;
+ } else {
+ fl.fl6_src = &rt6->rt6i_src.addr;
+ }
fl.fl6_flowlabel = np->flow_label;
IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
@@ -111,63 +147,8 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
fl.nl_u.ip6_u.daddr = rt0->addr;
}
- dst = __sk_dst_check(sk, np->dst_cookie);
-
- if (dst == NULL) {
- dst = ip6_route_output(sk, &fl);
-
- if (dst->error) {
- sk->err_soft = -dst->error;
- dst_release(dst);
- return -sk->err_soft;
- }
- ip6_dst_store(sk, dst, NULL);
- }
-
- skb->dst = dst_clone(dst);
-
- /* FIXME: This is all temporary until real source address
- * selection is done.
- */
- if (ipv6_addr_any(&np->saddr)) {
- err = ipv6_get_saddr(dst, fl.fl6_dst, &saddr);
-
- if (err)
- printk(KERN_ERR "sctp_v6_xmit: no saddr available\n");
-
- /* FIXME: This is a workaround until we get
- * real source address selection done. This is here
- * to disallow loopback when the scoping rules have
- * not bound loopback to the endpoint.
- */
- if (sctp_ipv6_addr_type(&saddr) & IPV6_ADDR_LOOPBACK) {
- if (!(sctp_ipv6_addr_type(&np->daddr) &
- IPV6_ADDR_LOOPBACK)) {
- ipv6_addr_copy(&saddr, &np->daddr);
- }
- }
- fl.fl6_src = &saddr;
- } else {
- fl.fl6_src = &np->saddr;
- }
-
- /* Restore final destination back after routing done */
- fl.nl_u.ip6_u.daddr = &np->daddr;
-
return ip6_xmit(sk, skb, &fl, np->opt);
}
-#endif /* TEST_FRAME */
-
-/* FIXME: This macro needs to be moved to a common header file. */
-#define NIP6(addr) \
- ntohs((addr)->s6_addr16[0]), \
- ntohs((addr)->s6_addr16[1]), \
- ntohs((addr)->s6_addr16[2]), \
- ntohs((addr)->s6_addr16[3]), \
- ntohs((addr)->s6_addr16[4]), \
- ntohs((addr)->s6_addr16[5]), \
- ntohs((addr)->s6_addr16[6]), \
- ntohs((addr)->s6_addr16[7])
/* Returns the dst cache entry for the given source and destination ip
* addresses.
@@ -176,7 +157,7 @@ struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr,
union sctp_addr *saddr)
{
struct dst_entry *dst;
- struct flowi fl = {
+ struct flowi fl = {
.nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } };
@@ -261,6 +242,20 @@ static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb,
ipv6_addr_copy(&addr->v6.sin6_addr, from);
}
+/* Initialize an sctp_addr from a socket. */
+static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)
+{
+ addr->v6.sin6_family = AF_INET6;
+ addr->v6.sin6_port = inet_sk(sk)->num;
+ addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr;
+}
+
+/* Initialize sk->rcv_saddr from sctp_addr. */
+static void sctp_v6_to_sk(union sctp_addr *addr, struct sock *sk)
+{
+ inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr;
+}
+
/* Initialize a sctp_addr from a dst_entry. */
static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst)
{
@@ -270,15 +265,15 @@ static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst)
}
/* Compare addresses exactly. Well.. almost exactly; ignore scope_id
- * for now. FIXME.
+ * for now. FIXME: v4-mapped-v6.
*/
-static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
+static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2)
{
int match;
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
- match = !ipv6_addr_cmp((struct in6_addr *)&addr1->v6.sin6_addr,
+ match = !ipv6_addr_cmp((struct in6_addr *)&addr1->v6.sin6_addr,
(struct in6_addr *)&addr2->v6.sin6_addr);
return match;
@@ -300,6 +295,22 @@ static int sctp_v6_is_any(const union sctp_addr *addr)
return IPV6_ADDR_ANY == type;
}
+/* Should this be available for binding? */
+static int sctp_v6_available(const union sctp_addr *addr)
+{
+ int type;
+ struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr;
+
+ type = ipv6_addr_type(in6);
+ if (IPV6_ADDR_ANY == type)
+ return 1;
+ if (!(type & IPV6_ADDR_UNICAST))
+ return 0;
+
+ return ipv6_chk_addr(in6, NULL);
+}
+
+
/* This function checks if the address is a valid address to be used for
* SCTP.
*
@@ -309,7 +320,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr)
*/
static int sctp_v6_addr_valid(union sctp_addr *addr)
{
- int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr);
+ int ret = ipv6_addr_type(&addr->v6.sin6_addr);
/* FIXME: v4-mapped-v6 address support. */
@@ -442,14 +453,14 @@ static int sctp_inet6_af_supported(sa_family_t family)
/* Address matching with wildcards allowed. This extra level
* of indirection lets us choose whether a PF_INET6 should
- * disallow any v4 addresses if we so choose.
+ * disallow any v4 addresses if we so choose.
*/
-static int sctp_inet6_cmp_addr(const union sctp_addr *addr1,
+static int sctp_inet6_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2,
struct sctp_opt *opt)
{
- struct sctp_func *af1, *af2;
-
+ struct sctp_af *af1, *af2;
+
af1 = sctp_get_af_specific(addr1->sa.sa_family);
af2 = sctp_get_af_specific(addr2->sa.sa_family);
@@ -461,11 +472,25 @@ static int sctp_inet6_cmp_addr(const union sctp_addr *addr1,
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
-
+
return af1->cmp_addr(addr1, addr2);
}
+/* Verify that the provided sockaddr looks bindable. Common verification,
+ * has already been taken care of.
+ */
+static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
+{
+ struct sctp_af *af;
+
+ /* ASSERT: address family has already been verified. */
+ if (addr->sa.sa_family != AF_INET6) {
+ af = sctp_get_af_specific(addr->sa.sa_family);
+ } else
+ af = opt->pf->af;
+ return af->available(addr);
+}
static struct proto_ops inet6_seqpacket_ops = {
.family = PF_INET6,
@@ -501,29 +526,33 @@ static struct inet6_protocol sctpv6_protocol = {
.err_handler = sctp_v6_err,
};
-static sctp_func_t sctp_ipv6_specific = {
+static struct sctp_af sctp_ipv6_specific = {
.queue_xmit = sctp_v6_xmit,
- .setsockopt = ipv6_setsockopt,
+ .setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
.get_dst = sctp_v6_get_dst,
.copy_addrlist = sctp_v6_copy_addrlist,
.from_skb = sctp_v6_from_skb,
+ .from_sk = sctp_v6_from_sk,
+ .to_sk = sctp_v6_to_sk,
.dst_saddr = sctp_v6_dst_saddr,
.cmp_addr = sctp_v6_cmp_addr,
.scope = sctp_v6_scope,
.addr_valid = sctp_v6_addr_valid,
.inaddr_any = sctp_v6_inaddr_any,
.is_any = sctp_v6_is_any,
+ .available = sctp_v6_available,
.net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6,
};
-static sctp_pf_t sctp_pf_inet6_specific = {
+static struct sctp_pf sctp_pf_inet6_specific = {
.event_msgname = sctp_inet6_event_msgname,
.skb_msgname = sctp_inet6_skb_msgname,
.af_supported = sctp_inet6_af_supported,
.cmp_addr = sctp_inet6_cmp_addr,
+ .bind_verify = sctp_inet6_bind_verify,
.af = &sctp_ipv6_specific,
};
@@ -538,11 +567,13 @@ int sctp_v6_init(void)
inet6_register_protosw(&sctpv6_protosw);
/* Register the SCTP specfic PF_INET6 functions. */
- sctp_set_pf_specific(PF_INET6, &sctp_pf_inet6_specific);
+ sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6);
+
+ /* Register the SCTP specfic AF_INET6 functions. */
+ sctp_register_af(&sctp_ipv6_specific);
- /* Fill in address family info. */
- INIT_LIST_HEAD(&sctp_ipv6_specific.list);
- list_add_tail(&sctp_ipv6_specific.list, &sctp_proto.address_families);
+ /* Register notifier for inet6 address additions/deletions. */
+ register_inet6addr_notifier(&sctp_inetaddr_notifier);
return 0;
}
@@ -553,4 +584,5 @@ void sctp_v6_exit(void)
list_del(&sctp_ipv6_specific.list);
inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP);
inet6_unregister_protosw(&sctpv6_protosw);
+ unregister_inet6addr_notifier(&sctp_inetaddr_notifier);
}
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index cc45fe85c46d..fde475e70359 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -40,6 +40,7 @@
* Jon Grimm <jgrimm@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
+ * Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
@@ -67,8 +68,10 @@ struct sctp_mib sctp_statistics[NR_CPUS * 2];
*/
static struct socket *sctp_ctl_socket;
-static sctp_pf_t *sctp_pf_inet6_specific;
-static sctp_pf_t *sctp_pf_inet_specific;
+static struct sctp_pf *sctp_pf_inet6_specific;
+static struct sctp_pf *sctp_pf_inet_specific;
+static struct sctp_af *sctp_af_v4_specific;
+static struct sctp_af *sctp_af_v6_specific;
extern struct net_proto_family inet_family_ops;
@@ -140,12 +143,12 @@ static void __sctp_get_local_addr_list(sctp_protocol_t *proto)
{
struct net_device *dev;
struct list_head *pos;
- struct sctp_func *af;
+ struct sctp_af *af;
read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
list_for_each(pos, &proto->address_families) {
- af = list_entry(pos, sctp_func_t, list);
+ af = list_entry(pos, struct sctp_af, list);
af->copy_addrlist(&proto->local_addr_list, dev);
}
}
@@ -251,7 +254,6 @@ struct dst_entry *sctp_v4_get_dst(union sctp_addr *daddr,
return &rt->u.dst;
}
-
/* Initialize a sctp_addr from in incoming skb. */
static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
int is_saddr)
@@ -274,6 +276,21 @@ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr));
}
+/* Initialize an sctp_addr from a socket. */
+static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk)
+{
+ addr->v4.sin_family = AF_INET;
+ addr->v4.sin_port = inet_sk(sk)->num;
+ addr->v4.sin_addr.s_addr = inet_sk(sk)->rcv_saddr;
+}
+
+/* Initialize sk->rcv_saddr from sctp_addr. */
+static void sctp_v4_to_sk(union sctp_addr *addr, struct sock *sk)
+{
+ inet_sk(sk)->rcv_saddr = addr->v4.sin_addr.s_addr;
+}
+
+
/* Initialize a sctp_addr from a dst_entry. */
static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst)
{
@@ -311,7 +328,7 @@ static int sctp_v4_is_any(const union sctp_addr *addr)
}
/* This function checks if the address is a valid address to be used for
- * SCTP.
+ * SCTP binding.
*
* Output:
* Return 0 - If the address is a non-unicast or an illegal address.
@@ -326,6 +343,18 @@ static int sctp_v4_addr_valid(union sctp_addr *addr)
return 1;
}
+/* Should this be available for binding? */
+static int sctp_v4_available(const union sctp_addr *addr)
+{
+ int ret = inet_addr_type(addr->v4.sin_addr.s_addr);
+
+ /* FIXME: ip_nonlocal_bind sysctl support. */
+
+ if (addr->v4.sin_addr.s_addr != INADDR_ANY && ret != RTN_LOCAL)
+ return 0;
+ return 1;
+}
+
/* Checking the loopback, private and other address scopes as defined in
* RFC 1918. The IPv4 scoping is based on the draft for SCTP IPv4
* scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
@@ -365,11 +394,11 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
return retval;
}
-/* Event handler for inet device events.
+/* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list.
*/
-static int sctp_netdev_event(struct notifier_block *this, unsigned long event,
- void *ptr)
+static int sctp_inetaddr_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
{
long flags __attribute__ ((unused));
@@ -405,29 +434,42 @@ int sctp_ctl_sock_init(void)
return 0;
}
+/* Register address family specific functions. */
+int sctp_register_af(struct sctp_af *af)
+{
+ switch (af->sa_family) {
+ case AF_INET:
+ if (sctp_af_v4_specific)
+ return 0;
+ sctp_af_v4_specific = af;
+ break;
+ case AF_INET6:
+ if (sctp_af_v6_specific)
+ return 0;
+ sctp_af_v6_specific = af;
+ break;
+ default:
+ return 0;
+ }
+
+ INIT_LIST_HEAD(&af->list);
+ list_add_tail(&af->list, &sctp_proto.address_families);
+ return 1;
+}
+
/* Get the table of functions for manipulating a particular address
* family.
*/
-sctp_func_t *sctp_get_af_specific(sa_family_t family)
+struct sctp_af *sctp_get_af_specific(sa_family_t family)
{
- struct list_head *pos;
- sctp_protocol_t *proto = sctp_get_protocol();
- struct sctp_func *retval, *af;
-
- retval = NULL;
-
- /* Cycle through all AF specific functions looking for a
- * match.
- */
- list_for_each(pos, &proto->address_families) {
- af = list_entry(pos, sctp_func_t, list);
- if (family == af->sa_family) {
- retval = af;
- break;
- }
+ switch (family) {
+ case AF_INET:
+ return sctp_af_v4_specific;
+ case AF_INET6:
+ return sctp_af_v6_specific;
+ default:
+ return NULL;
}
-
- return retval;
}
/* Common code to initialize a AF_INET msg_name. */
@@ -495,21 +537,28 @@ static int sctp_inet_cmp_addr(const union sctp_addr *addr1,
return 0;
}
+/* Verify that provided sockaddr looks bindable. Common verification has
+ * already been taken care of.
+ */
+static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
+{
+ return sctp_v4_available(addr);
+}
-struct sctp_func sctp_ipv4_specific;
+struct sctp_af sctp_ipv4_specific;
-static sctp_pf_t sctp_pf_inet = {
+static struct sctp_pf sctp_pf_inet = {
.event_msgname = sctp_inet_event_msgname,
.skb_msgname = sctp_inet_skb_msgname,
.af_supported = sctp_inet_af_supported,
.cmp_addr = sctp_inet_cmp_addr,
+ .bind_verify = sctp_inet_bind_verify,
.af = &sctp_ipv4_specific,
};
-
-/* Registration for netdev events. */
-struct notifier_block sctp_netdev_notifier = {
- .notifier_call = sctp_netdev_event,
+/* Notifier for inetaddr addition/deletion events. */
+struct notifier_block sctp_inetaddr_notifier = {
+ .notifier_call = sctp_inetaddr_event,
};
/* Socket operations. */
@@ -551,25 +600,28 @@ static struct inet_protocol sctp_protocol = {
};
/* IPv4 address related functions. */
-struct sctp_func sctp_ipv4_specific = {
+struct sctp_af sctp_ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
.get_dst = sctp_v4_get_dst,
.copy_addrlist = sctp_v4_copy_addrlist,
.from_skb = sctp_v4_from_skb,
+ .from_sk = sctp_v4_from_sk,
+ .to_sk = sctp_v4_to_sk,
.dst_saddr = sctp_v4_dst_saddr,
.cmp_addr = sctp_v4_cmp_addr,
.addr_valid = sctp_v4_addr_valid,
.inaddr_any = sctp_v4_inaddr_any,
.is_any = sctp_v4_is_any,
+ .available = sctp_v4_available,
.scope = sctp_v4_scope,
.net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET,
};
-sctp_pf_t *sctp_get_pf_specific(int family) {
+struct sctp_pf *sctp_get_pf_specific(sa_family_t family) {
switch (family) {
case PF_INET:
@@ -581,20 +633,24 @@ sctp_pf_t *sctp_get_pf_specific(int family) {
}
}
-/* Set the PF specific function table. */
-void sctp_set_pf_specific(int family, sctp_pf_t *pf)
+/* Register the PF specific function table. */
+int sctp_register_pf(struct sctp_pf *pf, sa_family_t family)
{
switch (family) {
case PF_INET:
+ if (sctp_pf_inet_specific)
+ return 0;
sctp_pf_inet_specific = pf;
break;
case PF_INET6:
+ if (sctp_pf_inet6_specific)
+ return 0;
sctp_pf_inet6_specific = pf;
break;
default:
- BUG();
- break;
+ return 0;
}
+ return 1;
}
/* Initialize the universe into something sensible. */
@@ -617,7 +673,7 @@ int sctp_init(void)
sctp_dbg_objcnt_init();
/* Initialize the SCTP specific PF functions. */
- sctp_set_pf_specific(PF_INET, &sctp_pf_inet);
+ sctp_register_pf(&sctp_pf_inet, PF_INET);
/*
* 14. Suggested SCTP Protocol Parameter Values
*/
@@ -636,6 +692,9 @@ int sctp_init(void)
/* Valid.Cookie.Life - 60 seconds */
sctp_proto.valid_cookie_life = 60 * HZ;
+ /* Whether Cookie Preservative is enabled(1) or not(0) */
+ sctp_proto.cookie_preserve_enable = 1;
+
/* Max.Burst - 4 */
sctp_proto.max_burst = SCTP_MAX_BURST;
@@ -709,8 +768,7 @@ int sctp_init(void)
sctp_sysctl_register();
INIT_LIST_HEAD(&sctp_proto.address_families);
- INIT_LIST_HEAD(&sctp_ipv4_specific.list);
- list_add_tail(&sctp_ipv4_specific.list, &sctp_proto.address_families);
+ sctp_register_af(&sctp_ipv4_specific);
status = sctp_v6_init();
if (status)
@@ -727,7 +785,9 @@ int sctp_init(void)
INIT_LIST_HEAD(&sctp_proto.local_addr_list);
sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED;
- register_inetaddr_notifier(&sctp_netdev_notifier);
+ /* Register notifier for inet address additions/deletions. */
+ register_inetaddr_notifier(&sctp_inetaddr_notifier);
+
sctp_get_local_addr_list(&sctp_proto);
return 0;
@@ -757,8 +817,10 @@ void sctp_exit(void)
* up all the remaining associations and all that memory.
*/
+ /* Unregister notifier for inet address additions/deletions. */
+ unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
+
/* Free the local address list. */
- unregister_inetaddr_notifier(&sctp_netdev_notifier);
sctp_free_local_addr_list(&sctp_proto);
/* Free the control endpoint. */
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 288f4ec6edb4..0681874c23a9 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -77,13 +77,18 @@ static const sctp_supported_addrs_param_t sat_param = {
{
SCTP_PARAM_SUPPORTED_ADDRESS_TYPES,
__constant_htons(SCTP_SAT_LEN),
- },
- { /* types[] */
- SCTP_PARAM_IPV4_ADDRESS,
- SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,)
}
};
+/* gcc 3.2 doesn't allow initialization of zero-length arrays. So the above
+ * structure is split and the address types array is initialized using a
+ * fixed length array.
+ */
+static const __u16 sat_addr_types[2] = {
+ SCTP_PARAM_IPV4_ADDRESS,
+ SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,)
+};
+
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 2: The ECN capable field is reserved for future use of
@@ -163,7 +168,7 @@ void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code,
*/
sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
const sctp_bind_addr_t *bp,
- int priority)
+ int priority, int vparam_len)
{
sctp_inithdr_t init;
union sctp_params addrs;
@@ -192,6 +197,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN;
chunksize += sizeof(ecap_param);
+ chunksize += vparam_len;
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
@@ -213,7 +219,10 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
sctp_addto_chunk(retval, sizeof(init), &init);
retval->param_hdr.v =
sctp_addto_chunk(retval, addrs_len, addrs.v);
- sctp_addto_chunk(retval, SCTP_SAT_LEN, &sat_param);
+
+ sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &sat_param);
+ sctp_addto_chunk(retval, sizeof(sat_addr_types), sat_addr_types);
+
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
nodata:
@@ -1337,7 +1346,7 @@ nodata:
sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
sctp_chunk_t *chunk, int priority,
- int *error)
+ int *error, sctp_chunk_t **err_chk_p)
{
sctp_association_t *retval = NULL;
sctp_signed_cookie_t *cookie;
@@ -1394,7 +1403,29 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* for init collision case of lost COOKIE ACK.
*/
if (!asoc && tv_lt(bear_cookie->expiration, chunk->skb->stamp)) {
- *error = -SCTP_IERROR_STALE_COOKIE;
+ /*
+ * Section 3.3.10.3 Stale Cookie Error (3)
+ *
+ * Cause of error
+ * ---------------
+ * Stale Cookie Error: Indicates the receipt of a valid State
+ * Cookie that has expired.
+ */
+ *err_chk_p = sctp_make_op_error_space(asoc, chunk,
+ ntohs(chunk->chunk_hdr->length));
+ if (*err_chk_p) {
+ suseconds_t usecs = (chunk->skb->stamp.tv_sec -
+ bear_cookie->expiration.tv_sec) * 1000000L +
+ chunk->skb->stamp.tv_usec -
+ bear_cookie->expiration.tv_usec;
+
+ usecs = htonl(usecs);
+ sctp_init_cause(*err_chk_p, SCTP_ERROR_STALE_COOKIE,
+ &usecs, sizeof(usecs));
+ *error = -SCTP_IERROR_STALE_COOKIE;
+ } else
+ *error = -SCTP_IERROR_NOMEM;
+
goto fail;
}
@@ -1751,6 +1782,7 @@ int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
__u16 sat;
int retval = 1;
sctp_scope_t scope;
+ time_t stale;
/* We maintain all INIT parameters in network byte order all the
* time. This allows us to not worry about whether the parameters
@@ -1770,8 +1802,16 @@ int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
break;
case SCTP_PARAM_COOKIE_PRESERVATIVE:
- asoc->cookie_preserve =
- ntohl(param.life->lifespan_increment);
+ if (!sctp_proto.cookie_preserve_enable)
+ break;
+
+ stale = ntohl(param.life->lifespan_increment);
+
+ /* Suggested Cookie Life span increment's unit is msec,
+ * (1/1000sec).
+ */
+ asoc->cookie_life.tv_sec += stale / 1000;
+ asoc->cookie_life.tv_usec += (stale % 1000) * 1000;
break;
case SCTP_PARAM_HOST_NAME_ADDRESS:
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 69ca9dccc364..a979d06c4825 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -68,7 +68,8 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport);
static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *asoc);
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *asoc,
- sctp_event_t event_type, sctp_chunk_t *chunk);
+ sctp_event_t event_type, sctp_subtype_t stype,
+ sctp_chunk_t *chunk);
static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init,
@@ -517,7 +518,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_ASSOC_FAILED:
sctp_cmd_assoc_failed(commands, asoc, event_type,
- chunk);
+ subtype, chunk);
break;
case SCTP_CMD_COUNTER_INC:
@@ -1046,18 +1047,27 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_association_t *asoc,
sctp_event_t event_type,
+ sctp_subtype_t subtype,
sctp_chunk_t *chunk)
{
sctp_ulpevent_t *event;
__u16 error = 0;
- if (event_type == SCTP_EVENT_T_PRIMITIVE)
- error = SCTP_ERROR_USER_ABORT;
-
- if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) &&
- (ntohs(chunk->chunk_hdr->length) >= (sizeof(struct sctp_chunkhdr) +
- sizeof(struct sctp_errhdr)))) {
- error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
+ switch(event_type) {
+ case SCTP_EVENT_T_PRIMITIVE:
+ if (SCTP_PRIMITIVE_ABORT == subtype.primitive)
+ error = SCTP_ERROR_USER_ABORT;
+ break;
+ case SCTP_EVENT_T_CHUNK:
+ if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) &&
+ (ntohs(chunk->chunk_hdr->length) >=
+ (sizeof(struct sctp_chunkhdr) +
+ sizeof(struct sctp_errhdr)))) {
+ error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
+ }
+ break;
+ default:
+ break;
}
event = sctp_ulpevent_make_assoc_change(asoc,
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 6825e088ed7e..0ef43f61a9c8 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -2,6 +2,7 @@
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines, Corp.
+ * Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2002 Nokia Corp.
*
* This file is part of the SCTP kernel reference Implementation
@@ -502,6 +503,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
sctp_chunk_t *repl;
sctp_ulpevent_t *ev;
int error = 0;
+ sctp_chunk_t *err_chk_p;
/* If the packet is an OOTB packet which is temporarily on the
* control endpoint, responding with an ABORT.
@@ -521,7 +523,8 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
* "Z" will reply with a COOKIE ACK chunk after building a TCB
* and moving to the ESTABLISHED state.
*/
- new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error);
+ new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error,
+ &err_chk_p);
/* FIXME:
* If the re-build failed, what is the proper error path
@@ -537,6 +540,11 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
case -SCTP_IERROR_NOMEM:
goto nomem;
+ case -SCTP_IERROR_STALE_COOKIE:
+ sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
+ err_chk_p);
+ return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
case -SCTP_IERROR_BAD_SIG:
default:
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -862,8 +870,8 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
/* Check if the timestamp looks valid. */
if (time_after(hbinfo->sent_at, jiffies) ||
time_after(jiffies, hbinfo->sent_at + max_interval)) {
- SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp
- received for transport: %p\n",
+ SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp"
+ "received for transport: %p\n",
__FUNCTION__, link);
return SCTP_DISPOSITION_DISCARD;
}
@@ -1562,6 +1570,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
sctp_association_t *new_asoc;
int error = 0;
char action;
+ sctp_chunk_t *err_chk_p;
/* "Decode" the chunk. We have no optional parameters so we
* are in good shape.
@@ -1575,7 +1584,8 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
* current association, consider the State Cookie valid even if
* the lifespan is exceeded.
*/
- new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error);
+ new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error,
+ &err_chk_p);
/* FIXME:
* If the re-build failed, what is the proper error path
@@ -1591,6 +1601,12 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
case -SCTP_IERROR_NOMEM:
goto nomem;
+ case -SCTP_IERROR_STALE_COOKIE:
+ sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
+ err_chk_p);
+ return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+
+ break;
case -SCTP_IERROR_BAD_SIG:
default:
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -1706,7 +1722,47 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort(const sctp_endpoint_t *ep,
return sctp_sf_shutdown_sent_abort(ep, asoc, type, arg, commands);
}
-#if 0
+/*
+ * Handle an Error received in COOKIE_ECHOED state.
+ *
+ * Only handle the error type of stale COOKIE Error, the other errors will
+ * be ignored.
+ *
+ * Inputs
+ * (endpoint, asoc, chunk)
+ *
+ * Outputs
+ * (asoc, reply_msg, msg_up, timers, counters)
+ *
+ * The return value is the disposition of the chunk.
+ */
+sctp_disposition_t sctp_sf_cookie_echoed_err(const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_subtype_t type,
+ void *arg,
+ sctp_cmd_seq_t *commands)
+{
+ sctp_chunk_t *chunk = arg;
+ sctp_errhdr_t *err;
+
+ /* If we have gotten too many failures, give up. */
+ if (1 + asoc->counters[SCTP_COUNTER_INIT_ERROR] >
+ asoc->max_init_attempts) {
+ /* INIT_FAILED will issue an ulpevent. */
+ sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
+ return SCTP_DISPOSITION_DELETE_TCB;
+ }
+ err = (sctp_errhdr_t *)(chunk->skb->data);
+
+ /* Process the error here */
+ switch (err->cause) {
+ case SCTP_ERROR_STALE_COOKIE:
+ return sctp_sf_do_5_2_6_stale(ep, asoc, type, arg, commands);
+ default:
+ return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+ }
+}
+
/*
* Handle a Stale COOKIE Error
*
@@ -1732,47 +1788,30 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort(const sctp_endpoint_t *ep,
*
* The return value is the disposition of the chunk.
*/
-sctp_disposition_t do_5_2_6_stale(const sctp_endpoint_t *ep,
- const sctp_association_t *asoc,
- const sctp_subtype_t type,
- void *arg,
- sctp_cmd_seq_t *commands)
+sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_subtype_t type,
+ void *arg,
+ sctp_cmd_seq_t *commands)
{
sctp_chunk_t *chunk = arg;
+ time_t stale;
+ sctp_cookie_preserve_param_t bht;
+ sctp_errhdr_t *err;
+ struct list_head *pos;
+ sctp_transport_t *t;
+ sctp_chunk_t *reply;
+ sctp_bind_addr_t *bp;
+ int attempts;
- /* This is not a real chunk type. It is a subtype of the
- * ERROR chunk type. The ERROR chunk processing will bring us
- * here.
- */
- sctp_chunk_t *in_packet;
- stp_chunk_t *reply;
- sctp_inithdr_t initack;
- __u8 *addrs;
- int addrs_len;
- time_t rtt;
- struct sctpCookiePreserve bht;
+ attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1;
- /* If we have gotten too many failures, give up. */
- if (1 + asoc->counters[SctpCounterInits] > asoc->max_init_attempts) {
- /* FIXME: Move to new ulpevent. */
- retval->event_up = sctp_make_ulp_init_timeout(asoc);
- if (!retval->event_up)
- goto nomem;
- sctp_add_cmd_sf(retval->commands, SCTP_CMD_DELETE_TCB,
- SCTP_NULL());
+ if (attempts >= asoc->max_init_attempts) {
+ sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL());
return SCTP_DISPOSITION_DELETE_TCB;
}
- retval->counters[0] = SCTP_COUNTER_INCR;
- retval->counters[0] = SctpCounterInits;
- retval->counters[1] = 0;
- retval->counters[1] = 0;
-
- /* Calculate the RTT in ms. */
- /* BUG--we should get the send time of the HEARTBEAT REQUEST. */
- in_packet = chunk;
- rtt = 1000 * timeval_sub(in_packet->skb->stamp,
- asoc->c.state_timestamp);
+ err = (sctp_errhdr_t *)(chunk->skb->data);
/* When calculating the time extension, an implementation
* SHOULD use the RTT information measured based on the
@@ -1780,28 +1819,48 @@ sctp_disposition_t do_5_2_6_stale(const sctp_endpoint_t *ep,
* more than 1 second beyond the measured RTT, due to long
* State Cookie lifetimes making the endpoint more subject to
* a replay attack.
+ * Measure of Staleness's unit is usec. (1/1000000 sec)
+ * Suggested Cookie Life-span Increment's unit is msec.
+ * (1/1000 sec)
+ * In general, if you use the suggested cookie life, the value
+ * found in the field of measure of staleness should be doubled
+ * to give ample time to retransmit the new cookie and thus
+ * yield a higher probability of success on the reattempt.
*/
- bht.p = {SCTP_COOKIE_PRESERVE, 8};
- bht.extraTime = htonl(rtt + 1000);
+ stale = ntohl(*(suseconds_t *)((u8 *)err + sizeof(sctp_errhdr_t)));
+ stale = stale << 1 / 1000;
- initack.init_tag = htonl(asoc->c.my_vtag);
- initack.a_rwnd = htonl(atomic_read(&asoc->rnwd));
- initack.num_outbound_streams = htons(asoc->streamoutcnt);
- initack.num_inbound_streams = htons(asoc->streamincnt);
- initack.initial_tsn = htonl(asoc->c.initSeqNumber);
-
- sctp_get_my_addrs(asoc, &addrs, &addrs_len);
+ bht.param_hdr.type = SCTP_PARAM_COOKIE_PRESERVATIVE;
+ bht.param_hdr.length = htons(sizeof(bht));
+ bht.lifespan_increment = htonl(stale);
/* Build that new INIT chunk. */
- reply = sctp_make_chunk(SCTP_INITIATION, 0,
- sizeof(initack)
- + sizeof(bht)
- + addrs_len);
+ bp = (sctp_bind_addr_t *) &asoc->base.bind_addr;
+ reply = sctp_make_init(asoc, bp, GFP_ATOMIC, sizeof(bht));
if (!reply)
goto nomem;
- sctp_addto_chunk(reply, sizeof(initack), &initack);
+
sctp_addto_chunk(reply, sizeof(bht), &bht);
- sctp_addto_chunk(reply, addrs_len, addrs);
+
+ /* Cast away the const modifier, as we want to just
+ * rerun it through as a sideffect.
+ */
+ sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC,
+ SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
+
+ /* If we've sent any data bundled with COOKIE-ECHO we need to resend. */
+ list_for_each(pos, &asoc->peer.transport_addr_list) {
+ t = list_entry(pos, sctp_transport_t, transports);
+ sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(t));
+ }
+
+ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
+ SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
+ sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+ SCTP_STATE(SCTP_STATE_COOKIE_WAIT));
+ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
+ SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
return SCTP_DISPOSITION_CONSUME;
@@ -1809,7 +1868,6 @@ sctp_disposition_t do_5_2_6_stale(const sctp_endpoint_t *ep,
nomem:
return SCTP_DISPOSITION_NOMEM;
}
-#endif /* 0 */
/*
* Process an ABORT.
@@ -3220,7 +3278,7 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
* 1 to 4294967295 (see 5.3.1 for Tag value selection). ...
*/
- repl = sctp_make_init(asoc, bp, GFP_ATOMIC);
+ repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0);
if (!repl)
goto nomem;
@@ -3992,7 +4050,7 @@ sctp_disposition_t sctp_sf_t1_timer_expire(const sctp_endpoint_t *ep,
switch (timer) {
case SCTP_EVENT_TIMEOUT_T1_INIT:
bp = (sctp_bind_addr_t *) &asoc->base.bind_addr;
- repl = sctp_make_init(asoc, bp, GFP_ATOMIC);
+ repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0);
break;
case SCTP_EVENT_TIMEOUT_T1_COOKIE:
@@ -4334,3 +4392,25 @@ void sctp_ootb_pkt_free(sctp_packet_t *packet)
sctp_transport_free(packet->transport);
sctp_packet_free(packet);
}
+
+/* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */
+void sctp_send_stale_cookie_err(const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_chunk_t *chunk,
+ sctp_cmd_seq_t *commands,
+ sctp_chunk_t *err_chunk)
+{
+ sctp_packet_t *packet;
+
+ if (err_chunk) {
+ packet = sctp_ootb_pkt_new(asoc, chunk);
+ if (packet) {
+ /* Set the skb to the belonging sock for accounting. */
+ err_chunk->skb->sk = ep->base.sk;
+ sctp_packet_append_chunk(packet, err_chunk);
+ sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+ SCTP_PACKET(packet));
+ } else
+ sctp_free_chunk (err_chunk);
+ }
+}
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index e0744f1f463a..fc603bba8cda 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -295,7 +295,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_cookie_echoed_err, .name = "sctp_sf_cookie_echoed_err"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 54e06e12395c..fe1244e22449 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -87,12 +87,7 @@ static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p);
-static inline void sctp_sk_addr_set(struct sock *,
- const union sctp_addr *newaddr,
- union sctp_addr *saveaddr);
-static inline void sctp_sk_addr_restore(struct sock *,
- const union sctp_addr *);
-static inline int sctp_verify_addr(struct sock *, struct sockaddr *, int);
+static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int);
static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
static int sctp_do_bind(struct sock *, union sctp_addr *, int);
@@ -133,101 +128,75 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
return retval;
}
-static long sctp_get_port_local(struct sock *, unsigned short);
+static long sctp_get_port_local(struct sock *, union sctp_addr *);
-/* Bind a local address either to an endpoint or to an association. */
-SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *newaddr,
- int addr_len)
+/* Verify this is a valid sockaddr. */
+static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
+ union sctp_addr *addr, int len)
{
- sctp_opt_t *sp = sctp_sk(sk);
- sctp_endpoint_t *ep = sp->ep;
- sctp_bind_addr_t *bp = &ep->base.bind_addr;
- unsigned short sa_family = newaddr->sa.sa_family;
- union sctp_addr tmpaddr, saveaddr;
- unsigned short *snum;
- int ret = 0;
+ struct sctp_af *af;
- SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, addr_len: %d)\n",
- sk, newaddr, addr_len);
-
- /* FIXME: This function needs to handle v4-mapped-on-v6
- * addresses!
- */
- if (PF_INET == sk->family) {
- if (sa_family != AF_INET)
- return -EINVAL;
- }
+ /* Check minimum size. */
+ if (len < sizeof (struct sockaddr))
+ return NULL;
- /* Make a local copy of the new address. */
- tmpaddr = *newaddr;
+ /* Does this PF support this AF? */
+ if (!opt->pf->af_supported(addr->sa.sa_family))
+ return NULL;
- switch (sa_family) {
- case AF_INET:
- if (addr_len < sizeof(struct sockaddr_in))
- return -EINVAL;
+ /* If we get this far, af is valid. */
+ af = sctp_get_af_specific(addr->sa.sa_family);
- ret = inet_addr_type(newaddr->v4.sin_addr.s_addr);
+ if (len < af->sockaddr_len)
+ return NULL;
- /* FIXME:
- * Should we allow apps to bind to non-local addresses by
- * checking the IP sysctl parameter "ip_nonlocal_bind"?
- */
- if (newaddr->v4.sin_addr.s_addr != INADDR_ANY &&
- ret != RTN_LOCAL)
- return -EADDRNOTAVAIL;
+ return af;
+}
- tmpaddr.v4.sin_port = htons(tmpaddr.v4.sin_port);
- snum = &tmpaddr.v4.sin_port;
- break;
- case AF_INET6:
- SCTP_V6(
- /* FIXME: Hui, please verify this. Looking at
- * the ipv6 code I see a SIN6_LEN_RFC2133 check.
- * I'm guessing that scope_id is a newer addition.
- */
- if (addr_len < sizeof(struct sockaddr_in6))
- return -EINVAL;
+/* Bind a local address either to an endpoint or to an association. */
+SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
+{
+ sctp_opt_t *sp = sctp_sk(sk);
+ sctp_endpoint_t *ep = sp->ep;
+ sctp_bind_addr_t *bp = &ep->base.bind_addr;
+ struct sctp_af *af;
+ unsigned short snum;
+ int ret = 0;
- /* FIXME - The support for IPv6 multiple types
- * of addresses need to be added later.
- */
- ret = sctp_ipv6_addr_type(&newaddr->v6.sin6_addr);
- tmpaddr.v6.sin6_port = htons(tmpaddr.v6.sin6_port);
- snum = &tmpaddr.v6.sin6_port;
- break;
- )
+ SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d)\n",
+ sk, addr, len);
- default:
+ /* Common sockaddr verification. */
+ af = sctp_sockaddr_af(sp, addr, len);
+ if (!af)
return -EINVAL;
- };
+
+ /* PF specific bind() address verification. */
+ if (!sp->pf->bind_verify(sp, addr))
+ return -EADDRNOTAVAIL;
+
+ snum= ntohs(addr->v4.sin_port);
SCTP_DEBUG_PRINTK("sctp_do_bind: port: %d, new port: %d\n",
- bp->port, *snum);
+ bp->port, snum);
/* We must either be unbound, or bind to the same port. */
- if (bp->port && (*snum != bp->port)) {
+ if (bp->port && (snum != bp->port)) {
SCTP_DEBUG_PRINTK("sctp_do_bind:"
" New port %d does not match existing port "
- "%d.\n", *snum, bp->port);
+ "%d.\n", snum, bp->port);
return -EINVAL;
}
- if (*snum && *snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+ if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
- /* FIXME - Make socket understand that there might be multiple bind
- * addresses and there will be multiple source addresses involved in
- * routing and failover decisions.
- */
- sctp_sk_addr_set(sk, &tmpaddr, &saveaddr);
-
/* Make sure we are allowed to bind here.
* The function sctp_get_port_local() does duplicate address
* detection.
*/
- if ((ret = sctp_get_port_local(sk, *snum))) {
- sctp_sk_addr_restore(sk, &saveaddr);
+ if ((ret = sctp_get_port_local(sk, addr))) {
if (ret == (long) sk) {
/* This endpoint has a conflicting address. */
return -EINVAL;
@@ -237,25 +206,32 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *newaddr,
}
/* Refresh ephemeral port. */
- if (!*snum)
- *snum = inet_sk(sk)->num;
+ if (!snum)
+ snum = inet_sk(sk)->num;
+
+
- /* The getsockname() API depends on 'sport' being set. */
- inet_sk(sk)->sport = htons(inet_sk(sk)->num);
/* Add the address to the bind address list. */
sctp_local_bh_disable();
sctp_write_lock(&ep->base.addr_lock);
/* Use GFP_ATOMIC since BHs are disabled. */
- if ((ret = sctp_add_bind_addr(bp, &tmpaddr, GFP_ATOMIC))) {
- sctp_sk_addr_restore(sk, &saveaddr);
- } else if (!bp->port) {
- bp->port = *snum;
- }
+ addr->v4.sin_port = ntohs(addr->v4.sin_port);
+ ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC);
+ addr->v4.sin_port = htons(addr->v4.sin_port);
+ if (!ret && !bp->port)
+ bp->port = snum;
sctp_write_unlock(&ep->base.addr_lock);
sctp_local_bh_enable();
+
+ /* Copy back into socket for getsockname() use. */
+ if (!ret) {
+ inet_sk(sk)->sport = htons(inet_sk(sk)->num);
+ af->to_sk(addr, sk);
+ }
+
return ret;
}
@@ -735,7 +711,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
- struct msghdr *msg, int size)
+ struct msghdr *msg, int msg_len)
{
sctp_opt_t *sp;
sctp_endpoint_t *ep;
@@ -750,13 +726,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_assoc_t associd = NULL;
sctp_cmsgs_t cmsgs = { 0 };
int err;
- size_t msg_len;
sctp_scope_t scope;
long timeo;
__u16 sinfo_flags = 0;
- SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, "
- "size: %d)\n", sk, msg, size);
+ SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
+ sk, msg, msg_len);
err = 0;
sp = sctp_sk(sk);
@@ -778,12 +753,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
* For a peeled-off socket, msg_name is ignored.
*/
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
- err = sctp_verify_addr(sk, (struct sockaddr *)msg->msg_name,
- msg->msg_namelen);
+ int msg_namelen = msg->msg_namelen;
+
+ err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name,
+ msg_namelen);
if (err)
return err;
- memcpy(&to, msg->msg_name, msg->msg_namelen);
+ if (msg_namelen > sizeof(to))
+ msg_namelen = sizeof(to);
+ memcpy(&to, msg->msg_name, msg_namelen);
SCTP_DEBUG_PRINTK("Just memcpy'd. msg_name is "
"0x%x:%u.\n",
to.v4.sin_addr.s_addr, to.v4.sin_port);
@@ -792,8 +771,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
msg_name = msg->msg_name;
}
- msg_len = get_user_iov_size(msg->msg_iov, msg->msg_iovlen);
-
sinfo = cmsgs.info;
sinit = cmsgs.init;
@@ -1216,9 +1193,11 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
* Otherwise, set MSG_EOR indicating the end of a message.
*/
if (skb_len > copied) {
+ msg->msg_flags &= ~MSG_EOR;
+ if (flags & MSG_PEEK)
+ goto out_free;
sctp_skb_pull(skb, copied);
skb_queue_head(&sk->receive_queue, skb);
- msg->msg_flags &= ~MSG_EOR;
goto out;
} else {
msg->msg_flags |= MSG_EOR;
@@ -1335,6 +1314,16 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
return 0;
}
+static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval,
+ int optlen)
+{
+ if (optlen != sizeof(struct sctp_initmsg))
+ return -EINVAL;
+ if (copy_from_user(&sctp_sk(sk)->initmsg, optval, optlen))
+ return -EFAULT;
+ return 0;
+}
+
/* API 6.2 setsockopt(), getsockopt()
*
* Applications use setsockopt() and getsockopt() to set or retrieve
@@ -1355,13 +1344,10 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
* optlen - the size of the buffer.
*/
SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
- char *optval, int optlen)
+ char *optval, int optlen)
{
int retval = 0;
char *tmp;
- sctp_protocol_t *proto = sctp_get_protocol();
- struct list_head *pos;
- sctp_func_t *af;
SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n",
sk, optname);
@@ -1373,14 +1359,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
* are at all well-founded.
*/
if (level != SOL_SCTP) {
- list_for_each(pos, &proto->address_families) {
- af = list_entry(pos, sctp_func_t, list);
-
- retval = af->setsockopt(sk, level, optname, optval,
- optlen);
- if (retval < 0)
- goto out_nounlock;
- }
+ struct sctp_af *af = sctp_sk(sk)->pf->af;
+ retval = af->setsockopt(sk, level, optname, optval, optlen);
+ goto out_nounlock;
}
sctp_lock_sock(sk);
@@ -1430,6 +1411,10 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
optlen);
break;
+ case SCTP_INITMSG:
+ retval = sctp_setsockopt_initmsg(sk, optval, optlen);
+ break;
+
default:
retval = -ENOPROTOOPT;
break;
@@ -1484,7 +1469,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
goto out_unlock;
}
- err = sctp_verify_addr(sk, uaddr, addr_len);
+ err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len);
if (err)
goto out_unlock;
@@ -1938,13 +1923,19 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
return 0;
}
+static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen)
+{
+ if (len != sizeof(struct sctp_initmsg))
+ return -EINVAL;
+ if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
+ return -EFAULT;
+ return 0;
+}
+
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
int retval = 0;
- sctp_protocol_t *proto = sctp_get_protocol();
- sctp_func_t *af;
- struct list_head *pos;
int len;
SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p, ...)\n", sk);
@@ -1956,13 +1947,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
* are at all well-founded.
*/
if (level != SOL_SCTP) {
- list_for_each(pos, &proto->address_families) {
- af = list_entry(pos, sctp_func_t, list);
- retval = af->getsockopt(sk, level, optname,
- optval, optlen);
- if (retval < 0)
- return retval;
- }
+ struct sctp_af *af = sctp_sk(sk)->pf->af;
+
+ retval = af->getsockopt(sk, level, optname, optval, optlen);
+ return retval;
}
if (get_user(len, optlen))
@@ -1997,6 +1985,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
optlen);
break;
+ case SCTP_INITMSG:
+ retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
+ break;
+
default:
retval = -ENOPROTOOPT;
break;
@@ -2030,12 +2022,17 @@ static void sctp_unhash(struct sock *sk)
*/
static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
unsigned short snum);
-static long sctp_get_port_local(struct sock *sk, unsigned short snum)
+static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
{
sctp_bind_hashbucket_t *head; /* hash list */
sctp_bind_bucket_t *pp; /* hash list port iterator */
sctp_protocol_t *sctp = sctp_get_protocol();
+ unsigned short snum;
int ret;
+
+ /* NOTE: Remember to put this back to net order. */
+ addr->v4.sin_port = ntohs(addr->v4.sin_port);
+ snum = addr->v4.sin_port;
SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
@@ -2101,6 +2098,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
}
}
+
if (pp != NULL && pp->sk != NULL) {
/* We had a port hash table hit - there is an
* available port (pp != NULL) and it is being
@@ -2108,7 +2106,6 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
* socket is going to be sk2.
*/
int sk_reuse = sk->reuse;
- union sctp_addr tmpaddr;
struct sock *sk2 = pp->sk;
SCTP_DEBUG_PRINTK("sctp_get_port() found a "
@@ -2116,27 +2113,6 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
if (pp->fastreuse != 0 && sk->reuse != 0)
goto success;
- /* FIXME - multiple addresses need to be supported
- * later.
- */
- switch (sk->family) {
- case PF_INET:
- tmpaddr.v4.sin_family = AF_INET;
- tmpaddr.v4.sin_port = snum;
- tmpaddr.v4.sin_addr.s_addr = inet_sk(sk)->rcv_saddr;
- break;
-
- case PF_INET6:
- SCTP_V6(tmpaddr.v6.sin6_family = AF_INET6;
- tmpaddr.v6.sin6_port = snum;
- tmpaddr.v6.sin6_addr = inet6_sk(sk)->rcv_saddr;
- )
- break;
-
- default:
- break;
- };
-
/* Run through the list of sockets bound to the port
* (pp->port) [via the pointers bind_next and
* bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
@@ -2154,8 +2130,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
if (sk_reuse && sk2->reuse)
continue;
- if (sctp_bind_addr_match(&ep2->base.bind_addr,
- &tmpaddr,
+ if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
sctp_sk(sk)))
goto found;
}
@@ -2207,12 +2182,25 @@ fail:
sctp_local_bh_enable();
SCTP_DEBUG_PRINTK("sctp_get_port() ends, ret=%d\n", ret);
+ addr->v4.sin_port = htons(addr->v4.sin_port);
return ret;
}
+/* Assign a 'snum' port to the socket. If snum == 0, an ephemeral
+ * port is requested.
+ */
static int sctp_get_port(struct sock *sk, unsigned short snum)
{
- long ret = sctp_get_port_local(sk, snum);
+ long ret;
+ union sctp_addr addr;
+ struct sctp_af *af = sctp_sk(sk)->pf->af;
+
+ /* Set up a dummy address struct from the sk. */
+ af->from_sk(&addr, sk);
+ addr.v4.sin_port = htons(snum);
+
+ /* Note: sk->num gets filled in if ephemeral port request. */
+ ret = sctp_get_port_local(sk, &addr);
return (ret ? 1 : 0);
}
@@ -2413,7 +2401,7 @@ void sctp_put_port(struct sock *sk)
static int sctp_autobind(struct sock *sk)
{
union sctp_addr autoaddr;
- struct sctp_func *af;
+ struct sctp_af *af;
unsigned short port;
/* Initialize a local sockaddr structure to INADDR_ANY. */
@@ -2537,58 +2525,6 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
return 0;
}
-/* Setup sk->rcv_saddr before calling get_port(). */
-static inline void sctp_sk_addr_set(struct sock *sk,
- const union sctp_addr *newaddr,
- union sctp_addr *saveaddr)
-{
- struct inet_opt *inet = inet_sk(sk);
-
- saveaddr->sa.sa_family = newaddr->sa.sa_family;
-
- switch (newaddr->sa.sa_family) {
- case AF_INET:
- saveaddr->v4.sin_addr.s_addr = inet->rcv_saddr;
- inet->rcv_saddr = inet->saddr = newaddr->v4.sin_addr.s_addr;
- break;
-
- case AF_INET6:
- SCTP_V6({
- struct ipv6_pinfo *np = inet6_sk(sk);
-
- saveaddr->v6.sin6_addr = np->rcv_saddr;
- np->rcv_saddr = np->saddr = newaddr->v6.sin6_addr;
- break;
- })
-
- default:
- break;
- };
-}
-
-/* Restore sk->rcv_saddr after failing get_port(). */
-static inline void sctp_sk_addr_restore(struct sock *sk, const union sctp_addr *addr)
-{
- struct inet_opt *inet = inet_sk(sk);
-
- switch (addr->sa.sa_family) {
- case AF_INET:
- inet->rcv_saddr = inet->saddr = addr->v4.sin_addr.s_addr;
- break;
-
- case AF_INET6:
- SCTP_V6({
- struct ipv6_pinfo *np = inet6_sk(sk);
-
- np->rcv_saddr = np->saddr = addr->v6.sin6_addr;
- break;
- })
-
- default:
- break;
- };
-}
-
/*
* Wait for a packet..
* Note: This function is the same function as in core/datagram.c
@@ -2711,27 +2647,15 @@ no_packet:
}
/* Verify that this is a valid address. */
-static int sctp_verify_addr(struct sock *sk, struct sockaddr *addr, int len)
+static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len)
{
- struct sctp_func *af;
-
- /* Check minimum size. */
- if (len < sizeof (struct sockaddr))
- return -EINVAL;
+ struct sctp_af *af;
- /* Do we support this address family in general? */
- af = sctp_get_af_specific(addr->sa_family);
+ /* Verify basic sockaddr. */
+ af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
if (!af)
return -EINVAL;
- /* Does this PF support this AF? */
- if (!sctp_sk(sk)->pf->af_supported(addr->sa_family))
- return -EINVAL;
-
- /* Verify the minimum for this AF sockaddr. */
- if (len < af->sockaddr_len)
- return -EINVAL;
-
/* Is this a valid SCTP address? */
if (!af->addr_valid((union sctp_addr *)addr))
return -EINVAL;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 11b3bb63d241..75eee265cbfc 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -1,5 +1,6 @@
/* SCTP kernel reference Implementation
* Copyright (c) 2002 International Business Machines Corp.
+ * Copyright (c) 2002 Intel Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
@@ -32,6 +33,7 @@
* Written or modified by:
* Mingqin Liu <liuming@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
+ * Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
@@ -70,6 +72,9 @@ static ctl_table sctp_table[] = {
{ NET_SCTP_HB_INTERVAL, "hb_interval",
&sctp_proto.hb_interval, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies },
+ { NET_SCTP_PRESERVE_ENABLE, "cookie_preserve_enable",
+ &sctp_proto.cookie_preserve_enable, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies },
{ NET_SCTP_RTO_ALPHA, "rto_alpha_exp_divisor",
&sctp_proto.rto_alpha, sizeof(int), 0644, NULL,
&proc_dointvec },
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 4fba07425349..303c33852b99 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -207,7 +207,7 @@ void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr,
struct sctp_opt *opt)
{
sctp_association_t *asoc = transport->asoc;
- struct sctp_func *af = transport->af_specific;
+ struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;