summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-11-10 23:37:00 -0800
committerSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-11-10 23:37:00 -0800
commitaf18c09381cf2bd0ab8263d086aa2f48cf174b28 (patch)
tree75a2e3f93b672b19b580a49121d9893954314b50
parent5be2bc3c89f6813ab0ab21cdd27d6d0f2ee94d91 (diff)
parent7851956a217f9c2a5a674f3ccd9b29b60cbeeeaf (diff)
Merge dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/linux-2.5.47
into dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/lksctp-2.5.47
-rw-r--r--include/net/sctp/command.h3
-rw-r--r--include/net/sctp/sctp.h81
-rw-r--r--include/net/sctp/sm.h10
-rw-r--r--include/net/sctp/structs.h209
-rw-r--r--net/sctp/associola.c45
-rw-r--r--net/sctp/bind_addr.c147
-rw-r--r--net/sctp/endpointola.c51
-rw-r--r--net/sctp/input.c136
-rw-r--r--net/sctp/ipv6.c151
-rw-r--r--net/sctp/output.c18
-rw-r--r--net/sctp/outqueue.c6
-rw-r--r--net/sctp/primitive.c23
-rw-r--r--net/sctp/protocol.c217
-rw-r--r--net/sctp/sm_make_chunk.c335
-rw-r--r--net/sctp/sm_sideeffect.c111
-rw-r--r--net/sctp/sm_statefuns.c165
-rw-r--r--net/sctp/sm_statetable.c24
-rw-r--r--net/sctp/socket.c456
-rw-r--r--net/sctp/transport.c16
19 files changed, 1312 insertions, 892 deletions
diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h
index d299eee377c1..b1fba0151675 100644
--- a/include/net/sctp/command.h
+++ b/include/net/sctp/command.h
@@ -27,6 +27,7 @@
*
* La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us>
+ * 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.
@@ -72,6 +73,7 @@ typedef enum {
SCTP_CMD_STRIKE, /* Mark a strike against a transport. */
SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
+ SCTP_CMD_HB_TIMERS_UPDATE, /* Update the heartbeat timers. */
SCTP_CMD_TRANSPORT_RESET, /* Reset the status of a transport. */
SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */
SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */
@@ -83,6 +85,7 @@ typedef enum {
SCTP_CMD_UPDATE_ASSOC, /* Update association information. */
SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */
SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */
+ SCTP_CMD_RTO_PENDING, /* Set transport's rto_pending. */
SCTP_CMD_LAST
} sctp_verb_t;
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 9997455f9a74..73c948813e72 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -1,43 +1,43 @@
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001 International Business Machines, Corp.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
- *
+ *
* This file is part of the SCTP kernel reference Implementation
- *
- * The base lksctp header.
- *
- * The SCTP reference implementation is free software;
- * you can redistribute it and/or modify it under the terms of
+ *
+ * The base lksctp header.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- *
- * The SCTP reference implementation is distributed in the hope that it
+ *
+ * The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
+ * Boston, MA 02111-1307, USA.
+ *
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
- *
+ *
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
- * Written or modified by:
+ * Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org>
* Xingang Guo <xingang.guo@intel.com>
* Jon Grimm <jgrimm@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
- *
+ *
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
@@ -52,10 +52,10 @@
* structs
* prototypes
* macros, externs, and inlines
- *
- * Move test_frame specific items out of the kernel headers
+ *
+ * Move test_frame specific items out of the kernel headers
* and into the test frame headers. This is not perfect in any sense
- * and will continue to evolve.
+ * and will continue to evolve.
*/
@@ -78,7 +78,7 @@
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#include <net/ipv6.h>
#include <net/ip6_route.h>
-#endif
+#endif
#include <asm/uaccess.h>
#include <asm/page.h>
@@ -105,19 +105,19 @@
#endif
-/* Certain internal static functions need to be exported when
+/* Certain internal static functions need to be exported when
* compiled into the test frame.
*/
#ifndef SCTP_STATIC
#define SCTP_STATIC static
#endif
-/*
- * Function declarations.
+/*
+ * Function declarations.
*/
/*
- * sctp_protocol.c
+ * sctp_protocol.c
*/
extern sctp_protocol_t sctp_proto;
extern struct sock *sctp_get_ctl_sock(void);
@@ -142,7 +142,7 @@ extern int sctp_primitive_ASSOCIATE(sctp_association_t *, void *arg);
extern int sctp_primitive_SHUTDOWN(sctp_association_t *, void *arg);
extern int sctp_primitive_ABORT(sctp_association_t *, void *arg);
extern int sctp_primitive_SEND(sctp_association_t *, void *arg);
-
+extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg);
/*
* sctp_crc32c.c
@@ -418,6 +418,19 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
return retval;
}
+/* Walk through a list of TLV parameters. Don't trust the
+ * individual parameter lengths and instead depend on
+ * the chunk length to indicate when to stop. Make sure
+ * there is room for a param header too.
+ */
+#define sctp_walk_params(pos, chunk, member)\
+_sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member)
+
+#define _sctp_walk_params(pos, chunk, end, member)\
+for (pos.v = chunk->member;\
+ pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\
+ pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)); \
+ pos.v += WORD_ROUND(ntohs(pos.p->length)))
/* Round an int up to the next multiple of 4. */
#define WORD_ROUND(s) (((s)+3)&~3)
@@ -460,6 +473,26 @@ static inline sctp_protocol_t *sctp_get_protocol(void)
return &sctp_proto;
}
+/* Convert from an IP version number to an Address Family symbol. */
+static inline int ipver2af(__u8 ipver)
+{
+ int family;
+
+ switch (ipver) {
+ case 4:
+ family = AF_INET;
+ break;
+ case 6:
+ family = AF_INET6;
+ break;
+ default:
+ family = 0;
+ break;
+ };
+
+ return family;
+}
+
/* Warning: The following hash functions assume a power of two 'size'. */
/* This is the hash function for the SCTP port hash table. */
static inline int sctp_phashfn(__u16 lport)
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index 80cc12e4ac5c..5082a4a11cb9 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -157,6 +157,7 @@ sctp_state_fn_t sctp_sf_shutdown_ack_sent_prm_abort;
sctp_state_fn_t sctp_sf_error_closed;
sctp_state_fn_t sctp_sf_error_shutdown;
sctp_state_fn_t sctp_sf_ignore_primitive;
+sctp_state_fn_t sctp_sf_do_prm_requestheartbeat;
/* Prototypes for other event state functions. */
sctp_state_fn_t sctp_sf_do_9_2_start_shutdown;
@@ -206,9 +207,6 @@ sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *,
sctp_chunk_t *,
const int priority);
__u32 sctp_generate_verification_tag(void);
-sctpParam_t sctp_get_my_addrs_raw(const sctp_association_t *,
- const int priority, int *addrs_len);
-
void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag);
/* Prototypes for chunk-building functions. */
@@ -334,10 +332,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *);
__u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */
-void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *,
+void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *,
__u16 port);
-int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *);
-int sockaddr2sctp_addr(const sockaddr_storage_t *, sctp_addr_param_t *);
+int sctp_addr2sockaddr(const union sctp_params, union sctp_addr *);
+int sockaddr2sctp_addr(const union sctp_addr *, sctp_addr_param_t *);
/* Extern declarations for major data structures. */
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 9c3979f31d1f..a6f1651ad0f1 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -2,35 +2,35 @@
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
- * Copyright (c) 2001 International Business Machines Corp.
- *
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ *
* This file is part of the SCTP kernel reference Implementation
- *
- * The SCTP reference implementation is free software;
- * you can redistribute it and/or modify it under the terms of
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- *
- * The SCTP reference implementation is distributed in the hope that it
+ *
+ * The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
+ * Boston, MA 02111-1307, USA.
+ *
* Please send any bug reports or fixes you make to the
* email addresses:
* lksctp developers <lksctp-developers@lists.sourceforge.net>
- *
+ *
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
- * Written or modified by:
+ * Written or modified by:
* Randall Stewart <randall@sctp.chicago.il.us>
* Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@email.mot.com>
@@ -41,8 +41,8 @@
* Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
- * Dajiang Zhang <dajiang.zhang@nokia.com>
- *
+ * Dajiang Zhang <dajiang.zhang@nokia.com>
+ *
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
@@ -95,11 +95,11 @@ struct sockaddr_storage {
/* A convenience structure for handling sockaddr structures.
* We should wean ourselves off this.
*/
-typedef union {
+union sctp_addr {
struct sockaddr_in v4;
struct sockaddr_in6 v6;
struct sockaddr sa;
-} sockaddr_storage_t;
+};
/* Forward declarations for data structures. */
@@ -246,29 +246,41 @@ typedef struct sctp_func {
int optname,
char *optval,
int *optlen);
- struct dst_entry *(*get_dst) (sockaddr_storage_t *daddr,
- sockaddr_storage_t *saddr);
+ struct dst_entry *(*get_dst) (union sctp_addr *daddr,
+ union sctp_addr *saddr);
+ void (*copy_addrlist) (struct list_head *,
+ struct net_device *);
int (*cmp_saddr) (struct dst_entry *dst,
- sockaddr_storage_t *saddr);
- __u16 net_header_len;
+ union sctp_addr *saddr);
+ void (*addr_copy) (union sctp_addr *dst,
+ union sctp_addr *src);
+ void (*from_skb) (union sctp_addr *,
+ struct sk_buff *skb,
+ int saddr);
+ int (*addr_valid) (union sctp_addr *);
+ sctp_scope_t (*scope) (union sctp_addr *);
+ void (*inaddr_any) (union sctp_addr *, unsigned short);
+ __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(const sockaddr_storage_t *address);
+sctp_func_t *sctp_get_af_specific(sa_family_t);
/* 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);
+ struct sctp_func *af;
} sctp_pf_t;
/* SCTP Socket type: UDP or TCP style. */
typedef enum {
SCTP_SOCKET_UDP = 0,
- SCTP_SOCKET_UDP_HIGH_BANDWIDTH,
- SCTP_SOCKET_TCP
+ SCTP_SOCKET_UDP_HIGH_BANDWIDTH,
+ SCTP_SOCKET_TCP
} sctp_socket_type_t;
/* Per socket SCTP information. */
@@ -339,7 +351,7 @@ typedef struct sctp_cookie {
__u32 initial_tsn;
/* This holds the originating address of the INIT packet. */
- sockaddr_storage_t peer_addr;
+ union sctp_addr peer_addr;
/* This is a shim for my peer's INIT packet, followed by
* a copy of the raw address list of the association.
@@ -359,20 +371,6 @@ typedef struct sctp_signed_cookie {
} sctp_signed_cookie_t;
-/* This convenience type allows us to avoid casting when walking
- * through a parameter list.
- */
-typedef union {
- __u8 *v;
- sctp_paramhdr_t *p;
-
- sctp_cookie_preserve_param_t *bht;
- sctp_hostname_param_t *dns;
- sctp_cookie_param_t *cookie;
- sctp_supported_addrs_param_t *sat;
- sctp_ipv4addr_param_t *v4;
- sctp_ipv6addr_param_t *v6;
-} sctpParam_t;
/* This is another convenience type to allocate memory for address
* params for the maximum size and pass such structures around
@@ -383,6 +381,21 @@ typedef union {
sctp_ipv6addr_param_t v6;
} sctp_addr_param_t;
+/* A convenience type to allow walking through the various
+ * parameters and avoid casting all over the place.
+ */
+union sctp_params {
+ void *v;
+ sctp_paramhdr_t *p;
+ sctp_cookie_preserve_param_t *life;
+ sctp_hostname_param_t *dns;
+ sctp_cookie_param_t *cookie;
+ sctp_supported_addrs_param_t *sat;
+ sctp_ipv4addr_param_t *v4;
+ sctp_ipv6addr_param_t *v6;
+ sctp_addr_param_t *addr;
+};
+
/* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length
* The Sender-specific Heartbeat Info field should normally include
@@ -392,7 +405,7 @@ typedef union {
*/
typedef struct sctp_sender_hb_info {
sctp_paramhdr_t param_hdr;
- sockaddr_storage_t daddr;
+ union sctp_addr daddr;
unsigned long sent_at;
} sctp_sender_hb_info_t __attribute__((packed));
@@ -433,7 +446,7 @@ struct SCTP_chunk {
*/
/* We point this at the FIRST TLV parameter to chunk_hdr. */
- sctpParam_t param_hdr;
+ union sctp_params param_hdr;
union {
__u8 *v;
sctp_datahdr_t *data_hdr;
@@ -474,13 +487,13 @@ struct SCTP_chunk {
__u8 ecn_ce_done; /* Have we processed the ECN CE bit? */
__u8 pdiscard; /* Discard the whole packet now? */
__u8 tsn_gap_acked; /* Is this chunk acked by a GAP ACK? */
- __u8 fast_retransmit; /* Is this chunk fast retransmitted? */
+ __u8 fast_retransmit; /* Is this chunk fast retransmitted? */
__u8 tsn_missing_report; /* Data chunk missing counter. */
/* What is the origin IP address for this chunk? */
- sockaddr_storage_t source;
+ union sctp_addr source;
/* Destination address for this chunk. */
- sockaddr_storage_t dest;
+ union sctp_addr dest;
/* For an inbound chunk, this tells us where it came from.
* For an outbound chunk, it tells us where we'd like it to
@@ -497,8 +510,8 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data);
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data);
sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *,
struct sock *);
-void sctp_init_addrs(sctp_chunk_t *chunk);
-const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk);
+void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *);
+const union sctp_addr *sctp_source(const sctp_chunk_t *chunk);
/* This is a structure for holding either an IPv6 or an IPv4 address. */
/* sin_family -- AF_INET or AF_INET6
@@ -507,7 +520,7 @@ const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk);
*/
struct sockaddr_storage_list {
struct list_head list;
- sockaddr_storage_t a;
+ union sctp_addr a;
};
typedef sctp_chunk_t *(sctp_packet_phandler_t)(sctp_association_t *);
@@ -573,7 +586,7 @@ void sctp_packet_free(sctp_packet_t *);
/* This represents a remote transport address.
- * For local transport addresses, we just use sockaddr_storage_t.
+ * For local transport addresses, we just use union sctp_addr.
*
* RFC2960 Section 1.4 Key Terms
*
@@ -601,7 +614,7 @@ struct SCTP_transport {
int dead;
/* This is the peer's IP address and port. */
- sockaddr_storage_t ipaddr;
+ union sctp_addr ipaddr;
/* These are the functions we call to handle LLP stuff. */
sctp_func_t *af_specific;
@@ -684,13 +697,15 @@ struct SCTP_transport {
*/
unsigned long last_time_ecne_reduced;
- /* state : The current state of this destination,
- * : i.e. DOWN, UP, ALLOW-HB, NO-HEARTBEAT, etc.
+ /* active : The current active state of this destination,
+ * : i.e. DOWN, UP, etc.
*/
- struct {
- int active;
- int hb_allowed;
- } state;
+ int active;
+
+ /* hb_allowed : The current heartbeat state of this destination,
+ * : i.e. ALLOW-HB, NO-HEARTBEAT, etc.
+ */
+ int hb_allowed;
/* These are the error stats for this destination. */
@@ -739,11 +754,11 @@ struct SCTP_transport {
int malloced; /* Is this structure kfree()able? */
};
-extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int);
+extern sctp_transport_t *sctp_transport_new(const union sctp_addr *, int);
extern sctp_transport_t *sctp_transport_init(sctp_transport_t *,
- const sockaddr_storage_t *, int);
+ const union sctp_addr *, int);
extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *);
-extern void sctp_transport_route(sctp_transport_t *, sockaddr_storage_t *);
+extern void sctp_transport_route(sctp_transport_t *, union sctp_addr *);
extern void sctp_transport_free(sctp_transport_t *);
extern void sctp_transport_destroy(sctp_transport_t *);
extern void sctp_transport_reset_timers(sctp_transport_t *);
@@ -890,30 +905,30 @@ void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port);
void sctp_bind_addr_free(sctp_bind_addr_t *);
int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src,
sctp_scope_t scope, int priority,int flags);
-int sctp_add_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *,
+int sctp_add_bind_addr(sctp_bind_addr_t *, union sctp_addr *,
int priority);
-int sctp_del_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *);
-int sctp_bind_addr_has_addr(sctp_bind_addr_t *, const sockaddr_storage_t *);
-sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
- int *addrs_len,
- int priority);
+int sctp_del_bind_addr(sctp_bind_addr_t *, union sctp_addr *);
+int sctp_bind_addr_has_addr(sctp_bind_addr_t *, const union sctp_addr *);
+union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
+ int *addrs_len,
+ int priority);
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp,
__u8 *raw_addr_list,
int addrs_len,
unsigned short port,
int priority);
-sctp_scope_t sctp_scope(const sockaddr_storage_t *);
-int sctp_in_scope(const sockaddr_storage_t *addr, const sctp_scope_t scope);
-int sctp_is_any(const sockaddr_storage_t *addr);
-int sctp_addr_is_valid(const sockaddr_storage_t *addr);
+sctp_scope_t sctp_scope(const union sctp_addr *);
+int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
+int sctp_is_any(const union sctp_addr *addr);
+int sctp_addr_is_valid(const union sctp_addr *addr);
/* What type of sctp_endpoint_common? */
typedef enum {
SCTP_EP_TYPE_SOCKET,
SCTP_EP_TYPE_ASSOCIATION,
-} sctp_endpoint_type_t;
+} sctp_endpoint_type_t;
/*
* A common base class to bridge the implmentation view of a
@@ -1048,35 +1063,25 @@ void sctp_endpoint_put(sctp_endpoint_t *);
void sctp_endpoint_hold(sctp_endpoint_t *);
void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc);
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
- const sockaddr_storage_t *paddr,
+ const union sctp_addr *paddr,
sctp_transport_t **);
+int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
- const sockaddr_storage_t *);
+ const union sctp_addr *);
-int sctp_has_association(const sockaddr_storage_t *laddr,
- const sockaddr_storage_t *paddr);
+int sctp_has_association(const union sctp_addr *laddr,
+ const union sctp_addr *paddr);
int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid,
sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
-int sctp_verify_param(const sctp_association_t *asoc,
- sctpParam_t param,
- sctp_cid_t cid,
- sctp_chunk_t *chunk,
- sctp_chunk_t **err_chunk);
-int sctp_process_unk_param(const sctp_association_t *asoc,
- sctpParam_t param,
- sctp_chunk_t *chunk,
- sctp_chunk_t **err_chunk);
-void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
- const sockaddr_storage_t *peer_addr,
- sctp_init_chunk_t *peer_init, int priority);
-int sctp_process_param(sctp_association_t *asoc,
- sctpParam_t param,
- const sockaddr_storage_t *peer_addr,
- sctp_cid_t cid, int priority);
+int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
+ const union sctp_addr *peer_addr,
+ sctp_init_chunk_t *peer_init, int priority);
+int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
+ const union sctp_addr *peer_addr, int priority);
__u32 sctp_generate_tag(const sctp_endpoint_t *ep);
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep);
@@ -1163,10 +1168,10 @@ struct SCTP_association {
sctp_transport_t *primary_path;
/* Cache the primary path address here, when we
- * need a an address for msg_name.
+ * need a an address for msg_name.
*/
- sockaddr_storage_t primary_addr;
-
+ union sctp_addr primary_addr;
+
/* active_path
* The path that we are currently using to
* transmit new data and most control chunks.
@@ -1267,7 +1272,7 @@ struct SCTP_association {
/* Overall : The threshold for this association that if
* Error : the Overall Error Count reaches will cause
- * Threshold : this association to be torn down.
+ * Threshold : this association to be torn down.
*/
int overall_error_threshold;
@@ -1313,13 +1318,13 @@ struct SCTP_association {
*/
__u32 next_tsn;
- /*
+ /*
* Last Rcvd : This is the last TSN received in sequence. This value
* TSN : is set initially by taking the peer's Initial TSN,
* : received in the INIT or INIT ACK chunk, and
* : subtracting one from it.
*
- * Most of RFC 2960 refers to this as the Cumulative TSN Ack Point.
+ * Most of RFC 2960 refers to this as the Cumulative TSN Ack Point.
*/
__u32 ctsn_ack_point;
@@ -1543,16 +1548,16 @@ void sctp_association_hold(sctp_association_t *);
sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *);
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *,
- const sockaddr_storage_t *);
+ const union sctp_addr *);
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *,
- const sockaddr_storage_t *address,
+ const union sctp_addr *address,
const int priority);
void sctp_assoc_control_transport(sctp_association_t *, sctp_transport_t *,
sctp_transport_cmd_t, sctp_sn_error_t);
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *, __u32);
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *,
- const sockaddr_storage_t *,
- const sockaddr_storage_t *);
+ const union sctp_addr *,
+ const union sctp_addr *);
void sctp_assoc_migrate(sctp_association_t *, struct sock *);
void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src);
@@ -1560,10 +1565,10 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *);
__u32 __sctp_association_get_tsn_block(sctp_association_t *, int);
__u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid);
-int sctp_cmp_addr(const sockaddr_storage_t *ss1,
- const sockaddr_storage_t *ss2);
-int sctp_cmp_addr_exact(const sockaddr_storage_t *ss1,
- const sockaddr_storage_t *ss2);
+int sctp_cmp_addr(const union sctp_addr *ss1,
+ const union sctp_addr *ss2);
+int sctp_cmp_addr_exact(const union sctp_addr *ss1,
+ const union sctp_addr *ss2);
sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc);
sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index d6d8f9f9c873..b0b061e5ba32 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1,7 +1,7 @@
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001 International Business Machines Corp.
+ * Copyright (c) 2001-2002 International Business Machines Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
@@ -363,7 +363,7 @@ static void sctp_association_destroy(sctp_association_t *asoc)
/* Add a transport address to an association. */
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
- const sockaddr_storage_t *addr,
+ const union sctp_addr *addr,
int priority)
{
sctp_transport_t *peer;
@@ -423,7 +423,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->frag_point = asoc->pmtu -
(SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t));
- /* The asoc->peer.port might not be meaningful as of now, but
+ /* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway.
*/
(asoc->outqueue.init_output)(&peer->packet,
@@ -460,6 +460,9 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
min(asoc->overall_error_threshold + peer->error_threshold,
asoc->max_retrans);
+ /* By default, enable heartbeat for peer address. */
+ peer->hb_allowed = 1;
+
/* Initialize the peer's heartbeat interval based on the
* sock configured value.
*/
@@ -474,7 +477,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->peer.primary_path = peer;
/* Set a default msg_name for events. */
memcpy(&asoc->peer.primary_addr, &peer->ipaddr,
- sizeof(sockaddr_storage_t));
+ sizeof(union sctp_addr));
asoc->peer.active_path = peer;
asoc->peer.retran_path = peer;
}
@@ -487,7 +490,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
/* Lookup a transport by address. */
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
- const sockaddr_storage_t *address)
+ const union sctp_addr *address)
{
sctp_transport_t *t;
struct list_head *pos;
@@ -522,12 +525,12 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
/* Record the transition on the transport. */
switch (command) {
case SCTP_TRANSPORT_UP:
- transport->state.active = 1;
+ transport->active = 1;
spc_state = ADDRESS_AVAILABLE;
break;
case SCTP_TRANSPORT_DOWN:
- transport->state.active = 0;
+ transport->active = 0;
spc_state = ADDRESS_UNREACHABLE;
break;
@@ -557,7 +560,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
- if (!t->state.active)
+ if (!t->active)
continue;
if (!first || t->last_time_heard > first->last_time_heard) {
second = first;
@@ -577,7 +580,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
* [If the primary is active but not most recent, bump the most
* recently used transport.]
*/
- if (asoc->peer.primary_path->state.active &&
+ if (asoc->peer.primary_path->active &&
first != asoc->peer.primary_path) {
second = first;
first = asoc->peer.primary_path;
@@ -650,7 +653,7 @@ __u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid)
*
* FIXME: We do not match address scopes correctly.
*/
-int sctp_cmp_addr(const sockaddr_storage_t *ss1, const sockaddr_storage_t *ss2)
+int sctp_cmp_addr(const union sctp_addr *ss1, const union sctp_addr *ss2)
{
int len;
const void *base1;
@@ -706,8 +709,8 @@ match:
*
* FIXME: We do not match address scopes correctly.
*/
-int sctp_cmp_addr_exact(const sockaddr_storage_t *ss1,
- const sockaddr_storage_t *ss2)
+int sctp_cmp_addr_exact(const union sctp_addr *ss1,
+ const union sctp_addr *ss2)
{
int len;
const void *base1;
@@ -842,8 +845,8 @@ out:
/* Is this the association we are looking for? */
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc,
- const sockaddr_storage_t *laddr,
- const sockaddr_storage_t *paddr)
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr)
{
sctp_transport_t *transport;
@@ -902,17 +905,13 @@ static void sctp_assoc_bh_rcv(sctp_association_t *asoc)
* the incoming chunk. If so, get out of the while loop.
*/
if (!sctp_id2assoc(sk, associd))
- goto out;
+ break;
- if (error != 0)
- goto err_out;
+ /* If there is an error on chunk, discard this packet. */
+ if (error && chunk)
+ chunk->pdiscard = 1;
}
-err_out:
- /* Is this the right way to pass errors up to the ULP? */
- if (error)
- sk->err = -error;
-out:
}
/* This routine moves an association from its old sk to a new sk. */
@@ -1017,7 +1016,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
/* Try to find an active transport. */
- if (t->state.active) {
+ if (t->active) {
break;
} else {
/* Keep track of the next transport in case
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index 1c70d05e2c33..31b627e84327 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -1,7 +1,7 @@
/* SCTP kernel reference Implementation
* Copyright (c) Cisco 1999,2000
* Copyright (c) Motorola 1999,2000,2001
- * Copyright (c) International Business Machines Corp., 2001
+ * Copyright (c) International Business Machines Corp., 2001,2002
* Copyright (c) La Monte H.P. Yarroll 2001
*
* This file is part of the SCTP kernel reference implementation.
@@ -52,7 +52,7 @@
#include <net/sctp/sm.h>
/* Forward declarations for internal helpers. */
-static int sctp_copy_one_addr(sctp_bind_addr_t *, sockaddr_storage_t *,
+static int sctp_copy_one_addr(sctp_bind_addr_t *, union sctp_addr *,
sctp_scope_t scope, int priority, int flags);
static void sctp_bind_addr_clean(sctp_bind_addr_t *);
@@ -143,7 +143,7 @@ void sctp_bind_addr_free(sctp_bind_addr_t *bp)
}
/* Add an address to the bind address list in the SCTP_bind_addr structure. */
-int sctp_add_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *new,
+int sctp_add_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *new,
int priority)
{
struct sockaddr_storage_list *addr;
@@ -171,7 +171,7 @@ int sctp_add_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *new,
/* Delete an address from the bind address list in the SCTP_bind_addr
* structure.
*/
-int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
+int sctp_del_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *del_addr)
{
struct list_head *pos, *temp;
struct sockaddr_storage_list *addr;
@@ -196,18 +196,16 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
*
* The second argument is the return value for the length.
*/
-sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
- int priority)
+union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
+ int *addrs_len, int priority)
{
- sctpParam_t addrparms;
- sctpParam_t retval;
+ union sctp_params addrparms;
+ union sctp_params retval;
int addrparms_len;
sctp_addr_param_t rawaddr;
int len;
struct sockaddr_storage_list *addr;
struct list_head *pos;
-
- retval.v = NULL;
addrparms_len = 0;
len = 0;
@@ -216,11 +214,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
len += sizeof(sctp_addr_param_t);
}
- addrparms.v = kmalloc(len, priority);
- if (!addrparms.v)
+ retval.v = kmalloc(len, priority);
+ if (!retval.v)
goto end_raw;
- retval = addrparms;
+ addrparms = retval;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
@@ -244,7 +242,7 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
{
sctp_addr_param_t *rawaddr;
sctp_paramhdr_t *param;
- sockaddr_storage_t addr;
+ union sctp_addr addr;
int retval = 0;
int len;
@@ -254,7 +252,7 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
rawaddr = (sctp_addr_param_t *)raw_addr_list;
switch (param->type) {
- case SCTP_PARAM_IPV4_ADDRESS:
+ case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
sctp_param2sockaddr(&addr, rawaddr, port);
retval = sctp_add_bind_addr(bp, &addr, priority);
@@ -285,7 +283,7 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
********************************************************************/
/* Does this contain a specified address? */
-int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const sockaddr_storage_t *addr)
+int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const union sctp_addr *addr)
{
struct sockaddr_storage_list *laddr;
struct list_head *pos;
@@ -300,7 +298,7 @@ int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const sockaddr_storage_t *addr
}
/* Copy out addresses from the global local address list. */
-static int sctp_copy_one_addr(sctp_bind_addr_t *dest, sockaddr_storage_t *addr,
+static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr,
sctp_scope_t scope, int priority, int flags)
{
sctp_protocol_t *proto = sctp_get_protocol();
@@ -326,7 +324,7 @@ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, sockaddr_storage_t *addr,
}
/* Is addr one of the wildcards? */
-int sctp_is_any(const sockaddr_storage_t *addr)
+int sctp_is_any(const union sctp_addr *addr)
{
int retval = 0;
@@ -352,7 +350,7 @@ int sctp_is_any(const sockaddr_storage_t *addr)
}
/* Is 'addr' valid for 'scope'? */
-int sctp_in_scope(const sockaddr_storage_t *addr, sctp_scope_t scope)
+int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
{
sctp_scope_t addr_scope = sctp_scope(addr);
@@ -422,112 +420,13 @@ int sctp_in_scope(const sockaddr_storage_t *addr, sctp_scope_t scope)
********************************************************************/
/* What is the scope of 'addr'? */
-sctp_scope_t sctp_scope(const sockaddr_storage_t *addr)
-{
- sctp_scope_t retval = SCTP_SCOPE_GLOBAL;
-
- switch (addr->sa.sa_family) {
- case AF_INET:
- /* We are 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>.
- * The set of SCTP address scope hopefully can cover both
- * types of addresses.
- */
-
- /* Should IPv4 scoping be a sysctl configurable option
- * so users can turn it off (default on) for certain
- * unconventional networking environments?
- */
-
- /* Check for unusable SCTP addresses. */
- if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
- retval = SCTP_SCOPE_UNUSABLE;
- } else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
- retval = SCTP_SCOPE_LOOPBACK;
- } else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
- retval = SCTP_SCOPE_LINK;
- } else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
- retval = SCTP_SCOPE_PRIVATE;
- } else {
- retval = SCTP_SCOPE_GLOBAL;
- }
- break;
-
- case AF_INET6:
- {
- SCTP_V6(
- int v6scope;
- v6scope = ipv6_addr_scope((struct in6_addr *)
- &addr->v6.sin6_addr);
- /* The IPv6 scope is really a set of bit
- * fields. See IFA_* in <net/if_inet6.h>.
- * Mapping them to the generic SCTP scope
- * set is an attempt to have code
- * consistencies with the IPv4 scoping.
- */
- switch (v6scope) {
- case IFA_HOST:
- retval = SCTP_SCOPE_LOOPBACK;
- break;
-
- case IFA_LINK:
- retval = SCTP_SCOPE_LINK;
- break;
-
- case IFA_SITE:
- retval = SCTP_SCOPE_PRIVATE;
- break;
-
- default:
- retval = SCTP_SCOPE_GLOBAL;
- break;
- };
- );
- break;
- }
-
- default:
- retval = SCTP_SCOPE_GLOBAL;
- break;
- };
-
- return retval;
-}
-
-/* This function checks if the address is a valid address to be used for
- * SCTP.
- *
- * Output:
- * Return 0 - If the address is a non-unicast or an illegal address.
- * Return 1 - If the address is a unicast.
- */
-int sctp_addr_is_valid(const sockaddr_storage_t *addr)
+sctp_scope_t sctp_scope(const union sctp_addr *addr)
{
- unsigned short sa_family = addr->sa.sa_family;
+ struct sctp_func *af;
- switch (sa_family) {
- case AF_INET:
- /* Is this a non-unicast address or a unusable SCTP address? */
- if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
- return 0;
- break;
-
- case AF_INET6:
- SCTP_V6(
- {
- int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr);
-
- /* Is this a non-unicast address */
- if (!(ret & IPV6_ADDR_UNICAST))
- return 0;
- break;
- });
-
- default:
- return 0;
- };
+ af = sctp_get_af_specific(addr->sa.sa_family);
+ if (!af)
+ return SCTP_SCOPE_UNUSABLE;
- return 1;
+ return af->scope((union sctp_addr *)addr);
}
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index d3f16f85acb5..c095caf7badd 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -1,7 +1,7 @@
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001 International Business Machines, Corp.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
@@ -237,7 +237,7 @@ void sctp_endpoint_put(sctp_endpoint_t *ep)
/* Is this the endpoint we are looking for? */
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep,
- const sockaddr_storage_t *laddr)
+ const union sctp_addr *laddr)
{
sctp_endpoint_t *retval;
@@ -262,7 +262,7 @@ out:
*/
sctp_association_t *__sctp_endpoint_lookup_assoc(
const sctp_endpoint_t *endpoint,
- const sockaddr_storage_t *paddr,
+ const union sctp_addr *paddr,
sctp_transport_t **transport)
{
int rport;
@@ -289,7 +289,7 @@ sctp_association_t *__sctp_endpoint_lookup_assoc(
/* Lookup association on an endpoint based on a peer address. BH-safe. */
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
- const sockaddr_storage_t *paddr,
+ const union sctp_addr *paddr,
sctp_transport_t **transport)
{
sctp_association_t *asoc;
@@ -301,6 +301,30 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
return asoc;
}
+/* Look for any peeled off association from the endpoint that matches the
+ * given peer address.
+ */
+int sctp_endpoint_is_peeled_off(sctp_endpoint_t *ep,
+ const union sctp_addr *paddr)
+{
+ struct list_head *pos;
+ struct sockaddr_storage_list *addr;
+ sctp_bind_addr_t *bp;
+
+ sctp_read_lock(&ep->base.addr_lock);
+ bp = &ep->base.bind_addr;
+ list_for_each(pos, &bp->address_list) {
+ addr = list_entry(pos, struct sockaddr_storage_list, list);
+ if (sctp_has_association(&addr->a, paddr)) {
+ sctp_read_unlock(&ep->base.addr_lock);
+ return 1;
+ }
+ }
+ sctp_read_unlock(&ep->base.addr_lock);
+
+ return 0;
+}
+
/* Do delayed input processing. This is scheduled by sctp_rcv().
* This may be called on BH or task time.
*/
@@ -316,13 +340,13 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep)
int error = 0;
if (ep->base.dead)
- goto out;
+ return;
asoc = NULL;
inqueue = &ep->base.inqueue;
sk = ep->base.sk;
- while (NULL != (chunk = sctp_pop_inqueue(inqueue))) {
+ while (NULL != (chunk = sctp_pop_inqueue(inqueue))) {
subtype.chunk = chunk->chunk_hdr->type;
/* We might have grown an association since last we
@@ -350,25 +374,16 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep)
if (chunk->transport)
chunk->transport->last_time_heard = jiffies;
- /* FIX ME We really would rather NOT have to use
- * GFP_ATOMIC.
- */
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state,
ep, asoc, chunk, GFP_ATOMIC);
- if (error != 0)
- goto err_out;
+ if (error && chunk)
+ chunk->pdiscard = 1;
/* Check to see if the endpoint is freed in response to
* the incoming chunk. If so, get out of the while loop.
*/
if (!sctp_sk(sk)->ep)
- goto out;
+ break;
}
-
-err_out:
- /* Is this the right way to pass errors up to the ULP? */
- if (error)
- ep->base.sk->err = -error;
-out:
}
diff --git a/net/sctp/input.c b/net/sctp/input.c
index ede0def64d93..cf9e54b99283 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -60,64 +60,11 @@
/* Forward declarations for internal helpers. */
static int sctp_rcv_ootb(struct sk_buff *);
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
- const sockaddr_storage_t *laddr,
- const sockaddr_storage_t *paddr,
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr,
sctp_transport_t **transportp);
-sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const sockaddr_storage_t *laddr);
+sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
-/* Initialize a sockaddr_storage from in incoming skb.
- * FIXME: This belongs with AF specific sctp_func_t. --jgrimm
- */
-static sockaddr_storage_t *sctp_sockaddr_storage_init(sockaddr_storage_t *addr,
- const struct sk_buff *skb,
- int is_saddr)
-{
- sockaddr_storage_t *ret = NULL;
- void *to, *saddr, *daddr;
- __u16 *port;
- size_t len;
- struct sctphdr *sh;
-
- switch (skb->nh.iph->version) {
- case 4:
- to = &addr->v4.sin_addr.s_addr;
- port = &addr->v4.sin_port;
- saddr = &skb->nh.iph->saddr;
- daddr = &skb->nh.iph->daddr;
- len = sizeof(struct in_addr);
- addr->v4.sin_family = AF_INET;
- break;
-
- case 6:
- SCTP_V6(
- to = &addr->v6.sin6_addr;
- port = &addr->v6.sin6_port;
- saddr = &skb->nh.ipv6h->saddr;
- daddr = &skb->nh.ipv6h->daddr;
- len = sizeof(struct in6_addr);
- addr->v6.sin6_family = AF_INET6;
- addr->v6.sin6_flowinfo = 0; /* FIXME */
- addr->v6.sin6_scope_id = 0; /* FIXME */
- break;
- )
-
- default:
- goto out;
- };
-
- sh = (struct sctphdr *) skb->h.raw;
- if (is_saddr) {
- *port = ntohs(sh->source);
- memcpy(to, saddr, len);
- } else {
- *port = ntohs(sh->dest);
- memcpy(to, daddr, len);
- }
-
- ret = addr;
-out:
- return ret;
-}
/* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb)
@@ -147,8 +94,9 @@ int sctp_rcv(struct sk_buff *skb)
sctp_transport_t *transport = NULL;
sctp_chunk_t *chunk;
struct sctphdr *sh;
- sockaddr_storage_t src;
- sockaddr_storage_t dest;
+ union sctp_addr src;
+ union sctp_addr dest;
+ struct sctp_func *af;
int ret = 0;
if (skb->pkt_type!=PACKET_HOST)
@@ -163,10 +111,15 @@ int sctp_rcv(struct sk_buff *skb)
if (sctp_rcv_checksum(skb) < 0)
goto bad_packet;
- skb_pull(skb, sizeof(struct sctphdr));
+ skb_pull(skb, sizeof(struct sctphdr));
+
+ af = sctp_get_af_specific(ipver2af(skb->nh.iph->version));
+ if (unlikely(!af))
+ goto bad_packet;
- sctp_sockaddr_storage_init(&src, skb, 1);
- sctp_sockaddr_storage_init(&dest, skb, 0);
+ /* Initialize local addresses for lookups. */
+ af->from_skb(&src, skb, 1);
+ af->from_skb(&dest, skb, 0);
/* If the packet is to or from a non-unicast address,
* silently discard the packet.
@@ -179,7 +132,7 @@ int sctp_rcv(struct sk_buff *skb)
* IP broadcast addresses cannot be used in an SCTP transport
* address."
*/
- if (!sctp_addr_is_valid(&src) || !sctp_addr_is_valid(&dest))
+ if (!af->addr_valid(&src) || !af->addr_valid(&dest))
goto discard_it;
asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
@@ -219,7 +172,7 @@ int sctp_rcv(struct sk_buff *skb)
chunk->sctp_hdr = sh;
/* Set the source and destination addresses of the incoming chunk. */
- sctp_init_addrs(chunk);
+ sctp_init_addrs(chunk, &src, &dest);
/* Remember where we came from. */
chunk->transport = transport;
@@ -431,7 +384,7 @@ void sctp_unhash_endpoint(sctp_endpoint_t *ep)
}
/* Look up an endpoint. */
-sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const sockaddr_storage_t *laddr)
+sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr)
{
sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb;
@@ -523,8 +476,8 @@ void __sctp_unhash_established(sctp_association_t *asoc)
}
/* Look up an association. */
-sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr,
- const sockaddr_storage_t *paddr,
+sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr,
+ const union sctp_addr *paddr,
sctp_transport_t **transportp)
{
sctp_hashbucket_t *head;
@@ -559,8 +512,8 @@ hit:
}
/* Look up an association. BH-safe. */
-sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr,
- const sockaddr_storage_t *paddr,
+sctp_association_t *sctp_lookup_association(const union sctp_addr *laddr,
+ const union sctp_addr *paddr,
sctp_transport_t **transportp)
{
sctp_association_t *asoc;
@@ -568,13 +521,13 @@ sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr,
sctp_local_bh_disable();
asoc = __sctp_lookup_association(laddr, paddr, transportp);
sctp_local_bh_enable();
-
+
return asoc;
}
/* Is there an association matching the given local and peer addresses? */
-int sctp_has_association(const sockaddr_storage_t *laddr,
- const sockaddr_storage_t *paddr)
+int sctp_has_association(const union sctp_addr *laddr,
+ const union sctp_addr *paddr)
{
sctp_association_t *asoc;
sctp_transport_t *transport;
@@ -606,21 +559,19 @@ int sctp_has_association(const sockaddr_storage_t *laddr,
* in certain circumstances.
*
*/
-static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
- const sockaddr_storage_t *laddr, sctp_transport_t **transportp)
+static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb,
+ const union sctp_addr *laddr, sctp_transport_t **transportp)
{
sctp_association_t *asoc;
- sockaddr_storage_t addr;
- sockaddr_storage_t *paddr = &addr;
+ union sctp_addr addr;
+ union sctp_addr *paddr = &addr;
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch;
- __u8 *ch_end, *data;
- sctp_paramhdr_t *parm;
+ union sctp_params params;
+ sctp_init_chunk_t *init;
ch = (sctp_chunkhdr_t *) skb->data;
- ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
-
/* If this is INIT/INIT-ACK look inside the chunk too. */
switch (ch->type) {
case SCTP_CID_INIT:
@@ -646,24 +597,17 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters.
*/
- data = skb->data + sizeof(sctp_init_chunk_t);
-
- /* See sctp_process_init() for how to go thru TLVs. */
- while (data < ch_end) {
- parm = (sctp_paramhdr_t *)data;
-
- if (!parm->length)
- break;
+ init = (sctp_init_chunk_t *)skb->data;
- data += WORD_ROUND(ntohs(parm->length));
+ /* Walk the parameters looking for embedded addresses. */
+ sctp_walk_params(params, init, init_hdr.params) {
/* Note: Ignoring hostname addresses. */
- if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) &&
- (SCTP_PARAM_IPV6_ADDRESS != parm->type))
+ if ((SCTP_PARAM_IPV4_ADDRESS != params.p->type) &&
+ (SCTP_PARAM_IPV6_ADDRESS != params.p->type))
continue;
- sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm,
- ntohs(sh->source));
+ sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source));
asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc)
return asoc;
@@ -674,20 +618,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Lookup an association for an inbound skb. */
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
- const sockaddr_storage_t *paddr,
- const sockaddr_storage_t *laddr,
+ const union sctp_addr *paddr,
+ const union sctp_addr *laddr,
sctp_transport_t **transportp)
{
sctp_association_t *asoc;
asoc = __sctp_lookup_association(laddr, paddr, transportp);
- /* Further lookup for INIT-ACK packet.
+ /* Further lookup for INIT/INIT-ACK packets.
* SCTP Implementors Guide, 2.18 Handling of address
* parameters within the INIT or INIT-ACK.
*/
if (!asoc)
- asoc = __sctp_rcv_initack_lookup(skb, laddr, transportp);
+ asoc = __sctp_rcv_init_lookup(skb, laddr, transportp);
return asoc;
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index eb31e378985c..d81ea9f589ec 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -172,8 +172,8 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
-struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr,
- sockaddr_storage_t *saddr)
+struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr,
+ union sctp_addr *saddr)
{
struct dst_entry *dst;
struct flowi fl = { .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr,
@@ -181,7 +181,7 @@ struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr,
SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
- __FUNCTION__, NIP6(fl.fl6_dst));
+ __FUNCTION__, NIP6(fl.fl6_dst));
if (saddr) {
fl.fl6_src = &saddr->v6.sin6_addr;
@@ -206,12 +206,124 @@ struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr,
return dst;
}
+/* Make a copy of all potential local addresses. */
+static void sctp_v6_copy_addrlist(struct list_head *addrlist,
+ struct net_device *dev)
+{
+ struct inet6_dev *in6_dev;
+ struct inet6_ifaddr *ifp;
+ struct sockaddr_storage_list *addr;
+
+ read_lock(&addrconf_lock);
+ if ((in6_dev = __in6_dev_get(dev)) == NULL) {
+ read_unlock(&addrconf_lock);
+ return;
+ }
+
+ read_lock(&in6_dev->lock);
+ for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
+ /* Add the address to the local list. */
+ addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
+ if (addr) {
+ addr->a.v6.sin6_family = AF_INET6;
+ addr->a.v6.sin6_port = 0;
+ addr->a.v6.sin6_addr = ifp->addr;
+ INIT_LIST_HEAD(&addr->list);
+ list_add_tail(&addr->list, addrlist);
+ }
+ }
+
+ read_unlock(&in6_dev->lock);
+ read_unlock(&addrconf_lock);
+}
+
+/* Initialize a sockaddr_storage from in incoming skb. */
+static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb,
+ int is_saddr)
+{
+ void *from;
+ __u16 *port;
+ struct sctphdr *sh;
+
+ port = &addr->v6.sin6_port;
+ addr->v6.sin6_family = AF_INET6;
+ addr->v6.sin6_flowinfo = 0; /* FIXME */
+ addr->v6.sin6_scope_id = 0; /* FIXME */
+
+ sh = (struct sctphdr *) skb->h.raw;
+ if (is_saddr) {
+ *port = ntohs(sh->source);
+ from = &skb->nh.ipv6h->saddr;
+ } else {
+ *port = ntohs(sh->dest);
+ from = &skb->nh.ipv6h->daddr;
+ }
+ ipv6_addr_copy(&addr->v6.sin6_addr, from);
+}
+
/* Check if the dst entry's source addr matches the given source addr. */
-int sctp_v6_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr)
+static int sctp_v6_cmp_saddr(struct dst_entry *dst, union sctp_addr *saddr)
{
struct rt6_info *rt = (struct rt6_info *)dst;
- return ipv6_addr_cmp(&rt->rt6i_src.addr, &saddr->v6.sin6_addr);
+ return ipv6_addr_cmp(&rt->rt6i_src.addr, &saddr->v6.sin6_addr);
+}
+
+/* Initialize addr struct to INADDR_ANY. */
+void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port)
+{
+ memset(addr, 0x00, sizeof(union sctp_addr));
+ addr->v6.sin6_family = AF_INET6;
+ addr->v6.sin6_port = port;
+}
+
+/* This function checks if the address is a valid address to be used for
+ * SCTP.
+ *
+ * Output:
+ * Return 0 - If the address is a non-unicast or an illegal address.
+ * Return 1 - If the address is a unicast.
+ */
+static int sctp_v6_addr_valid(union sctp_addr *addr)
+{
+ int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr);
+
+ /* FIXME: v4-mapped-v6 address support. */
+
+ /* Is this a non-unicast address */
+ if (!(ret & IPV6_ADDR_UNICAST))
+ return 0;
+
+ return 1;
+}
+
+/* What is the scope of 'addr'? */
+static sctp_scope_t sctp_v6_scope(union sctp_addr *addr)
+{
+ int v6scope;
+ sctp_scope_t retval;
+
+ /* The IPv6 scope is really a set of bit fields.
+ * See IFA_* in <net/if_inet6.h>. Map to a generic SCTP scope.
+ */
+
+ v6scope = ipv6_addr_scope(&addr->v6.sin6_addr);
+ switch (v6scope) {
+ case IFA_HOST:
+ retval = SCTP_SCOPE_LOOPBACK;
+ break;
+ case IFA_LINK:
+ retval = SCTP_SCOPE_LINK;
+ break;
+ case IFA_SITE:
+ retval = SCTP_SCOPE_PRIVATE;
+ break;
+ default:
+ retval = SCTP_SCOPE_GLOBAL;
+ break;
+ };
+
+ return retval;
}
/* Initialize a PF_INET6 socket msg_name. */
@@ -227,12 +339,13 @@ static void sctp_inet6_msgname(char *msgname, int *addr_len)
}
/* Initialize a PF_INET msgname from a ulpevent. */
-static void sctp_inet6_event_msgname(sctp_ulpevent_t *event, char *msgname, int *addrlen)
+static void sctp_inet6_event_msgname(sctp_ulpevent_t *event, char *msgname,
+ int *addrlen)
{
struct sockaddr_in6 *sin6, *sin6from;
if (msgname) {
- sockaddr_storage_t *addr;
+ union sctp_addr *addr;
sctp_inet6_msgname(msgname, addrlen);
sin6 = (struct sockaddr_in6 *)msgname;
@@ -288,6 +401,23 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
}
}
+/* Do we support this AF? */
+static int sctp_inet6_af_supported(sa_family_t family)
+{
+ /* FIXME: v4-mapped-v6 addresses. The I-D is still waffling
+ * on what to do with sockaddr formats for PF_INET6 sockets.
+ * For now assume we'll support both.
+ */
+ switch (family) {
+ case AF_INET6:
+ case AF_INET:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
static struct proto_ops inet6_seqpacket_ops = {
.family = PF_INET6,
.release = inet6_release,
@@ -327,7 +457,12 @@ static sctp_func_t sctp_ipv6_specific = {
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
.get_dst = sctp_v6_get_dst,
+ .copy_addrlist = sctp_v6_copy_addrlist,
+ .from_skb = sctp_v6_from_skb,
.cmp_saddr = sctp_v6_cmp_saddr,
+ .scope = sctp_v6_scope,
+ .addr_valid = sctp_v6_addr_valid,
+ .inaddr_any = sctp_v6_inaddr_any,
.net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6,
@@ -336,6 +471,8 @@ static sctp_func_t sctp_ipv6_specific = {
static sctp_pf_t sctp_pf_inet6_specific = {
.event_msgname = sctp_inet6_event_msgname,
.skb_msgname = sctp_inet6_skb_msgname,
+ .af_supported = sctp_inet6_af_supported,
+ .af = &sctp_ipv6_specific,
};
/* Initialize IPv6 support and register with inet6 stack. */
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 5962d3e72e52..9726bbe04246 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -366,18 +366,13 @@ int sctp_packet_transmit(sctp_packet_t *packet)
*/
sh->checksum = htonl(crc32);
+ /* FIXME: Delete the rest of this switch statement once phase 2
+ * of address selection (ipv6 support) drops in.
+ */
switch (transport->ipaddr.sa.sa_family) {
- case AF_INET:
- inet_sk(sk)->daddr = transport->ipaddr.v4.sin_addr.s_addr;
- break;
-
case AF_INET6:
SCTP_V6(inet6_sk(sk)->daddr = transport->ipaddr.v6.sin6_addr;)
break;
-
- default:
- /* This is bogus address type, just bail. */
- break;
};
/* IP layer ECN support
@@ -434,6 +429,8 @@ int sctp_packet_transmit(sctp_packet_t *packet)
}
nskb->dst = dst_clone(transport->dst);
+ if (!nskb->dst)
+ goto no_route;
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
nskb->len);
@@ -441,6 +438,11 @@ int sctp_packet_transmit(sctp_packet_t *packet)
out:
packet->size = SCTP_IP_OVERHEAD;
return err;
+no_route:
+ kfree_skb(nskb);
+ IP_INC_STATS_BH(IpOutNoRoutes);
+ err = -EHOSTUNREACH;
+ goto out;
}
/********************************************************************
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 45708249450a..5c313a6bb804 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -678,7 +678,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
if (!new_transport) {
new_transport = asoc->peer.active_path;
- } else if (!new_transport->state.active) {
+ } else if (!new_transport->active) {
/* If the chunk is Heartbeat, send it to
* chunk->transport, even it's inactive.
*/
@@ -835,7 +835,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/
new_transport = chunk->transport;
if (new_transport == NULL ||
- !new_transport->state.active)
+ !new_transport->active)
new_transport = asoc->peer.active_path;
/* Change packets if necessary. */
@@ -1404,7 +1404,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
/* Mark the destination transport address as
* active if it is not so marked.
*/
- if (!transport->state.active) {
+ if (!transport->active) {
sctp_assoc_control_transport(
transport->asoc,
transport,
diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c
index 5e72c910a390..76db4b56a807 100644
--- a/net/sctp/primitive.c
+++ b/net/sctp/primitive.c
@@ -38,6 +38,7 @@
* La Monte H.P. Yarroll <piggy@acm.org>
* Narasimha Budihal <narasimha@refcode.org>
* Karl Knutson <karl@athena.chicago.il.us>
+ * 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.
@@ -181,6 +182,28 @@ DECLARE_PRIMITIVE(ABORT);
DECLARE_PRIMITIVE(SEND);
+/* 10.1 ULP-to-SCTP
+ * J) Request Heartbeat
+ *
+ * Format: REQUESTHEARTBEAT(association id, destination transport address)
+ *
+ * -> result
+ *
+ * Instructs the local endpoint to perform a HeartBeat on the specified
+ * destination transport address of the given association. The returned
+ * result should indicate whether the transmission of the HEARTBEAT
+ * chunk to the destination address is successful.
+ *
+ * Mandatory attributes:
+ *
+ * o association id - local handle to the SCTP association
+ *
+ * o destination transport address - the transport address of the
+ * asociation on which a heartbeat should be issued.
+ */
+
+DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
+
/* COMMENT BUG. Find out where this is mentioned in the spec. */
int sctp_other_icmp_unreachfrag(sctp_association_t *asoc, void *arg)
{
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 5724996717d7..7891dfdff134 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1,46 +1,46 @@
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001 International Business Machines, Corp.
+ * Copyright (c) 2001-2002 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
- *
+ *
* This file is part of the SCTP kernel reference Implementation
- *
- * Initialization/cleanup for SCTP protocol support.
- *
- * The SCTP reference implementation is free software;
- * you can redistribute it and/or modify it under the terms of
+ *
+ * Initialization/cleanup for SCTP protocol support.
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- *
- * The SCTP reference implementation is distributed in the hope that it
+ *
+ * The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
+ * Boston, MA 02111-1307, USA.
+ *
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
- *
+ *
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
- * Written or modified by:
+ * Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us>
* Jon Grimm <jgrimm@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
- *
+ *
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
@@ -103,8 +103,8 @@ void sctp_proc_exit(void)
/* Private helper to extract ipv4 address and stash them in
* the protocol structure.
*/
-static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
- struct net_device *dev)
+static void sctp_v4_copy_addrlist(struct list_head *addrlist,
+ struct net_device *dev)
{
struct in_device *in_dev;
struct in_ifaddr *ifa;
@@ -117,7 +117,6 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
}
read_lock(&in_dev->lock);
-
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
/* Add the address to the local list. */
addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
@@ -126,7 +125,7 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
addr->a.v4.sin_family = AF_INET;
addr->a.v4.sin_port = 0;
addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
- list_add_tail(&addr->list, &proto->local_addr_list);
+ list_add_tail(&addr->list, addrlist);
}
}
@@ -134,56 +133,21 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
read_unlock(&inetdev_lock);
}
-/* Private helper to extract ipv6 address and stash them in
- * the protocol structure.
- * FIXME: Make this an address family function.
- */
-static inline void sctp_v6_get_local_addr_list(sctp_protocol_t *proto,
- struct net_device *dev)
-{
-#ifdef SCTP_V6_SUPPORT
- /* FIXME: The testframe doesn't support this function. */
-#ifndef TEST_FRAME
- struct inet6_dev *in6_dev;
- struct inet6_ifaddr *ifp;
- struct sockaddr_storage_list *addr;
-
- read_lock(&addrconf_lock);
- if ((in6_dev = __in6_dev_get(dev)) == NULL) {
- read_unlock(&addrconf_lock);
- return;
- }
-
- read_lock_bh(&in6_dev->lock);
- for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
- /* Add the address to the local list. */
- addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
- if (addr) {
- addr->a.v6.sin6_family = AF_INET6;
- addr->a.v6.sin6_port = 0;
- addr->a.v6.sin6_addr = ifp->addr;
- INIT_LIST_HEAD(&addr->list);
- list_add_tail(&addr->list, &proto->local_addr_list);
- }
- }
-
- read_unlock_bh(&in6_dev->lock);
- read_unlock(&addrconf_lock);
-#endif /* TEST_FRAME */
-#endif /* SCTP_V6_SUPPORT */
-}
-
/* Extract our IP addresses from the system and stash them in the
* protocol structure.
*/
static void __sctp_get_local_addr_list(sctp_protocol_t *proto)
{
struct net_device *dev;
+ struct list_head *pos;
+ struct sctp_func *af;
read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
- sctp_v4_get_local_addr_list(proto, dev);
- sctp_v6_get_local_addr_list(proto, dev);
+ list_for_each(pos, &proto->address_families) {
+ af = list_entry(pos, sctp_func_t, list);
+ af->copy_addrlist(&proto->local_addr_list, dev);
+ }
}
read_unlock(&dev_base_lock);
}
@@ -259,13 +223,16 @@ end_copy:
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
-struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *daddr,
- sockaddr_storage_t *saddr)
+struct dst_entry *sctp_v4_get_dst(union sctp_addr *daddr,
+ union sctp_addr *saddr)
{
struct rtable *rt;
- struct flowi fl = { .nl_u = { .ip4_u = { .daddr =
- daddr->v4.sin_addr.s_addr,
- } } };
+ struct flowi fl = {
+ .nl_u = {
+ .ip4_u = { .daddr =
+ daddr->v4.sin_addr.s_addr, }},
+ .proto = IPPROTO_SCTP,
+ };
if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr;
@@ -285,12 +252,88 @@ struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *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)
+{
+ void *from;
+ __u16 *port;
+ struct sctphdr *sh;
+
+ port = &addr->v4.sin_port;
+ addr->v4.sin_family = AF_INET;
+
+ sh = (struct sctphdr *) skb->h.raw;
+ if (is_saddr) {
+ *port = ntohs(sh->source);
+ from = &skb->nh.iph->saddr;
+ } else {
+ *port = ntohs(sh->dest);
+ from = &skb->nh.iph->daddr;
+ }
+ memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr));
+}
+
/* Check if the dst entry's source addr matches the given source addr. */
-int sctp_v4_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr)
+int sctp_v4_cmp_saddr(struct dst_entry *dst, union sctp_addr *saddr)
{
struct rtable *rt = (struct rtable *)dst;
- return (rt->rt_src == saddr->v4.sin_addr.s_addr);
+ return (rt->rt_src == saddr->v4.sin_addr.s_addr);
+}
+
+/* Initialize addr struct to INADDR_ANY. */
+void sctp_v4_inaddr_any(union sctp_addr *addr, unsigned short port)
+{
+ addr->v4.sin_family = AF_INET;
+ addr->v4.sin_addr.s_addr = INADDR_ANY;
+ addr->v4.sin_port = port;
+}
+
+/* This function checks if the address is a valid address to be used for
+ * SCTP.
+ *
+ * Output:
+ * Return 0 - If the address is a non-unicast or an illegal address.
+ * Return 1 - If the address is a unicast.
+ */
+static int sctp_v4_addr_valid(union sctp_addr *addr)
+{
+ /* Is this a non-unicast address or a unusable SCTP address? */
+ if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
+ return 0;
+
+ return 1;
+}
+
+/* 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>.
+ */
+static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
+{
+ sctp_scope_t retval;
+
+ /* Should IPv4 scoping be a sysctl configurable option
+ * so users can turn it off (default on) for certain
+ * unconventional networking environments?
+ */
+
+ /* Check for unusable SCTP addresses. */
+ if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+ retval = SCTP_SCOPE_UNUSABLE;
+ } else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
+ retval = SCTP_SCOPE_LOOPBACK;
+ } else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+ retval = SCTP_SCOPE_LINK;
+ } else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
+ retval = SCTP_SCOPE_PRIVATE;
+ } else {
+ retval = SCTP_SCOPE_GLOBAL;
+ }
+
+ return retval;
}
/* Event handler for inet device events.
@@ -336,11 +379,11 @@ int sctp_ctl_sock_init(void)
/* Get the table of functions for manipulating a particular address
* family.
*/
-sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address)
+sctp_func_t *sctp_get_af_specific(sa_family_t family)
{
struct list_head *pos;
sctp_protocol_t *proto = sctp_get_protocol();
- sctp_func_t *retval, *af;
+ struct sctp_func *retval, *af;
retval = NULL;
@@ -349,7 +392,7 @@ sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address)
*/
list_for_each(pos, &proto->address_families) {
af = list_entry(pos, sctp_func_t, list);
- if (address->sa.sa_family == af->sa_family) {
+ if (family == af->sa_family) {
retval = af;
break;
}
@@ -362,13 +405,13 @@ sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address)
static void sctp_inet_msgname(char *msgname, int *addr_len)
{
struct sockaddr_in *sin;
-
+
sin = (struct sockaddr_in *)msgname;
*addr_len = sizeof(struct sockaddr_in);
sin->sin_family = AF_INET;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
-
+
/* Copy the primary address of the peer primary address as the msg_name. */
static void sctp_inet_event_msgname(sctp_ulpevent_t *event, char *msgname, int *addr_len)
{
@@ -381,26 +424,37 @@ static void sctp_inet_event_msgname(sctp_ulpevent_t *event, char *msgname, int *
sin->sin_port = htons(event->asoc->peer.port);
sin->sin_addr.s_addr = sinfrom->sin_addr.s_addr;
}
-}
+}
/* Initialize and copy out a msgname from an inbound skb. */
-static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *addr_len)
+static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *len)
{
- struct sctphdr *sh;
+ struct sctphdr *sh;
struct sockaddr_in *sin;
if (msgname) {
- sctp_inet_msgname(msgname, addr_len);
+ sctp_inet_msgname(msgname, len);
sin = (struct sockaddr_in *)msgname;
sh = (struct sctphdr *)skb->h.raw;
sin->sin_port = sh->source;
sin->sin_addr.s_addr = skb->nh.iph->saddr;
}
-}
+}
+
+/* Do we support this AF? */
+static int sctp_inet_af_supported(sa_family_t family)
+{
+ /* PF_INET only supports AF_INET addresses. */
+ return (AF_INET == family);
+}
+
+struct sctp_func sctp_ipv4_specific;
static sctp_pf_t sctp_pf_inet = {
.event_msgname = sctp_inet_event_msgname,
- .skb_msgname = sctp_inet_skb_msgname,
+ .skb_msgname = sctp_inet_skb_msgname,
+ .af_supported = sctp_inet_af_supported,
+ .af = &sctp_ipv4_specific,
};
@@ -448,12 +502,17 @@ static struct inet_protocol sctp_protocol = {
};
/* IPv4 address related functions. */
-sctp_func_t sctp_ipv4_specific = {
+struct sctp_func 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,
.cmp_saddr = sctp_v4_cmp_saddr,
+ .addr_valid = sctp_v4_addr_valid,
+ .inaddr_any = sctp_v4_inaddr_any,
+ .scope = sctp_v4_scope,
.net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET,
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index fc0404a5880c..288f4ec6edb4 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1,8 +1,8 @@
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001 Intel Corp.
- * Copyright (c) 2001 International Business Machines Corp.
+ * Copyright (c) 2001-2002 Intel Corp.
+ * Copyright (c) 2001-2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
@@ -166,7 +166,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
int priority)
{
sctp_inithdr_t init;
- sctpParam_t addrs;
+ union sctp_params addrs;
size_t chunksize;
sctp_chunk_t *retval = NULL;
int addrs_len = 0;
@@ -228,7 +228,7 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
{
sctp_inithdr_t initack;
sctp_chunk_t *retval;
- sctpParam_t addrs;
+ union sctp_params addrs;
int addrs_len;
sctp_cookie_param_t *cookie;
int cookie_len;
@@ -1031,51 +1031,15 @@ nodata:
}
/* Set chunk->source and dest based on the IP header in chunk->skb. */
-void sctp_init_addrs(sctp_chunk_t *chunk)
+void sctp_init_addrs(sctp_chunk_t *chunk, union sctp_addr *src,
+ union sctp_addr *dest)
{
- sockaddr_storage_t *source, *dest;
- struct sk_buff *skb;
- struct sctphdr *sh;
- struct iphdr *ih4;
- struct ipv6hdr *ih6;
-
- source = &chunk->source;
- dest = &chunk->dest;
- skb = chunk->skb;
- ih4 = skb->nh.iph;
- ih6 = skb->nh.ipv6h;
- sh = chunk->sctp_hdr;
-
- switch (ih4->version) {
- case 4:
- source->v4.sin_family = AF_INET;
- source->v4.sin_port = ntohs(sh->source);
- source->v4.sin_addr.s_addr = ih4->saddr;
- dest->v4.sin_family = AF_INET;
- dest->v4.sin_port = ntohs(sh->dest);
- dest->v4.sin_addr.s_addr = ih4->daddr;
- break;
-
- case 6:
- SCTP_V6(
- source->v6.sin6_family = AF_INET6;
- source->v6.sin6_port = ntohs(sh->source);
- source->v6.sin6_addr = ih6->saddr;
- dest->v6.sin6_family = AF_INET6;
- dest->v6.sin6_port = ntohs(sh->dest);
- dest->v6.sin6_addr = ih6->daddr;
- /* FIXME: What do we do with scope, etc. ? */
- break;
- )
-
- default:
- /* This is a bogus address type, just bail. */
- break;
- };
+ memcpy(&chunk->source, src, sizeof(union sctp_addr));
+ memcpy(&chunk->dest, dest, sizeof(union sctp_addr));
}
/* Extract the source address from a chunk. */
-const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk)
+const union sctp_addr *sctp_source(const sctp_chunk_t *chunk)
{
/* If we have a known transport, use that. */
if (chunk->transport) {
@@ -1482,78 +1446,28 @@ malformed:
* 3rd Level Abstractions
********************************************************************/
-/* Verify the INIT packet before we process it. */
-int sctp_verify_init(const sctp_association_t *asoc,
- sctp_cid_t cid,
- sctp_init_chunk_t *peer_init,
- sctp_chunk_t *chunk,
- sctp_chunk_t **err_chk_p)
-{
- sctpParam_t param;
- uint8_t *end;
-
- /* FIXME - Verify the fixed fields of the INIT chunk. Also, verify
- * the mandatory parameters somewhere here and generate either the
- * "Missing mandatory parameter" error or the "Invalid mandatory
- * parameter" error. */
-
- /* Find unrecognized parameters. */
-
- end = ((uint8_t *)peer_init + ntohs(peer_init->chunk_hdr.length));
-
- for (param.v = peer_init->init_hdr.params;
- param.v < end;
- param.v += WORD_ROUND(ntohs(param.p->length))) {
-
- if (!sctp_verify_param(asoc, param, cid, chunk, err_chk_p))
- return 0;
-
- } /* for (loop through all parameters) */
-
- return 1;
-}
-
-
-/* Find unrecognized parameters in the chunk.
- * Return values:
- * 0 - discard the chunk
- * 1 - continue with the chunk
+/* Do not attempt to handle the HOST_NAME parm. However, do
+ * send back an indicator to the peer.
*/
-int sctp_verify_param(const sctp_association_t *asoc,
- sctpParam_t param,
- sctp_cid_t cid,
- sctp_chunk_t *chunk,
- sctp_chunk_t **err_chk_p)
+static int sctp_process_hn_param(const sctp_association_t *asoc,
+ union sctp_params param,
+ sctp_chunk_t *chunk,
+ sctp_chunk_t **err_chk_p)
{
- int retval = 1;
+ __u16 len = ntohs(param.p->length);
- /* FIXME - This routine is not looking at each parameter per the
- * chunk type, i.e., unrecognized parameters should be further
- * identified based on the chunk id.
+ /* Make an ERROR chunk, preparing enough room for
+ * returning multiple unknown parameters.
*/
+ if (!*err_chk_p)
+ *err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
- switch (param.p->type) {
- case SCTP_PARAM_IPV4_ADDRESS:
- case SCTP_PARAM_IPV6_ADDRESS:
- case SCTP_PARAM_COOKIE_PRESERVATIVE:
- /* FIXME - If we don't support the host name parameter, we should
- * generate an error for this - Unresolvable address.
- */
- case SCTP_PARAM_HOST_NAME_ADDRESS:
- case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
- case SCTP_PARAM_STATE_COOKIE:
- case SCTP_PARAM_HEARTBEAT_INFO:
- case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
- case SCTP_PARAM_ECN_CAPABLE:
- break;
- default:
- SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
- ntohs(param.p->type), cid);
- return sctp_process_unk_param(asoc, param, chunk, err_chk_p);
+ if (*err_chk_p)
+ sctp_init_cause(*err_chk_p, SCTP_ERROR_DNS_FAILED,
+ param.v, len);
- break;
- }
- return retval;
+ /* Stop processing this chunk. */
+ return 0;
}
/* RFC 3.2.1 & the Implementers Guide 2.2.
@@ -1582,10 +1496,10 @@ int sctp_verify_param(const sctp_association_t *asoc,
* 0 - discard the chunk
* 1 - continue with the chunk
*/
-int sctp_process_unk_param(const sctp_association_t *asoc,
- sctpParam_t param,
- sctp_chunk_t *chunk,
- sctp_chunk_t **err_chk_p)
+static int sctp_process_unk_param(const sctp_association_t *asoc,
+ union sctp_params param,
+ sctp_chunk_t *chunk,
+ sctp_chunk_t **err_chk_p)
{
int retval = 1;
@@ -1604,7 +1518,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
- (const void *)param.p,
+ param.v,
WORD_ROUND(ntohs(param.p->length)));
break;
@@ -1620,7 +1534,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
if (*err_chk_p) {
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
- (const void *)param.p,
+ param.v,
WORD_ROUND(ntohs(param.p->length)));
} else {
/* If there is no memory for generating the ERROR
@@ -1638,17 +1552,84 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
return retval;
}
-/* Unpack the parameters in an INIT packet.
- * FIXME: There is no return status to allow callers to do
- * error handling.
+/* Find unrecognized parameters in the chunk.
+ * Return values:
+ * 0 - discard the chunk
+ * 1 - continue with the chunk
*/
-void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
- const sockaddr_storage_t *peer_addr,
- sctp_init_chunk_t *peer_init,
- int priority)
+static int sctp_verify_param(const sctp_association_t *asoc,
+ union sctp_params param,
+ sctp_cid_t cid,
+ sctp_chunk_t *chunk,
+ sctp_chunk_t **err_chunk)
{
- sctpParam_t param;
- __u8 *end;
+ int retval = 1;
+
+ /* FIXME - This routine is not looking at each parameter per the
+ * chunk type, i.e., unrecognized parameters should be further
+ * identified based on the chunk id.
+ */
+
+ switch (param.p->type) {
+ case SCTP_PARAM_IPV4_ADDRESS:
+ case SCTP_PARAM_IPV6_ADDRESS:
+ case SCTP_PARAM_COOKIE_PRESERVATIVE:
+ case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
+ case SCTP_PARAM_STATE_COOKIE:
+ case SCTP_PARAM_HEARTBEAT_INFO:
+ case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
+ case SCTP_PARAM_ECN_CAPABLE:
+ break;
+
+ case SCTP_PARAM_HOST_NAME_ADDRESS:
+ /* Tell the peer, we won't support this param. */
+ return sctp_process_hn_param(asoc, param, chunk, err_chunk);
+ default:
+ SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
+ ntohs(param.p->type), cid);
+ return sctp_process_unk_param(asoc, param, chunk, err_chunk);
+
+ break;
+ }
+ return retval;
+}
+
+/* Verify the INIT packet before we process it. */
+int sctp_verify_init(const sctp_association_t *asoc,
+ sctp_cid_t cid,
+ sctp_init_chunk_t *peer_init,
+ sctp_chunk_t *chunk,
+ sctp_chunk_t **err_chk_p)
+{
+ union sctp_params param;
+
+ /* FIXME - Verify the fixed fields of the INIT chunk. Also, verify
+ * the mandatory parameters somewhere here and generate either the
+ * "Missing mandatory parameter" error or the "Invalid mandatory
+ * parameter" error.
+ */
+
+ /* Find unrecognized parameters. */
+
+ sctp_walk_params(param, peer_init, init_hdr.params) {
+
+ if (!sctp_verify_param(asoc, param, cid, chunk, err_chk_p))
+ return 0;
+
+ } /* for (loop through all parameters) */
+
+ return 1;
+}
+
+/* Unpack the parameters in an INIT packet into an association.
+ * Returns 0 on failure, else success.
+ */
+int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
+ const union sctp_addr *peer_addr,
+ sctp_init_chunk_t *peer_init,
+ int priority)
+{
+ union sctp_params param;
sctp_transport_t *transport;
struct list_head *pos, *temp;
char *cookie;
@@ -1664,15 +1645,14 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* be a a better choice than any of the embedded addresses.
*/
if (peer_addr)
- sctp_assoc_add_peer(asoc, peer_addr, priority);
+ if(!sctp_assoc_add_peer(asoc, peer_addr, priority))
+ goto nomem;
/* Process the initialization parameters. */
- end = ((__u8 *)peer_init + ntohs(peer_init->chunk_hdr.length));
- for (param.v = peer_init->init_hdr.params;
- param.v < end;
- param.v += WORD_ROUND(ntohs(param.p->length))) {
- if (!sctp_process_param(asoc, param, peer_addr, cid,
- priority))
+
+ sctp_walk_params(param, peer_init, init_hdr.params) {
+
+ if (!sctp_process_param(asoc, param, peer_addr, priority))
goto clean_up;
}
@@ -1738,7 +1718,7 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* association to the same value as the Initial TSN.
*/
asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
- return;
+ return 1;
clean_up:
/* Release the transport structures. */
@@ -1747,8 +1727,11 @@ clean_up:
list_del(pos);
sctp_transport_free(transport);
}
+nomem:
+ return 0;
}
+
/* Update asoc with the option described in param.
*
* RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT
@@ -1760,14 +1743,12 @@ clean_up:
* work we do. In particular, we should not build transport
* structures for the addresses.
*/
-int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
- const sockaddr_storage_t *peer_addr,
- sctp_cid_t cid, int priority)
+int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
+ const union sctp_addr *peer_addr, int priority)
{
- sockaddr_storage_t addr;
- sctp_addr_param_t *addrparm;
- int j;
+ union sctp_addr addr;
int i;
+ __u16 sat;
int retval = 1;
sctp_scope_t scope;
@@ -1776,30 +1757,21 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
* came from a fresh INIT, and INIT ACK, or were stored in a cookie.
*/
switch (param.p->type) {
+ case SCTP_PARAM_IPV6_ADDRESS:
+ if( PF_INET6 != asoc->base.sk->family)
+ break;
+ /* Fall through. */
case SCTP_PARAM_IPV4_ADDRESS:
- addrparm = (sctp_addr_param_t *)param.v;
- sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
+ sctp_param2sockaddr(&addr, param.addr, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
- sctp_assoc_add_peer(asoc, &addr, priority);
- break;
-
- case SCTP_PARAM_IPV6_ADDRESS:
- /* Rethink this as we may need to keep for
- * restart considerations.
- */
- if (PF_INET6 == asoc->base.sk->family) {
- addrparm = (sctp_addr_param_t *)param.v;
- sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
- scope = sctp_scope(peer_addr);
- if (sctp_in_scope(&addr, scope))
- sctp_assoc_add_peer(asoc, &addr, priority);
- }
+ if (!sctp_assoc_add_peer(asoc, &addr, priority))
+ return 0;
break;
case SCTP_PARAM_COOKIE_PRESERVATIVE:
asoc->cookie_preserve =
- ntohl(param.bht->lifespan_increment);
+ ntohl(param.life->lifespan_increment);
break;
case SCTP_PARAM_HOST_NAME_ADDRESS:
@@ -1813,10 +1785,12 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
asoc->peer.ipv4_address = 0;
asoc->peer.ipv6_address = 0;
- j = (ntohs(param.p->length) -
- sizeof(sctp_paramhdr_t)) /
- sizeof(__u16);
- for (i = 0; i < j; ++i) {
+ /* Cycle through address types; avoid divide by 0. */
+ sat = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
+ if (sat)
+ sat /= sizeof(__u16);
+
+ for (i = 0; i < sat; ++i) {
switch (param.sat->types[i]) {
case SCTP_PARAM_IPV4_ADDRESS:
asoc->peer.ipv4_address = 1;
@@ -1843,13 +1817,11 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
break;
case SCTP_PARAM_HEARTBEAT_INFO:
- SCTP_DEBUG_PRINTK("unimplemented "
- "SCTP_PARAM_HEARTBEAT_INFO\n");
+ /* Would be odd to receive, but it causes no problems. */
break;
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
- SCTP_DEBUG_PRINTK("unimplemented "
- "SCTP_PARAM_UNRECOGNIZED_PARAMETERS\n");
+ /* Rejected during verify stage. */
break;
case SCTP_PARAM_ECN_CAPABLE:
@@ -1898,8 +1870,8 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
* 4th Level Abstractions
********************************************************************/
-/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */
-void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param,
+/* Convert from an SCTP IP parameter to a union sctp_addr. */
+void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *param,
__u16 port)
{
switch(param->v4.param_hdr.type) {
@@ -1926,11 +1898,8 @@ void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param,
/* Convert an IP address in an SCTP param into a sockaddr_in. */
/* Returns true if a valid conversion was possible. */
-int sctp_addr2sockaddr(sctpParam_t p, sockaddr_storage_t *sa)
+int sctp_addr2sockaddr(union sctp_params p, union sctp_addr *sa)
{
- if (!p.v)
- return 0;
-
switch (p.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr);
@@ -1950,30 +1919,10 @@ int sctp_addr2sockaddr(sctpParam_t p, sockaddr_storage_t *sa)
return 1;
}
-/* Convert from an IP version number to an Address Family symbol. */
-int ipver2af(__u8 ipver)
-{
- int family;
-
- switch (ipver) {
- case 4:
- family = AF_INET;
- break;
- case 6:
- family = AF_INET6;
- break;
- default:
- family = 0;
- break;
- };
-
- return family;
-}
-
/* Convert a sockaddr_in to an IP address in an SCTP param.
- * Returns len if a valid conversion was possible.
+ * Returns len if a valid conversion was possible.
*/
-int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctp_addr_param_t *p)
+int sockaddr2sctp_addr(const union sctp_addr *sa, sctp_addr_param_t *p)
{
int len = 0;
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 81e11238fd1e..69ca9dccc364 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -41,6 +41,7 @@
* Dajiang Zhang <dajiang.zhang@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.
@@ -68,11 +69,13 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
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);
-static void sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
- sctp_chunk_t *chunk,
- sctp_init_chunk_t *peer_init,
- int priority);
+static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
+ sctp_chunk_t *chunk,
+ sctp_init_chunk_t *peer_init,
+ int priority);
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *);
+static void sctp_cmd_hb_timers_update(sctp_cmd_seq_t *, sctp_association_t *,
+ sctp_transport_t *);
static void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *, sctp_association_t *,
sctp_bind_addr_t *);
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *,
@@ -83,6 +86,8 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *,
sctp_sackhdr_t *);
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *,
sctp_chunk_t *);
+static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *,
+ sctp_state_t);
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
@@ -193,6 +198,7 @@ int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
/* BUG--we should now recover some memory, probably by
* reneging...
*/
+ error = -ENOMEM;
break;
case SCTP_DISPOSITION_DELETE_TCB:
@@ -301,8 +307,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_NEW_STATE:
/* Enter a new state. */
- asoc->state = command->obj.state;
- asoc->state_timestamp = jiffies;
+ sctp_cmd_new_state(commands, asoc, command->obj.state);
break;
case SCTP_CMD_REPORT_TSN:
@@ -339,9 +344,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_CMD_PEER_INIT:
- /* Process a unified INIT from the peer. */
- sctp_cmd_process_init(commands, asoc, chunk,
- command->obj.ptr, priority);
+ /* Process a unified INIT from the peer.
+ * Note: Only used during INIT-ACK processing. If
+ * there is an error just return to the outter
+ * layer which will bail.
+ */
+ error = sctp_cmd_process_init(commands, asoc, chunk,
+ command->obj.ptr,
+ priority);
break;
case SCTP_CMD_GEN_COOKIE_ECHO:
@@ -561,6 +571,11 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_hb_timers_start(commands, asoc);
break;
+ case SCTP_CMD_HB_TIMERS_UPDATE:
+ t = command->obj.transport;
+ sctp_cmd_hb_timers_update(commands, asoc, t);
+ break;
+
case SCTP_CMD_REPORT_ERROR:
error = command->obj.error;
break;
@@ -581,11 +596,18 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
chunk->pdiscard = 1;
break;
+ case SCTP_CMD_RTO_PENDING:
+ t = command->obj.transport;
+ t->rto_pending = 1;
+ break;
+
default:
printk(KERN_WARNING "Impossible command: %u, %p\n",
command->verb, command->obj.ptr);
break;
};
+ if (error)
+ return error;
}
return error;
@@ -648,7 +670,7 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
}
/* Always try to quiet the other end. In case of lost CWR,
- * resend last_cwr_tsn.
+ * resend last_cwr_tsn.
*/
repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk);
@@ -978,7 +1000,7 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
*/
asoc->overall_error_count++;
- if (transport->state.active &&
+ if (transport->active &&
(transport->error_count++ >= transport->error_threshold)) {
SCTP_DEBUG_PRINTK("transport_strike: transport "
"IP:%d.%d.%d.%d failed.\n",
@@ -1058,22 +1080,32 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
}
/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT
- * inside the cookie.
+ * inside the cookie. In reality, this is only used for INIT-ACK processing
+ * since all other cases use "temporary" associations and can do all
+ * their work in statefuns directly.
*/
-static void sctp_cmd_process_init(sctp_cmd_seq_t *commands,
- sctp_association_t *asoc,
- sctp_chunk_t *chunk,
- sctp_init_chunk_t *peer_init,
- int priority)
+static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
+ sctp_association_t *asoc,
+ sctp_chunk_t *chunk,
+ sctp_init_chunk_t *peer_init,
+ int priority)
{
- /* The command sequence holds commands assuming that the
- * processing will happen successfully. If this is not the
- * case, rewind the sequence and add appropriate error handling
- * to the sequence.
+ int error;
+
+ /* We only process the init as a sideeffect in a single
+ * case. This is when we process the INIT-ACK. If we
+ * fail during INIT processing (due to malloc problems),
+ * just return the error and stop processing the stack.
*/
- sctp_process_init(asoc, chunk->chunk_hdr->type,
- sctp_source(chunk), peer_init,
- priority);
+
+ if (!sctp_process_init(asoc, chunk->chunk_hdr->type,
+ sctp_source(chunk), peer_init,
+ priority))
+ error = -ENOMEM;
+ else
+ error = 0;
+
+ return error;
}
/* Helper function to break out starting up of heartbeat timers. */
@@ -1096,6 +1128,16 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
}
}
+/* Helper function to update the heartbeat timer. */
+static void sctp_cmd_hb_timers_update(sctp_cmd_seq_t *cmds,
+ sctp_association_t *asoc,
+ sctp_transport_t *t)
+{
+ /* Update the heartbeat timer. */
+ if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies))
+ sctp_transport_hold(t);
+}
+
/* Helper function to break out SCTP_CMD_SET_BIND_ADDR handling. */
void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_bind_addr_t *bp)
@@ -1131,7 +1173,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
/* Mark the destination transport address as active if it is not so
* marked.
*/
- if (!t->state.active)
+ if (!t->active)
sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
SCTP_HEARTBEAT_SUCCESS);
@@ -1154,10 +1196,6 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
/* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc, t);
-
- /* Update the heartbeat timer. */
- if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies))
- sctp_transport_hold(t);
}
/* Helper function to process the process SACK command. */
@@ -1196,3 +1234,18 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
chunk->transport = t;
}
+
+/* Helper function to change the state of an association. */
+static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
+ sctp_state_t state)
+{
+ asoc->state = state;
+ asoc->state_timestamp = jiffies;
+
+ /* Wake up any process waiting for the association to
+ * get established.
+ */
+ if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
+ (waitqueue_active(&asoc->wait)))
+ wake_up_interruptible(&asoc->wait);
+}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index e0e39d0d9d1f..4ddd1146bf5a 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -236,20 +236,18 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data;
/* Tag the variable length parameters. */
- chunk->param_hdr.v =
- skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
+ chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC);
if (!new_asoc)
goto nomem;
- /* FIXME: sctp_process_init can fail, but there is no
- * status nor handling.
- */
- sctp_process_init(new_asoc, chunk->chunk_hdr->type,
- sctp_source(chunk),
- (sctp_init_chunk_t *)chunk->chunk_hdr,
- GFP_ATOMIC);
+ /* The call, sctp_process_init(), can fail on memory allocation. */
+ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+ sctp_source(chunk),
+ (sctp_init_chunk_t *)chunk->chunk_hdr,
+ GFP_ATOMIC))
+ goto nomem_init;
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
@@ -302,10 +300,10 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_DELETE_TCB;
nomem_ack:
- sctp_association_free(new_asoc);
if (err_chunk)
sctp_free_chunk(err_chunk);
-
+nomem_init:
+ sctp_association_free(new_asoc);
nomem:
return SCTP_DISPOSITION_NOMEM;
}
@@ -563,9 +561,11 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
* effects--it is safe to run them here.
*/
peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
- sctp_process_init(new_asoc, chunk->chunk_hdr->type,
- &chunk->subh.cookie_hdr->c.peer_addr, peer_init,
- GFP_ATOMIC);
+
+ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+ &chunk->subh.cookie_hdr->c.peer_addr,
+ peer_init, GFP_ATOMIC))
+ goto nomem_init;
repl = sctp_make_cookie_ack(new_asoc, chunk);
if (!repl)
@@ -592,10 +592,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
nomem_ev:
sctp_free_chunk(repl);
-
nomem_repl:
+nomem_init:
sctp_association_free(new_asoc);
-
nomem:
return SCTP_DISPOSITION_NOMEM;
}
@@ -664,6 +663,39 @@ nomem:
return SCTP_DISPOSITION_NOMEM;
}
+/* Generate and sendout a heartbeat packet. */
+sctp_disposition_t sctp_sf_heartbeat(const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_subtype_t type,
+ void *arg,
+ sctp_cmd_seq_t *commands)
+{
+ sctp_transport_t *transport = (sctp_transport_t *) arg;
+ sctp_chunk_t *reply;
+ sctp_sender_hb_info_t hbinfo;
+ size_t paylen = 0;
+
+ hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
+ hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
+ hbinfo.daddr = transport->ipaddr;
+ hbinfo.sent_at = jiffies;
+
+ /* Send a heartbeat to our peer. */
+ paylen = sizeof(sctp_sender_hb_info_t);
+ reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
+ if (!reply)
+ return SCTP_DISPOSITION_NOMEM;
+
+ /* Set rto_pending indicating that an RTT measurement
+ * is started with this heartbeat chunk.
+ */
+ sctp_add_cmd_sf(commands, SCTP_CMD_RTO_PENDING,
+ SCTP_TRANSPORT(transport));
+
+ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+ return SCTP_DISPOSITION_CONSUME;
+}
+
/* Generate a HEARTBEAT packet on the given transport. */
sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
@@ -672,9 +704,6 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *transport = (sctp_transport_t *) arg;
- sctp_chunk_t *reply;
- sctp_sender_hb_info_t hbinfo;
- size_t paylen = 0;
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
@@ -689,34 +718,21 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
* HEARTBEAT is sent (see Section 8.3).
*/
- hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
- hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
- hbinfo.daddr = transport->ipaddr;
- hbinfo.sent_at = jiffies;
-
- /* Set rto_pending indicating that an RTT measurement is started
- * with this heartbeat chunk.
- */
- transport->rto_pending = 1;
-
- /* Send a heartbeat to our peer. */
- paylen = sizeof(sctp_sender_hb_info_t);
- reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
- if (!reply)
- goto nomem;
-
- sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
-
- /* Set transport error counter and association error counter
- * when sending heartbeat.
- */
- sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
+ if (transport->hb_allowed) {
+ if (SCTP_DISPOSITION_NOMEM ==
+ sctp_sf_heartbeat(ep, asoc, type, arg,
+ commands))
+ return SCTP_DISPOSITION_NOMEM;
+ /* Set transport error counter and association error counter
+ * when sending heartbeat.
+ */
+ sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
+ SCTP_TRANSPORT(transport));
+ }
+ sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_UPDATE,
SCTP_TRANSPORT(transport));
return SCTP_DISPOSITION_CONSUME;
-
-nomem:
- return SCTP_DISPOSITION_NOMEM;
}
/*
@@ -817,7 +833,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands)
{
sctp_chunk_t *chunk = arg;
- sockaddr_storage_t from_addr;
+ union sctp_addr from_addr;
sctp_transport_t *link;
sctp_sender_hb_info_t *hbinfo;
unsigned long max_interval;
@@ -866,7 +882,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
/* Helper function to send out an abort for the restart
* condition.
*/
-static int sctp_sf_send_restart_abort(sockaddr_storage_t *ssa,
+static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
sctp_chunk_t *init,
sctp_cmd_seq_t *commands)
{
@@ -1125,8 +1141,13 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* Verification Tag and Peers Verification tag into a reserved
* place (local tie-tag and per tie-tag) within the state cookie.
*/
- sctp_process_init(new_asoc, chunk->chunk_hdr->type, sctp_source(chunk),
- (sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC);
+ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+ sctp_source(chunk),
+ (sctp_init_chunk_t *)chunk->chunk_hdr,
+ GFP_ATOMIC)) {
+ retval = SCTP_DISPOSITION_NOMEM;
+ goto nomem_init;
+ }
/* Make sure no new addresses are being added during the
* restart. Do not do this check for COOKIE-WAIT state,
@@ -1197,6 +1218,7 @@ cleanup:
nomem:
retval = SCTP_DISPOSITION_NOMEM;
goto cleanup;
+nomem_init:
cleanup_asoc:
sctp_association_free(new_asoc);
goto cleanup;
@@ -1326,15 +1348,16 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
* side effects--it is safe to run them here.
*/
peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
- sctp_process_init(new_asoc, chunk->chunk_hdr->type,
- sctp_source(chunk), peer_init, GFP_ATOMIC);
+
+ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+ sctp_source(chunk), peer_init, GFP_ATOMIC))
+ goto nomem;
/* Make sure no new addresses are being added during the
* restart. Though this is a pretty complicated attack
* since you'd have to get inside the cookie.
*/
if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
- printk("cookie echo check\n");
return SCTP_DISPOSITION_CONSUME;
}
@@ -1391,8 +1414,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const sctp_endpoint_t *ep,
* side effects--it is safe to run them here.
*/
peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
- sctp_process_init(new_asoc, chunk->chunk_hdr->type,
- sctp_source(chunk), peer_init, GFP_ATOMIC);
+ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
+ sctp_source(chunk), peer_init, GFP_ATOMIC))
+ goto nomem;
/* Update the content of current association. */
sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
@@ -3657,6 +3681,39 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
}
/*
+ * Process the REQUESTHEARTBEAT primitive
+ *
+ * 10.1 ULP-to-SCTP
+ * J) Request Heartbeat
+ *
+ * Format: REQUESTHEARTBEAT(association id, destination transport address)
+ *
+ * -> result
+ *
+ * Instructs the local endpoint to perform a HeartBeat on the specified
+ * destination transport address of the given association. The returned
+ * result should indicate whether the transmission of the HEARTBEAT
+ * chunk to the destination address is successful.
+ *
+ * Mandatory attributes:
+ *
+ * o association id - local handle to the SCTP association
+ *
+ * o destination transport address - the transport address of the
+ * asociation on which a heartbeat should be issued.
+ */
+sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
+ const sctp_endpoint_t *ep,
+ const sctp_association_t *asoc,
+ const sctp_subtype_t type,
+ void *arg,
+ sctp_cmd_seq_t *commands)
+{
+ return sctp_sf_heartbeat(ep, asoc, type, (sctp_transport_t *)arg,
+ commands);
+}
+
+/*
* Ignore the primitive event
*
* The return value is the disposition of the primitive.
@@ -4257,7 +4314,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
/* Cache a route for the transport with the chunk's destination as
* the source address.
*/
- sctp_transport_route(transport, (sockaddr_storage_t *)&chunk->dest);
+ sctp_transport_route(transport, (union sctp_addr *)&chunk->dest);
packet = sctp_packet_init(packet, transport, sport, dport);
packet = sctp_packet_config(packet, vtag, 0, NULL);
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index 8d61ad763f83..e0744f1f463a 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -39,6 +39,7 @@
* Jon Grimm <jgrimm@us.ibm.com>
* Hui Huang <hui.huang@nokia.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.
@@ -706,21 +707,28 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_ESTABLISHED */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
- {.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
+ {.fn = sctp_sf_do_prm_requestheartbeat, \
+ .name = "sctp_sf_do_prm_requestheartbeat"}, \
} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_GETSRTTREPORT { \
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index ab4bf2a9c3f0..a9bfe566754e 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -86,15 +86,16 @@ static void sctp_wfree(struct sk_buff *skb);
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 sockaddr_storage_t *newaddr,
- sockaddr_storage_t *saveaddr);
+ const union sctp_addr *newaddr,
+ union sctp_addr *saveaddr);
static inline void sctp_sk_addr_restore(struct sock *,
- const sockaddr_storage_t *);
-static inline int sctp_sendmsg_verify_name(struct sock *, struct msghdr *);
+ const union sctp_addr *);
+static inline int sctp_verify_addr(struct sock *, struct sockaddr *, 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 *, sockaddr_storage_t *, int);
+static int sctp_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk);
@@ -122,7 +123,7 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
/* Disallow binding twice. */
if (!sctp_sk(sk)->ep->base.bind_addr.port)
- retval = sctp_do_bind(sk, (sockaddr_storage_t *)uaddr,
+ retval = sctp_do_bind(sk, (union sctp_addr *)uaddr,
addr_len);
else
retval = -EINVAL;
@@ -135,14 +136,14 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
static long sctp_get_port_local(struct sock *, unsigned short);
/* Bind a local address either to an endpoint or to an association. */
-SCTP_STATIC int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr,
+SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *newaddr,
int addr_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;
- sockaddr_storage_t tmpaddr, saveaddr;
+ union sctp_addr tmpaddr, saveaddr;
unsigned short *snum;
int ret = 0;
@@ -403,7 +404,7 @@ int sctp_bindx_add(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
goto err_bindx_add;
};
- retval = sctp_do_bind(sk, (sockaddr_storage_t *)&addrs[cnt],
+ retval = sctp_do_bind(sk, (union sctp_addr *)&addrs[cnt],
addr_len);
err_bindx_add:
@@ -481,7 +482,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
int cnt;
sctp_bind_addr_t *bp = &ep->base.bind_addr;
int retval = 0;
- sockaddr_storage_t saveaddr;
+ union sctp_addr saveaddr;
SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
sk, addrs, addrcnt);
@@ -500,7 +501,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
*/
switch (((struct sockaddr *)&addrs[cnt])->sa_family) {
case AF_INET:
- saveaddr = *((sockaddr_storage_t *)
+ saveaddr = *((union sctp_addr *)
&addrs[cnt]);
saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
/* Verify the port. */
@@ -511,7 +512,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
break;
case AF_INET6:
- saveaddr = *((sockaddr_storage_t *)
+ saveaddr = *((union sctp_addr *)
&addrs[cnt]);
saveaddr.v6.sin6_port =
ntohs(saveaddr.v6.sin6_port);
@@ -741,7 +742,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport;
sctp_chunk_t *chunk = NULL;
- sockaddr_storage_t to;
+ union sctp_addr to;
struct sockaddr *msg_name = NULL;
struct sctp_sndrcvinfo default_sinfo = { 0 };
struct sctp_sndrcvinfo *sinfo;
@@ -777,7 +778,8 @@ 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_sendmsg_verify_name(sk, msg);
+ err = sctp_verify_addr(sk, (struct sockaddr *)msg->msg_name,
+ msg->msg_namelen);
if (err)
return err;
@@ -826,28 +828,14 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* Look for a matching association on the endpoint. */
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) {
- struct list_head *pos;
- struct sockaddr_storage_list *addr;
- sctp_bind_addr_t *bp = &ep->base.bind_addr;
-
- sctp_read_lock(&ep->base.addr_lock);
-
- /* If we could not find a matching association on
- * the endpoint, make sure that there is no peeled-
- * off association.
- */
- list_for_each(pos, &bp->address_list) {
- addr = list_entry(pos,
- struct sockaddr_storage_list,
- list);
- if (sctp_has_association(&addr->a, &to)) {
- err = -EINVAL;
- sctp_read_unlock(&ep->base.addr_lock);
- goto out_unlock;
- }
+ /* If we could not find a matching association on the
+ * endpoint, make sure that there is no peeled-off
+ * association on another socket.
+ */
+ if (sctp_endpoint_is_peeled_off(ep, &to)) {
+ err = -EADDRNOTAVAIL;
+ goto out_unlock;
}
-
- sctp_read_unlock(&ep->base.addr_lock);
}
} else {
/* For a peeled-off socket, ignore any associd specified by
@@ -1252,6 +1240,67 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
return 0;
}
+static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
+ char *optval,
+ int optlen)
+{
+ struct sctp_paddrparams params;
+ sctp_association_t *asoc;
+ union sctp_addr *addr;
+ sctp_transport_t *trans;
+ int error;
+
+ if (optlen != sizeof(struct sctp_paddrparams))
+ return -EINVAL;
+ if (copy_from_user(&params, optval, optlen))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, params.spp_assoc_id);
+ if (!asoc)
+ return -EINVAL;
+
+ addr = (union sctp_addr *) &(params.spp_address);
+
+ trans = sctp_assoc_lookup_paddr(asoc, addr);
+ if (!trans)
+ return -ENOENT;
+
+ /* Applications can enable or disable heartbeats for any peer address
+ * of an association, modify an address's heartbeat interval, force a
+ * heartbeat to be sent immediately, and adjust the address's maximum
+ * number of retransmissions sent before an address is considered
+ * unreachable.
+ *
+ * The value of the heartbeat interval, in milliseconds. A value of
+ * UINT32_MAX (4294967295), when modifying the parameter, specifies
+ * that a heartbeat should be sent immediately to the peer address,
+ * and the current interval should remain unchanged.
+ */
+ if (0xffffffff == params.spp_hbinterval) {
+ error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans);
+ if (error)
+ return error;
+ }
+ else {
+ /* The value of the heartbeat interval, in milliseconds. A value of 0,
+ * when modifying the parameter, specifies that the heartbeat on this
+ * address should be disabled.
+ */
+ if (params.spp_hbinterval) {
+ trans->hb_allowed = 1;
+ trans->hb_interval = params.spp_hbinterval * HZ / 1000;
+ } else
+ trans->hb_allowed = 0;
+ }
+
+ /* spp_pathmaxrxt contains the maximum number of retransmissions
+ * before this address shall be considered unreachable.
+ */
+ trans->error_threshold = params.spp_pathmaxrxt;
+
+ return 0;
+}
+
/* API 6.2 setsockopt(), getsockopt()
*
* Applications use setsockopt() and getsockopt() to set or retrieve
@@ -1342,6 +1391,11 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
retval = sctp_setsockopt_autoclose(sk, optval, optlen);
break;
+ case SCTP_SET_PEER_ADDR_PARAMS:
+ retval = sctp_setsockopt_set_peer_addr_params(sk, optval,
+ optlen);
+ break;
+
default:
retval = -ENOPROTOOPT;
break;
@@ -1354,11 +1408,107 @@ out_nounlock:
return retval;
}
-/* FIXME: Write comments. */
+/* API 3.1.6 connect() - UDP Style Syntax
+ *
+ * An application may use the connect() call in the UDP model to initiate an
+ * association without sending data.
+ *
+ * The syntax is:
+ *
+ * ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
+ *
+ * sd: the socket descriptor to have a new association added to.
+ *
+ * nam: the address structure (either struct sockaddr_in or struct
+ * sockaddr_in6 defined in RFC2553 [7]).
+ *
+ * len: the size of the address.
+ */
SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
- return -EOPNOTSUPP; /* STUB */
+ sctp_opt_t *sp;
+ sctp_endpoint_t *ep;
+ sctp_association_t *asoc;
+ sctp_transport_t *transport;
+ union sctp_addr to;
+ sctp_scope_t scope;
+ long timeo;
+ int err = 0;
+
+ sctp_lock_sock(sk);
+
+ SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d)\n",
+ __FUNCTION__, sk, uaddr, addr_len);
+
+ sp = sctp_sk(sk);
+ ep = sp->ep;
+
+ /* connect() cannot be done on a peeled-off socket. */
+ if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
+ err = -EISCONN;
+ goto out_unlock;
+ }
+
+ err = sctp_verify_addr(sk, uaddr, addr_len);
+ if (err)
+ goto out_unlock;
+
+ memcpy(&to, uaddr, addr_len);
+ to.v4.sin_port = ntohs(to.v4.sin_port);
+
+ asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
+ if (asoc) {
+ if (asoc->state >= SCTP_STATE_ESTABLISHED)
+ err = -EISCONN;
+ else
+ err = -EALREADY;
+ goto out_unlock;
+ }
+
+ /* If we could not find a matching association on the endpoint,
+ * make sure that there is no peeled-off association matching the
+ * peer address even on another socket.
+ */
+ if (sctp_endpoint_is_peeled_off(ep, &to)) {
+ err = -EADDRNOTAVAIL;
+ goto out_unlock;
+ }
+
+ /* If a bind() or sctp_bindx() is not called prior to a connect()
+ * call, the system picks an ephemeral port and will choose an address
+ * set equivalent to binding with a wildcard address.
+ */
+ if (!ep->base.bind_addr.port) {
+ if (sctp_autobind(sk)) {
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+ }
+
+ scope = sctp_scope(&to);
+ asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
+ if (!asoc) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ /* Prime the peer's transport structures. */
+ transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
+
+ err = sctp_primitive_ASSOCIATE(asoc, NULL);
+ if (err < 0) {
+ sctp_association_free(asoc);
+ goto out_unlock;
+ }
+
+ timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
+ err = sctp_wait_for_connect(asoc, &timeo);
+
+out_unlock:
+ sctp_release_sock(sk);
+
+ return err;
}
/* FIXME: Write comments. */
@@ -1503,28 +1653,26 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (len != sizeof(status)) {
retval = -EINVAL;
- goto out_nounlock;
+ goto out;
}
if (copy_from_user(&status, optval, sizeof(status))) {
retval = -EFAULT;
- goto out_nounlock;
+ goto out;
}
- sctp_lock_sock(sk);
-
associd = status.sstat_assoc_id;
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) {
assoc = sctp_id2assoc(sk, associd);
if (!assoc) {
retval = -EINVAL;
- goto out_unlock;
+ goto out;
}
} else {
ep = sctp_sk(sk)->ep;
if (list_empty(&ep->asocs)) {
retval = -EINVAL;
- goto out_unlock;
+ goto out;
}
assoc = list_entry(ep->asocs.next, sctp_association_t, asocs);
@@ -1542,8 +1690,8 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_fragmentation_point = assoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address,
- &(transport->ipaddr), sizeof(sockaddr_storage_t));
- status.sstat_primary.spinfo_state = transport->state.active;
+ &(transport->ipaddr), sizeof(union sctp_addr));
+ status.sstat_primary.spinfo_state = transport->active;
status.sstat_primary.spinfo_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt;
status.sstat_primary.spinfo_rto = transport->rto;
@@ -1551,7 +1699,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (put_user(len, optlen)) {
retval = -EFAULT;
- goto out_unlock;
+ goto out;
}
SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n",
@@ -1560,13 +1708,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (copy_to_user(optval, &status, len)) {
retval = -EFAULT;
- goto out_unlock;
+ goto out;
}
-out_unlock:
- sctp_release_sock(sk);
-
-out_nounlock:
+out:
return (retval);
}
@@ -1684,25 +1829,23 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
- sctp_lock_sock(sk);
-
assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc) {
retval = -EINVAL;
- goto out_unlock;
+ goto out;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
retval = sctp_do_peeloff(assoc, &newsock);
if (retval < 0)
- goto out_unlock;
+ goto out;
/* Map the socket to an unused fd that can be returned to the user. */
retval = sock_map_fd(newsock);
if (retval < 0) {
sock_release(newsock);
- goto out_unlock;
+ goto out;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n",
@@ -1713,11 +1856,54 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
if (copy_to_user(optval, &peeloff, len))
retval = -EFAULT;
-out_unlock:
- sctp_release_sock(sk);
+out:
return retval;
}
+static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
+ int len, char *optval, int *optlen)
+{
+ struct sctp_paddrparams params;
+ sctp_association_t *asoc;
+ union sctp_addr *addr;
+ sctp_transport_t *trans;
+
+ if (len != sizeof(struct sctp_paddrparams))
+ return -EINVAL;
+ if (copy_from_user(&params, optval, *optlen))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, params.spp_assoc_id);
+ if (!asoc)
+ return -EINVAL;
+
+ addr = (union sctp_addr *) &(params.spp_address);
+
+ trans = sctp_assoc_lookup_paddr(asoc, addr);
+ if (!trans)
+ return -ENOENT;
+
+ /* The value of the heartbeat interval, in milliseconds. A value of 0,
+ * when modifying the parameter, specifies that the heartbeat on this
+ * address should be disabled.
+ */
+ if (!trans->hb_allowed)
+ params.spp_hbinterval = 0;
+ else
+ params.spp_hbinterval = trans->hb_interval * 1000 / HZ;
+
+ /* spp_pathmaxrxt contains the maximum number of retransmissions
+ * before this address shall be considered unreachable.
+ */
+ params.spp_pathmaxrxt = trans->error_threshold;
+
+ if (copy_to_user(optval, &params, len))
+ return -EFAULT;
+ *optlen = len;
+
+ return 0;
+}
+
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
@@ -1748,6 +1934,8 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
if (get_user(len, optlen))
return -EFAULT;
+ sctp_lock_sock(sk);
+
switch (optname) {
case SCTP_STATUS:
retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
@@ -1770,11 +1958,17 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
break;
+ case SCTP_GET_PEER_ADDR_PARAMS:
+ retval = sctp_getsockopt_get_peer_addr_params(sk, len, optval,
+ optlen);
+ break;
+
default:
retval = -ENOPROTOOPT;
break;
};
+ sctp_release_sock(sk);
return retval;
}
@@ -1880,7 +2074,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
* socket is going to be sk2.
*/
int sk_reuse = sk->reuse;
- sockaddr_storage_t tmpaddr;
+ union sctp_addr tmpaddr;
struct sock *sk2 = pp->sk;
SCTP_DEBUG_PRINTK("sctp_get_port() found a "
@@ -2183,34 +2377,17 @@ void sctp_put_port(struct sock *sk)
*/
static int sctp_autobind(struct sock *sk)
{
- sockaddr_storage_t autoaddr;
- int addr_len = 0;
-
- memset(&autoaddr, 0, sizeof(sockaddr_storage_t));
-
- switch (sk->family) {
- case PF_INET:
- autoaddr.v4.sin_family = AF_INET;
- autoaddr.v4.sin_addr.s_addr = INADDR_ANY;
- autoaddr.v4.sin_port = htons(inet_sk(sk)->num);
- addr_len = sizeof(struct sockaddr_in);
- break;
+ union sctp_addr autoaddr;
+ struct sctp_func *af;
+ unsigned short port;
- case PF_INET6:
- SCTP_V6(
- /* FIXME: Write me for v6! */
- BUG();
- autoaddr.v6.sin6_family = AF_INET6;
- autoaddr.v6.sin6_port = htons(inet_sk(sk)->num);
- addr_len = sizeof(struct sockaddr_in6);
- );
- break;
+ /* Initialize a local sockaddr structure to INADDR_ANY. */
+ af = sctp_sk(sk)->pf->af;
- default: /* This should not happen. */
- break;
- };
+ port = htons(inet_sk(sk)->num);
+ af->inaddr_any(&autoaddr, port);
- return sctp_do_bind(sk, &autoaddr, addr_len);
+ return sctp_do_bind(sk, &autoaddr, af->sockaddr_len);
}
/* Parse out IPPROTO_SCTP CMSG headers. Perform only minimal validation.
@@ -2327,8 +2504,8 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
/* Setup sk->rcv_saddr before calling get_port(). */
static inline void sctp_sk_addr_set(struct sock *sk,
- const sockaddr_storage_t *newaddr,
- sockaddr_storage_t *saveaddr)
+ const union sctp_addr *newaddr,
+ union sctp_addr *saveaddr)
{
struct inet_opt *inet = inet_sk(sk);
@@ -2355,7 +2532,7 @@ static inline void sctp_sk_addr_set(struct sock *sk,
}
/* Restore sk->rcv_saddr after failing get_port(). */
-static inline void sctp_sk_addr_restore(struct sock *sk, const sockaddr_storage_t *addr)
+static inline void sctp_sk_addr_restore(struct sock *sk, const union sctp_addr *addr)
{
struct inet_opt *inet = inet_sk(sk);
@@ -2498,38 +2675,33 @@ no_packet:
return NULL;
}
-static inline int sctp_sendmsg_verify_name(struct sock *sk, struct msghdr *msg)
+/* Verify that this is a valid address. */
+static int sctp_verify_addr(struct sock *sk, struct sockaddr *addr, int len)
{
- sockaddr_storage_t *sa;
+ struct sctp_func *af;
- if (msg->msg_namelen < sizeof (struct sockaddr))
+ /* Check minimum size. */
+ if (len < sizeof (struct sockaddr))
return -EINVAL;
- sa = (sockaddr_storage_t *) msg->msg_name;
- switch (sa->sa.sa_family) {
- case AF_INET:
- if (msg->msg_namelen < sizeof(struct sockaddr_in))
- return -EINVAL;
- break;
-
- case AF_INET6:
- if (PF_INET == sk->family)
- return -EINVAL;
- SCTP_V6(
- if (msg->msg_namelen < sizeof(struct sockaddr_in6))
- return -EINVAL;
- break;
- );
+ /* Do we support this address family in general? */
+ af = sctp_get_af_specific(addr->sa_family);
+ if (!af)
+ return -EINVAL;
- default:
+ /* 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;
- };
- /* Disallow any illegal addresses to be used as destinations. */
- if (!sctp_addr_is_valid(sa))
+ /* Is this a valid SCTP address? */
+ if (!af->addr_valid((union sctp_addr *)addr))
return -EINVAL;
- return 0;
+ return 0;
}
/* Get the sndbuf space available at the time on the association. */
@@ -2710,6 +2882,70 @@ static int sctp_writeable(struct sock *sk)
return amt;
}
+/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
+ * returns immediately with EINPROGRESS.
+ */
+static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
+{
+ struct sock *sk = asoc->base.sk;
+ int err = 0;
+ long current_timeo = *timeo_p;
+ DECLARE_WAITQUEUE(wait, current);
+
+ SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __FUNCTION__, asoc,
+ (long)(*timeo_p));
+
+ add_wait_queue_exclusive(&asoc->wait, &wait);
+
+ /* Increment the association's refcnt. */
+ sctp_association_hold(asoc);
+
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (!*timeo_p)
+ goto do_nonblock;
+ if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
+ asoc->base.dead)
+ goto do_error;
+ if (signal_pending(current))
+ goto do_interrupted;
+
+ if (asoc->state == SCTP_STATE_ESTABLISHED)
+ break;
+
+ /* Let another process have a go. Since we are going
+ * to sleep anyway.
+ */
+ sctp_release_sock(sk);
+ current_timeo = schedule_timeout(current_timeo);
+ sctp_lock_sock(sk);
+
+ *timeo_p = current_timeo;
+ }
+
+out:
+ remove_wait_queue(&asoc->wait, &wait);
+
+ /* Release the association's refcnt. */
+ sctp_association_put(asoc);
+
+ __set_current_state(TASK_RUNNING);
+
+ return err;
+
+do_error:
+ err = -ECONNABORTED;
+ goto out;
+
+do_interrupted:
+ err = sock_intr_errno(*timeo_p);
+ goto out;
+
+do_nonblock:
+ err = -EINPROGRESS;
+ goto out;
+}
+
/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index eed219643e9d..482b1a34effc 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -9,7 +9,7 @@
*
* This module provides the abstraction for an SCTP tranport representing
* a remote transport address. For local transport addresses, we just use
- * sockaddr_storage_t.
+ * union sctp_addr.
*
* The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of
@@ -53,7 +53,7 @@
/* 1st Level Abstractions. */
/* Allocate and initialize a new transport. */
-sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *addr, int priority)
+sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority)
{
sctp_transport_t *transport;
@@ -78,14 +78,14 @@ fail:
/* Intialize a new transport from provided memory. */
sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
- const sockaddr_storage_t *addr,
+ const union sctp_addr *addr,
int priority)
{
sctp_protocol_t *proto = sctp_get_protocol();
/* Copy in the address. */
peer->ipaddr = *addr;
- peer->af_specific = sctp_get_af_specific(addr);
+ peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
peer->asoc = NULL;
/* From 6.3.1 RTO Calculation:
@@ -104,8 +104,8 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->last_time_used = jiffies;
peer->last_time_ecne_reduced = jiffies;
- peer->state.active = 1;
- peer->state.hb_allowed = 0;
+ peer->active = 1;
+ peer->hb_allowed = 0;
/* Initialize the default path max_retrans. */
peer->max_retrans = proto->max_retrans_path;
@@ -204,11 +204,11 @@ void sctp_transport_set_owner(sctp_transport_t *transport,
* souce address.
*/
void sctp_transport_route(sctp_transport_t *transport,
- sockaddr_storage_t *saddr)
+ union sctp_addr *saddr)
{
sctp_association_t *asoc = transport->asoc;
sctp_func_t *af = transport->af_specific;
- sockaddr_storage_t *daddr = &transport->ipaddr;
+ union sctp_addr *daddr = &transport->ipaddr;
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;