Commit e5e956d0 authored by Sridhar Samudrala's avatar Sridhar Samudrala

Merge us.ibm.com:/home/sridhar/BK/linux-2.5.64

into us.ibm.com:/home/sridhar/BK/lksctp-2.5.64
parents 236b5445 56d10bed
...@@ -110,13 +110,13 @@ typedef union { ...@@ -110,13 +110,13 @@ typedef union {
sctp_event_timeout_t to; sctp_event_timeout_t to;
sctp_counter_t counter; sctp_counter_t counter;
void *ptr; void *ptr;
sctp_chunk_t *chunk; struct sctp_chunk *chunk;
sctp_association_t *asoc; struct sctp_association *asoc;
struct sctp_transport *transport; struct sctp_transport *transport;
sctp_bind_addr_t *bp; struct sctp_bind_addr *bp;
sctp_init_chunk_t *init; sctp_init_chunk_t *init;
struct sctp_ulpevent *ulpevent; struct sctp_ulpevent *ulpevent;
sctp_packet_t *packet; struct sctp_packet *packet;
sctp_sackhdr_t *sackh; sctp_sackhdr_t *sackh;
} sctp_arg_t; } sctp_arg_t;
...@@ -158,13 +158,13 @@ SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state) ...@@ -158,13 +158,13 @@ SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state)
SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter) SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter)
SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to)
SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr) SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr)
SCTP_ARG_CONSTRUCTOR(CHUNK, sctp_chunk_t *, chunk) SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk)
SCTP_ARG_CONSTRUCTOR(ASOC, sctp_association_t *, asoc) SCTP_ARG_CONSTRUCTOR(ASOC, struct sctp_association *, asoc)
SCTP_ARG_CONSTRUCTOR(TRANSPORT, struct sctp_transport *, transport) SCTP_ARG_CONSTRUCTOR(TRANSPORT, struct sctp_transport *, transport)
SCTP_ARG_CONSTRUCTOR(BA, sctp_bind_addr_t *, bp) SCTP_ARG_CONSTRUCTOR(BA, struct sctp_bind_addr *, bp)
SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init) SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init)
SCTP_ARG_CONSTRUCTOR(ULPEVENT, struct sctp_ulpevent *, ulpevent) SCTP_ARG_CONSTRUCTOR(ULPEVENT, struct sctp_ulpevent *, ulpevent)
SCTP_ARG_CONSTRUCTOR(PACKET, sctp_packet_t *, packet) SCTP_ARG_CONSTRUCTOR(PACKET, struct sctp_packet *, packet)
SCTP_ARG_CONSTRUCTOR(SACKH, sctp_sackhdr_t *, sackh) SCTP_ARG_CONSTRUCTOR(SACKH, sctp_sackhdr_t *, sackh)
typedef struct { typedef struct {
......
...@@ -210,14 +210,19 @@ typedef enum { ...@@ -210,14 +210,19 @@ typedef enum {
/* These are values for sk->state. /* These are values for sk->state.
* For a UDP-style SCTP socket, the states are defined as follows * For a UDP-style SCTP socket, the states are defined as follows
* (at this point of time, may change later after more discussions: FIXME) * - A socket in SCTP_SS_CLOSED state indicates that it is not willing to
* A socket in SCTP_SS_UNCONNECTED state indicates that it is not willing * accept new associations, but it can initiate the creation of new ones.
* to accept new associations, but it can initiate the creation of new * - A socket in SCTP_SS_LISTENING state indicates that it is willing to
* ones.
* A socket in SCTP_SS_LISTENING state indicates that it is willing to
* accept new associations and can initiate the creation of new ones. * accept new associations and can initiate the creation of new ones.
* A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off * - A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off
* socket with one association. * socket with one association.
* For a TCP-style SCTP socket, the states are defined as follows
* - A socket in SCTP_SS_CLOSED state indicates that it is not willing to
* accept new associations, but it can initiate the creation of new ones.
* - A socket in SCTP_SS_LISTENING state indicates that it is willing to
* accept new associations, but cannot initiate the creation of new ones.
* - A socket in SCTP_SS_ESTABLISHED state indicates that it has a single
* association in ESTABLISHED state.
*/ */
typedef enum { typedef enum {
SCTP_SS_CLOSED = TCP_CLOSE, SCTP_SS_CLOSED = TCP_CLOSE,
...@@ -345,6 +350,7 @@ typedef enum { ...@@ -345,6 +350,7 @@ typedef enum {
SCTP_XMIT_PMTU_FULL, SCTP_XMIT_PMTU_FULL,
SCTP_XMIT_RWND_FULL, SCTP_XMIT_RWND_FULL,
SCTP_XMIT_MUST_FRAG, SCTP_XMIT_MUST_FRAG,
SCTP_XMIT_NAGLE_DELAY,
} sctp_xmit_t; } sctp_xmit_t;
/* These are the commands for manipulating transports. */ /* These are the commands for manipulating transports. */
......
...@@ -121,9 +121,10 @@ ...@@ -121,9 +121,10 @@
/* /*
* sctp_protocol.c * sctp_protocol.c
*/ */
extern sctp_protocol_t sctp_proto; extern struct sctp_protocol sctp_proto;
extern struct sock *sctp_get_ctl_sock(void); extern struct sock *sctp_get_ctl_sock(void);
extern int sctp_copy_local_addr_list(sctp_protocol_t *, sctp_bind_addr_t *, extern int sctp_copy_local_addr_list(struct sctp_protocol *,
struct sctp_bind_addr *,
sctp_scope_t, int priority, int flags); sctp_scope_t, int priority, int flags);
extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
extern int sctp_register_pf(struct sctp_pf *, sa_family_t); extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
...@@ -312,30 +313,21 @@ static inline void sctp_sysctl_unregister(void) { return; } ...@@ -312,30 +313,21 @@ static inline void sctp_sysctl_unregister(void) { return; }
#endif #endif
/* Size of Supported Address Parameter for 'x' address types. */
#define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16))
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
extern int sctp_v6_init(void); extern int sctp_v6_init(void);
extern void sctp_v6_exit(void); extern void sctp_v6_exit(void);
static inline int sctp_ipv6_addr_type(const struct in6_addr *addr) static inline int sctp_ipv6_addr_type(const struct in6_addr *addr)
{ {
return ipv6_addr_type((struct in6_addr*) addr); return ipv6_addr_type((struct in6_addr*) addr);
} }
#define SCTP_SAT_LEN (sizeof(sctp_paramhdr_t) + 2 * sizeof(__u16))
/* Note: These V6 macros are obsolescent. */
/* Use this macro to enclose code fragments which are V6-dependent. */
#define SCTP_V6(m...) m
#define SCTP_V6_SUPPORT 1
#else /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ #else /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
#define sctp_ipv6_addr_type(a) 0 #define sctp_ipv6_addr_type(a) 0
#define SCTP_SAT_LEN (sizeof(sctp_paramhdr_t) + 1 * sizeof(__u16))
#define SCTP_V6(m...) /* Do nothing. */
#undef SCTP_V6_SUPPORT
static inline int sctp_v6_init(void) { return 0; } static inline int sctp_v6_init(void) { return 0; }
static inline void sctp_v6_exit(void) { return; } static inline void sctp_v6_exit(void) { return; }
...@@ -348,25 +340,10 @@ static inline sctp_assoc_t sctp_assoc2id(const sctp_association_t *asoc) ...@@ -348,25 +340,10 @@ static inline sctp_assoc_t sctp_assoc2id(const sctp_association_t *asoc)
return (sctp_assoc_t) asoc; return (sctp_assoc_t) asoc;
} }
/* Look up the association by its id. */
static inline sctp_association_t *sctp_id2assoc(const struct sock *sk, sctp_assoc_t id)
{
sctp_association_t *asoc = NULL;
/* First, verify that this is a kernel address. */
if (sctp_is_valid_kaddr((unsigned long) id)) {
sctp_association_t *temp = (sctp_association_t *) id;
/* Verify that this _is_ an sctp_association_t /* Look up the association by its id. */
* data structure and if so, that the socket matches. sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id);
*/
if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) &&
(temp->base.sk == sk))
asoc = temp;
}
return asoc;
}
/* A macro to walk a list of skbs. */ /* A macro to walk a list of skbs. */
#define sctp_skb_for_each(pos, head, tmp) \ #define sctp_skb_for_each(pos, head, tmp) \
...@@ -494,7 +471,7 @@ extern void sctp_put_port(struct sock *sk); ...@@ -494,7 +471,7 @@ extern void sctp_put_port(struct sock *sk);
/* Static inline functions. */ /* Static inline functions. */
/* Return the SCTP protocol structure. */ /* Return the SCTP protocol structure. */
static inline sctp_protocol_t *sctp_get_protocol(void) static inline struct sctp_protocol *sctp_get_protocol(void)
{ {
return &sctp_proto; return &sctp_proto;
} }
...@@ -523,21 +500,21 @@ static inline int ipver2af(__u8 ipver) ...@@ -523,21 +500,21 @@ static inline int ipver2af(__u8 ipver)
/* This is the hash function for the SCTP port hash table. */ /* This is the hash function for the SCTP port hash table. */
static inline int sctp_phashfn(__u16 lport) static inline int sctp_phashfn(__u16 lport)
{ {
sctp_protocol_t *sctp_proto = sctp_get_protocol(); struct sctp_protocol *sctp_proto = sctp_get_protocol();
return (lport & (sctp_proto->port_hashsize - 1)); return (lport & (sctp_proto->port_hashsize - 1));
} }
/* This is the hash function for the endpoint hash table. */ /* This is the hash function for the endpoint hash table. */
static inline int sctp_ep_hashfn(__u16 lport) static inline int sctp_ep_hashfn(__u16 lport)
{ {
sctp_protocol_t *sctp_proto = sctp_get_protocol(); struct sctp_protocol *sctp_proto = sctp_get_protocol();
return (lport & (sctp_proto->ep_hashsize - 1)); return (lport & (sctp_proto->ep_hashsize - 1));
} }
/* This is the hash function for the association hash table. */ /* This is the hash function for the association hash table. */
static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport) static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
{ {
sctp_protocol_t *sctp_proto = sctp_get_protocol(); struct sctp_protocol *sctp_proto = sctp_get_protocol();
int h = (lport << 16) + rport; int h = (lport << 16) + rport;
h ^= h>>8; h ^= h>>8;
return (h & (sctp_proto->assoc_hashsize - 1)); return (h & (sctp_proto->assoc_hashsize - 1));
...@@ -549,7 +526,7 @@ static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport) ...@@ -549,7 +526,7 @@ static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
*/ */
static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag) static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
{ {
sctp_protocol_t *sctp_proto = sctp_get_protocol(); struct sctp_protocol *sctp_proto = sctp_get_protocol();
int h = (lport << 16) + rport; int h = (lport << 16) + rport;
h ^= vtag; h ^= vtag;
return (h & (sctp_proto->assoc_hashsize-1)); return (h & (sctp_proto->assoc_hashsize-1));
......
...@@ -313,18 +313,18 @@ void sctp_generate_t3_rtx_event(unsigned long peer); ...@@ -313,18 +313,18 @@ void sctp_generate_t3_rtx_event(unsigned long peer);
void sctp_generate_heartbeat_event(unsigned long peer); void sctp_generate_heartbeat_event(unsigned long peer);
sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *); sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *);
sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *,
const sctp_association_t *asoc, const struct sctp_association *,
sctp_chunk_t *chunk, struct sctp_chunk *chunk,
const void *payload, const void *payload,
size_t paylen); size_t paylen);
sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *,
const sctp_chunk_t *chunk); const struct sctp_chunk *);
void sctp_ootb_pkt_free(sctp_packet_t *packet); void sctp_ootb_pkt_free(struct sctp_packet *);
sctp_cookie_param_t * sctp_cookie_param_t *
sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *, sctp_pack_cookie(const struct sctp_endpoint *, const struct sctp_association *,
const sctp_chunk_t *, int *cookie_len, const struct sctp_chunk *, int *cookie_len,
const __u8 *, int addrs_len); const __u8 *, int addrs_len);
sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *, sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *,
const sctp_association_t *, const sctp_association_t *,
......
...@@ -86,10 +86,8 @@ struct sctp_opt; ...@@ -86,10 +86,8 @@ struct sctp_opt;
struct sctp_endpoint_common; struct sctp_endpoint_common;
struct sctp_ssnmap; struct sctp_ssnmap;
typedef struct sctp_protocol sctp_protocol_t;
typedef struct sctp_endpoint sctp_endpoint_t; typedef struct sctp_endpoint sctp_endpoint_t;
typedef struct sctp_association sctp_association_t; typedef struct sctp_association sctp_association_t;
typedef struct sctp_packet sctp_packet_t;
typedef struct sctp_chunk sctp_chunk_t; typedef struct sctp_chunk sctp_chunk_t;
typedef struct sctp_bind_addr sctp_bind_addr_t; typedef struct sctp_bind_addr sctp_bind_addr_t;
typedef struct sctp_endpoint_common sctp_endpoint_common_t; typedef struct sctp_endpoint_common sctp_endpoint_common_t;
...@@ -262,6 +260,9 @@ struct sctp_pf { ...@@ -262,6 +260,9 @@ struct sctp_pf {
const union sctp_addr *, const union sctp_addr *,
struct sctp_opt *); struct sctp_opt *);
int (*bind_verify) (struct sctp_opt *, union sctp_addr *); int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
int (*supported_addrs)(const struct sctp_opt *, __u16 *);
struct sock *(*create_accept_sk) (struct sock *sk,
struct sctp_association *asoc);
struct sctp_af *af; struct sctp_af *af;
}; };
...@@ -366,8 +367,6 @@ typedef struct sctp_signed_cookie { ...@@ -366,8 +367,6 @@ typedef struct sctp_signed_cookie {
sctp_cookie_t c; sctp_cookie_t c;
} sctp_signed_cookie_t; } sctp_signed_cookie_t;
/* This is another convenience type to allocate memory for address /* This is another convenience type to allocate memory for address
* params for the maximum size and pass such structures around * params for the maximum size and pass such structures around
* internally. * internally.
...@@ -604,26 +603,26 @@ struct sctp_packet { ...@@ -604,26 +603,26 @@ struct sctp_packet {
typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *); typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *);
typedef int (sctp_outq_ehandler_t)(struct sctp_outq *); typedef int (sctp_outq_ehandler_t)(struct sctp_outq *);
typedef sctp_packet_t *(sctp_outq_ohandler_init_t) typedef struct sctp_packet *(sctp_outq_ohandler_init_t)
(sctp_packet_t *, (struct sctp_packet *,
struct sctp_transport *, struct sctp_transport *,
__u16 sport, __u16 sport,
__u16 dport); __u16 dport);
typedef sctp_packet_t *(sctp_outq_ohandler_config_t) typedef struct sctp_packet *(sctp_outq_ohandler_config_t)
(sctp_packet_t *, (struct sctp_packet *,
__u32 vtag, __u32 vtag,
int ecn_capable, int ecn_capable,
sctp_packet_phandler_t *get_prepend_chunk); sctp_packet_phandler_t *get_prepend_chunk);
typedef sctp_xmit_t (sctp_outq_ohandler_t)(sctp_packet_t *, typedef sctp_xmit_t (sctp_outq_ohandler_t)(struct sctp_packet *,
sctp_chunk_t *); sctp_chunk_t *);
typedef int (sctp_outq_ohandler_force_t)(sctp_packet_t *); typedef int (sctp_outq_ohandler_force_t)(struct sctp_packet *);
sctp_outq_ohandler_init_t sctp_packet_init; sctp_outq_ohandler_init_t sctp_packet_init;
sctp_outq_ohandler_config_t sctp_packet_config; sctp_outq_ohandler_config_t sctp_packet_config;
sctp_outq_ohandler_t sctp_packet_append_chunk; sctp_outq_ohandler_t sctp_packet_append_chunk;
sctp_outq_ohandler_t sctp_packet_transmit_chunk; sctp_outq_ohandler_t sctp_packet_transmit_chunk;
sctp_outq_ohandler_force_t sctp_packet_transmit; sctp_outq_ohandler_force_t sctp_packet_transmit;
void sctp_packet_free(sctp_packet_t *); void sctp_packet_free(struct sctp_packet *);
/* This represents a remote transport address. /* This represents a remote transport address.
...@@ -789,7 +788,7 @@ struct sctp_transport { ...@@ -789,7 +788,7 @@ struct sctp_transport {
struct list_head transmitted; struct list_head transmitted;
/* We build bundle-able packets for this transport here. */ /* We build bundle-able packets for this transport here. */
sctp_packet_t packet; struct sctp_packet packet;
/* This is the list of transports that have chunks to send. */ /* This is the list of transports that have chunks to send. */
struct list_head send_ready; struct list_head send_ready;
...@@ -865,12 +864,11 @@ void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *); ...@@ -865,12 +864,11 @@ void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *);
struct sctp_outq { struct sctp_outq {
sctp_association_t *asoc; sctp_association_t *asoc;
/* BUG: This really should be an array of streams. /* Data pending that has never been transmitted. */
* This really holds a list of chunks (one stream).
* FIXME: If true, why so?
*/
struct sk_buff_head out; struct sk_buff_head out;
unsigned out_qlen; /* Total length of queued data chunks. */
/* These are control chunks we want to send. */ /* These are control chunks we want to send. */
struct sk_buff_head control; struct sk_buff_head control;
...@@ -885,7 +883,7 @@ struct sctp_outq { ...@@ -885,7 +883,7 @@ struct sctp_outq {
struct list_head retransmit; struct list_head retransmit;
/* Call these functions to send chunks down to the next lower /* Call these functions to send chunks down to the next lower
* layer. This is always SCTP_packet, but we separate the two * layer. This is always sctp_packet, but we separate the two
* structures to make testing simpler. * structures to make testing simpler.
*/ */
sctp_outq_ohandler_init_t *init_output; sctp_outq_ohandler_init_t *init_output;
...@@ -1098,8 +1096,9 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base) ...@@ -1098,8 +1096,9 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base)
} }
/* These are function signatures for manipulating endpoints. */ /* These are function signatures for manipulating endpoints. */
sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *, struct sock *, int); sctp_endpoint_t *sctp_endpoint_new(struct sctp_protocol *, struct sock *, int);
sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *, sctp_protocol_t *, sctp_endpoint_t *sctp_endpoint_init(struct sctp_endpoint *,
struct sctp_protocol *,
struct sock *, int priority); struct sock *, int priority);
void sctp_endpoint_free(sctp_endpoint_t *); void sctp_endpoint_free(sctp_endpoint_t *);
void sctp_endpoint_put(sctp_endpoint_t *); void sctp_endpoint_put(sctp_endpoint_t *);
...@@ -1111,7 +1110,6 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, ...@@ -1111,7 +1110,6 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *); int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const union sctp_addr *); const union sctp_addr *);
int sctp_has_association(const union sctp_addr *laddr, int sctp_has_association(const union sctp_addr *laddr,
const union sctp_addr *paddr); const union sctp_addr *paddr);
...@@ -1587,7 +1585,7 @@ struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *, ...@@ -1587,7 +1585,7 @@ struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *,
struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *, struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *,
const union sctp_addr *address, const union sctp_addr *address,
const int priority); const int priority);
void sctp_assoc_control_transport(sctp_association_t *, void sctp_assoc_control_transport(struct sctp_association *,
struct sctp_transport *, struct sctp_transport *,
sctp_transport_cmd_t, sctp_sn_error_t); sctp_transport_cmd_t, sctp_sn_error_t);
struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *, __u32); struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *, __u32);
...@@ -1597,14 +1595,14 @@ struct sctp_transport *sctp_assoc_is_match(sctp_association_t *, ...@@ -1597,14 +1595,14 @@ struct sctp_transport *sctp_assoc_is_match(sctp_association_t *,
void sctp_assoc_migrate(sctp_association_t *, struct sock *); void sctp_assoc_migrate(sctp_association_t *, struct sock *);
void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src); void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src);
__u32 __sctp_association_get_next_tsn(sctp_association_t *); __u32 sctp_association_get_next_tsn(struct sctp_association *);
__u32 __sctp_association_get_tsn_block(sctp_association_t *, int); __u32 sctp_association_get_tsn_block(struct sctp_association *, int);
__u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid);
void sctp_assoc_sync_pmtu(sctp_association_t *);
void sctp_assoc_rwnd_increase(sctp_association_t *, int);
void sctp_assoc_rwnd_decrease(sctp_association_t *, int);
void sctp_assoc_sync_pmtu(struct sctp_association *);
void sctp_assoc_rwnd_increase(struct sctp_association *, int);
void sctp_assoc_rwnd_decrease(struct sctp_association *, int);
void sctp_assoc_set_primary(struct sctp_association *,
struct sctp_transport *);
int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *, int); int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *, int);
int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *, int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *,
sctp_cookie_t *, int); sctp_cookie_t *, int);
......
...@@ -108,6 +108,8 @@ enum sctp_optname { ...@@ -108,6 +108,8 @@ enum sctp_optname {
#define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM #define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM
SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */ SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS #define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
SCTP_NODELAY, /* Get/set nodelay option. */
#define SCTP_NODELAY SCTP_NODELAY
}; };
......
...@@ -181,7 +181,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -181,7 +181,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
else else
asoc->rwnd = sk->rcvbuf; asoc->rwnd = sk->rcvbuf;
asoc->a_rwnd = 0; asoc->a_rwnd = asoc->rwnd;
asoc->rwnd_over = 0; asoc->rwnd_over = 0;
...@@ -360,9 +360,25 @@ static void sctp_association_destroy(sctp_association_t *asoc) ...@@ -360,9 +360,25 @@ static void sctp_association_destroy(sctp_association_t *asoc)
} }
} }
/* Change the primary destination address for the peer. */
void sctp_assoc_set_primary(struct sctp_association *asoc,
struct sctp_transport *transport)
{
asoc->peer.primary_path = transport;
/* Set a default msg_name for events. */
memcpy(&asoc->peer.primary_addr, &transport->ipaddr,
sizeof(union sctp_addr));
/* If the primary path is changing, assume that the
* user wants to use this new path.
*/
if (transport->active)
asoc->peer.active_path = transport;
}
/* Add a transport address to an association. */ /* Add a transport address to an association. */
struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc, struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
const union sctp_addr *addr, const union sctp_addr *addr,
int priority) int priority)
{ {
...@@ -397,17 +413,16 @@ struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -397,17 +413,16 @@ struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc,
* If not and the current association PMTU is higher than the new * If not and the current association PMTU is higher than the new
* peer's PMTU, reset the association PMTU to the new peer's PMTU. * peer's PMTU, reset the association PMTU to the new peer's PMTU.
*/ */
if (asoc->pmtu) { if (asoc->pmtu)
asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu); asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu);
} else { else
asoc->pmtu = peer->pmtu; asoc->pmtu = peer->pmtu;
}
SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "
"%d\n", asoc, asoc->pmtu); "%d\n", asoc, asoc->pmtu);
asoc->frag_point = asoc->pmtu - asoc->frag_point = asoc->pmtu;
(SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t)); asoc->frag_point -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk);
/* The asoc->peer.port might not be meaningful yet, but /* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway. * initialize the packet structure anyway.
...@@ -460,11 +475,7 @@ struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -460,11 +475,7 @@ struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc,
/* If we do not yet have a primary path, set one. */ /* If we do not yet have a primary path, set one. */
if (NULL == asoc->peer.primary_path) { if (NULL == asoc->peer.primary_path) {
asoc->peer.primary_path = peer; sctp_assoc_set_primary(asoc, peer);
/* Set a default msg_name for events. */
memcpy(&asoc->peer.primary_addr, &peer->ipaddr,
sizeof(union sctp_addr));
asoc->peer.active_path = peer;
asoc->peer.retran_path = peer; asoc->peer.retran_path = peer;
} }
...@@ -603,7 +614,7 @@ void sctp_association_put(sctp_association_t *asoc) ...@@ -603,7 +614,7 @@ void sctp_association_put(sctp_association_t *asoc)
/* Allocate the next TSN, Transmission Sequence Number, for the given /* Allocate the next TSN, Transmission Sequence Number, for the given
* association. * association.
*/ */
__u32 __sctp_association_get_next_tsn(sctp_association_t *asoc) __u32 sctp_association_get_next_tsn(sctp_association_t *asoc)
{ {
/* From Section 1.6 Serial Number Arithmetic: /* From Section 1.6 Serial Number Arithmetic:
* Transmission Sequence Numbers wrap around when they reach * Transmission Sequence Numbers wrap around when they reach
...@@ -618,7 +629,7 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *asoc) ...@@ -618,7 +629,7 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *asoc)
} }
/* Allocate 'num' TSNs by incrementing the association's TSN by num. */ /* Allocate 'num' TSNs by incrementing the association's TSN by num. */
__u32 __sctp_association_get_tsn_block(sctp_association_t *asoc, int num) __u32 sctp_association_get_tsn_block(sctp_association_t *asoc, int num)
{ {
__u32 retval = asoc->next_tsn; __u32 retval = asoc->next_tsn;
...@@ -983,6 +994,24 @@ void sctp_assoc_sync_pmtu(sctp_association_t *asoc) ...@@ -983,6 +994,24 @@ void sctp_assoc_sync_pmtu(sctp_association_t *asoc)
__FUNCTION__, asoc, asoc->pmtu, asoc->frag_point); __FUNCTION__, asoc, asoc->pmtu, asoc->frag_point);
} }
/* Should we send a SACK to update our peer? */
static inline int sctp_peer_needs_update(struct sctp_association *asoc)
{
switch (asoc->state) {
case SCTP_STATE_ESTABLISHED:
case SCTP_STATE_SHUTDOWN_PENDING:
case SCTP_STATE_SHUTDOWN_RECEIVED:
if ((asoc->rwnd > asoc->a_rwnd) &&
((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu)))
return 1;
break;
default:
break;
}
return 0;
}
/* Increase asoc's rwnd by len and send any window update SACK if needed. */ /* Increase asoc's rwnd by len and send any window update SACK if needed. */
void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len)
{ {
...@@ -1009,10 +1038,8 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) ...@@ -1009,10 +1038,8 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len)
* The algorithm used is similar to the one described in * The algorithm used is similar to the one described in
* Section 4.2.3.3 of RFC 1122. * Section 4.2.3.3 of RFC 1122.
*/ */
if ((asoc->state == SCTP_STATE_ESTABLISHED) && if (sctp_peer_needs_update(asoc)) {
(asoc->rwnd > asoc->a_rwnd) && asoc->a_rwnd = asoc->rwnd;
((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) {
SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p " SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "
"rwnd: %u a_rwnd: %u\n", __FUNCTION__, "rwnd: %u a_rwnd: %u\n", __FUNCTION__,
asoc, asoc->rwnd, asoc->a_rwnd); asoc, asoc->rwnd, asoc->a_rwnd);
...@@ -1020,9 +1047,6 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) ...@@ -1020,9 +1047,6 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len)
if (!sack) if (!sack)
return; return;
/* Update the last advertised rwnd value. */
asoc->a_rwnd = asoc->rwnd;
asoc->peer.sack_needed = 0; asoc->peer.sack_needed = 0;
sctp_outq_tail(&asoc->outqueue, sack); sctp_outq_tail(&asoc->outqueue, sack);
...@@ -1046,7 +1070,8 @@ void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len) ...@@ -1046,7 +1070,8 @@ void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len)
asoc->rwnd = 0; asoc->rwnd = 0;
} }
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n",
__FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over); __FUNCTION__, asoc, len, asoc->rwnd,
asoc->rwnd_over);
} }
/* Build the bind address list for the association based on info from the /* Build the bind address list for the association based on info from the
......
...@@ -302,7 +302,7 @@ int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr, ...@@ -302,7 +302,7 @@ int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr,
static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *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_scope_t scope, int priority, int flags)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); struct sctp_protocol *proto = sctp_get_protocol();
int error = 0; int error = 0;
if (sctp_is_any(addr)) { if (sctp_is_any(addr)) {
......
...@@ -65,7 +65,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep); ...@@ -65,7 +65,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep);
/* Create a sctp_endpoint_t with all that boring stuff initialized. /* Create a sctp_endpoint_t with all that boring stuff initialized.
* Returns NULL if there isn't enough memory. * Returns NULL if there isn't enough memory.
*/ */
sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *proto, sctp_endpoint_t *sctp_endpoint_new(struct sctp_protocol *proto,
struct sock *sk, int priority) struct sock *sk, int priority)
{ {
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
...@@ -89,7 +89,8 @@ sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *proto, ...@@ -89,7 +89,8 @@ sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *proto,
/* /*
* Initialize the base fields of the endpoint structure. * Initialize the base fields of the endpoint structure.
*/ */
sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto, sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep,
struct sctp_protocol *proto,
struct sock *sk, int priority) struct sock *sk, int priority)
{ {
struct sctp_opt *sp = sctp_sk(sk); struct sctp_opt *sp = sctp_sk(sk);
...@@ -194,6 +195,8 @@ void sctp_endpoint_destroy(sctp_endpoint_t *ep) ...@@ -194,6 +195,8 @@ void sctp_endpoint_destroy(sctp_endpoint_t *ep)
{ {
SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
ep->base.sk->state = SCTP_SS_CLOSED;
/* Unlink this endpoint, so we can't find it again! */ /* Unlink this endpoint, so we can't find it again! */
sctp_unhash_endpoint(ep); sctp_unhash_endpoint(ep);
......
...@@ -432,6 +432,62 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) ...@@ -432,6 +432,62 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr)
return retval; return retval;
} }
/* Create and initialize a new sk for the socket to be returned by accept(). */
struct sock *sctp_v6_create_accept_sk(struct sock *sk,
struct sctp_association *asoc)
{
struct inet_opt *inet = inet_sk(sk);
struct sock *newsk;
struct inet_opt *newinet;
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
struct sctp6_sock *newsctp6sk;
newsk = sk_alloc(PF_INET6, GFP_KERNEL, sizeof(struct sctp6_sock),
sk->slab);
if (!newsk)
goto out;
sock_init_data(NULL, newsk);
newsk->type = SOCK_STREAM;
newsk->prot = sk->prot;
newsk->no_check = sk->no_check;
newsk->reuse = sk->reuse;
newsk->destruct = inet_sock_destruct;
newsk->zapped = 0;
newsk->family = PF_INET6;
newsk->protocol = IPPROTO_SCTP;
newsk->backlog_rcv = sk->prot->backlog_rcv;
newsctp6sk = (struct sctp6_sock *)newsk;
newsctp6sk->pinet6 = &newsctp6sk->inet6;
newinet = inet_sk(newsk);
newnp = inet6_sk(newsk);
memcpy(newnp, np, sizeof(struct ipv6_pinfo));
ipv6_addr_copy(&newnp->daddr, &asoc->peer.primary_addr.v6.sin6_addr);
newinet->sport = inet->sport;
newinet->dport = asoc->peer.port;
#ifdef INET_REFCNT_DEBUG
atomic_inc(&inet6_sock_nr);
atomic_inc(&inet_sock_nr);
#endif
if (0 != newsk->prot->init(newsk)) {
inet_sock_release(newsk);
newsk = NULL;
}
out:
return newsk;
}
/* Initialize a PF_INET6 socket msg_name. */ /* Initialize a PF_INET6 socket msg_name. */
static void sctp_inet6_msgname(char *msgname, int *addr_len) static void sctp_inet6_msgname(char *msgname, int *addr_len)
{ {
...@@ -564,6 +620,20 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) ...@@ -564,6 +620,20 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
return af->available(addr); return af->available(addr);
} }
/* Fill in Supported Address Type information for INIT and INIT-ACK
* chunks. Note: In the future, we may want to look at sock options
* to determine whether a PF_INET6 socket really wants to have IPV4
* addresses.
* Returns number of addresses supported.
*/
static int sctp_inet6_supported_addrs(const struct sctp_opt *opt,
__u16 *types)
{
types[0] = SCTP_PARAM_IPV4_ADDRESS;
types[1] = SCTP_PARAM_IPV6_ADDRESS;
return 2;
}
static struct proto_ops inet6_seqpacket_ops = { static struct proto_ops inet6_seqpacket_ops = {
.family = PF_INET6, .family = PF_INET6,
.release = inet6_release, .release = inet6_release,
...@@ -583,7 +653,7 @@ static struct proto_ops inet6_seqpacket_ops = { ...@@ -583,7 +653,7 @@ static struct proto_ops inet6_seqpacket_ops = {
.mmap = sock_no_mmap, .mmap = sock_no_mmap,
}; };
static struct inet_protosw sctpv6_protosw = { static struct inet_protosw sctpv6_seqpacket_protosw = {
.type = SOCK_SEQPACKET, .type = SOCK_SEQPACKET,
.protocol = IPPROTO_SCTP, .protocol = IPPROTO_SCTP,
.prot = &sctp_prot, .prot = &sctp_prot,
...@@ -592,6 +662,15 @@ static struct inet_protosw sctpv6_protosw = { ...@@ -592,6 +662,15 @@ static struct inet_protosw sctpv6_protosw = {
.no_check = 0, .no_check = 0,
.flags = SCTP_PROTOSW_FLAG .flags = SCTP_PROTOSW_FLAG
}; };
static struct inet_protosw sctpv6_stream_protosw = {
.type = SOCK_STREAM,
.protocol = IPPROTO_SCTP,
.prot = &sctp_prot,
.ops = &inet6_seqpacket_ops,
.capability = -1,
.no_check = 0,
.flags = SCTP_PROTOSW_FLAG
};
static struct inet6_protocol sctpv6_protocol = { static struct inet6_protocol sctpv6_protocol = {
.handler = sctp_rcv, .handler = sctp_rcv,
...@@ -626,6 +705,8 @@ static struct sctp_pf sctp_pf_inet6_specific = { ...@@ -626,6 +705,8 @@ static struct sctp_pf sctp_pf_inet6_specific = {
.af_supported = sctp_inet6_af_supported, .af_supported = sctp_inet6_af_supported,
.cmp_addr = sctp_inet6_cmp_addr, .cmp_addr = sctp_inet6_cmp_addr,
.bind_verify = sctp_inet6_bind_verify, .bind_verify = sctp_inet6_bind_verify,
.supported_addrs = sctp_inet6_supported_addrs,
.create_accept_sk = sctp_v6_create_accept_sk,
.af = &sctp_ipv6_specific, .af = &sctp_ipv6_specific,
}; };
...@@ -636,8 +717,9 @@ int sctp_v6_init(void) ...@@ -636,8 +717,9 @@ int sctp_v6_init(void)
if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0)
return -EAGAIN; return -EAGAIN;
/* Add SCTPv6 to inetsw6 linked list. */ /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */
inet6_register_protosw(&sctpv6_protosw); inet6_register_protosw(&sctpv6_seqpacket_protosw);
inet6_register_protosw(&sctpv6_stream_protosw);
/* Register the SCTP specfic PF_INET6 functions. */ /* Register the SCTP specfic PF_INET6 functions. */
sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6);
...@@ -656,6 +738,7 @@ void sctp_v6_exit(void) ...@@ -656,6 +738,7 @@ void sctp_v6_exit(void)
{ {
list_del(&sctp_ipv6_specific.list); list_del(&sctp_ipv6_specific.list);
inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP);
inet6_unregister_protosw(&sctpv6_protosw); inet6_unregister_protosw(&sctpv6_seqpacket_protosw);
inet6_unregister_protosw(&sctpv6_stream_protosw);
unregister_inet6addr_notifier(&sctp_inetaddr_notifier); unregister_inet6addr_notifier(&sctp_inetaddr_notifier);
} }
...@@ -62,16 +62,15 @@ ...@@ -62,16 +62,15 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Forward declarations for private helpers. */ /* Forward declarations for private helpers. */
static void sctp_packet_reset(sctp_packet_t *packet); static void sctp_packet_reset(struct sctp_packet *packet);
static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
sctp_chunk_t *chunk); struct sctp_chunk *chunk);
/* Config a packet. /* Config a packet.
* This appears to be a followup set of initializations.) * This appears to be a followup set of initializations.)
*/ */
sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
__u32 vtag, __u32 vtag, int ecn_capable,
int ecn_capable,
sctp_packet_phandler_t *prepend_handler) sctp_packet_phandler_t *prepend_handler)
{ {
int packet_empty = (packet->size == SCTP_IP_OVERHEAD); int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
...@@ -89,10 +88,9 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, ...@@ -89,10 +88,9 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet,
} }
/* Initialize the packet structure. */ /* Initialize the packet structure. */
sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
struct sctp_transport *transport, struct sctp_transport *transport,
__u16 sport, __u16 sport, __u16 dport)
__u16 dport)
{ {
packet->transport = transport; packet->transport = transport;
packet->source_port = sport; packet->source_port = sport;
...@@ -109,14 +107,12 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, ...@@ -109,14 +107,12 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *packet,
} }
/* Free a packet. */ /* Free a packet. */
void sctp_packet_free(sctp_packet_t *packet) void sctp_packet_free(struct sctp_packet *packet)
{ {
sctp_chunk_t *chunk; struct sctp_chunk *chunk;
while (NULL != while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks)))
(chunk = (sctp_chunk_t *)skb_dequeue(&packet->chunks))) {
sctp_free_chunk(chunk); sctp_free_chunk(chunk);
}
if (packet->malloced) if (packet->malloced)
kfree(packet); kfree(packet);
...@@ -129,8 +125,8 @@ void sctp_packet_free(sctp_packet_t *packet) ...@@ -129,8 +125,8 @@ void sctp_packet_free(sctp_packet_t *packet)
* as it can fit in the packet, but any more data that does not fit in this * as it can fit in the packet, but any more data that does not fit in this
* packet can be sent only after receiving the COOKIE_ACK. * packet can be sent only after receiving the COOKIE_ACK.
*/ */
sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
sctp_chunk_t *chunk) struct sctp_chunk *chunk)
{ {
sctp_xmit_t retval; sctp_xmit_t retval;
int error = 0; int error = 0;
...@@ -152,6 +148,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, ...@@ -152,6 +148,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet,
case SCTP_XMIT_MUST_FRAG: case SCTP_XMIT_MUST_FRAG:
case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_RWND_FULL:
case SCTP_XMIT_OK: case SCTP_XMIT_OK:
case SCTP_XMIT_NAGLE_DELAY:
break; break;
}; };
...@@ -161,7 +158,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, ...@@ -161,7 +158,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet,
/* Append a chunk to the offered packet reporting back any inability to do /* Append a chunk to the offered packet reporting back any inability to do
* so. * so.
*/ */
sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
struct sctp_chunk *chunk)
{ {
sctp_xmit_t retval = SCTP_XMIT_OK; sctp_xmit_t retval = SCTP_XMIT_OK;
__u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length)); __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length));
...@@ -223,7 +221,7 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) ...@@ -223,7 +221,7 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
} }
/* It is OK to send this chunk. */ /* It is OK to send this chunk. */
skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk); __skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk);
packet->size += chunk_len; packet->size += chunk_len;
finish: finish:
return retval; return retval;
...@@ -234,18 +232,18 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) ...@@ -234,18 +232,18 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
* *
* The return value is a normal kernel error return value. * The return value is a normal kernel error return value.
*/ */
int sctp_packet_transmit(sctp_packet_t *packet) int sctp_packet_transmit(struct sctp_packet *packet)
{ {
struct sctp_transport *transport = packet->transport; struct sctp_transport *transport = packet->transport;
sctp_association_t *asoc = transport->asoc; struct sctp_association *asoc = transport->asoc;
struct sctphdr *sh; struct sctphdr *sh;
__u32 crc32; __u32 crc32;
struct sk_buff *nskb; struct sk_buff *nskb;
sctp_chunk_t *chunk; struct sctp_chunk *chunk;
struct sock *sk; struct sock *sk;
int err = 0; int err = 0;
int padding; /* How much padding do we need? */ int padding; /* How much padding do we need? */
__u8 packet_has_data = 0; __u8 has_data = 0;
struct dst_entry *dst; struct dst_entry *dst;
/* Do NOT generate a chunkless packet... */ /* Do NOT generate a chunkless packet... */
...@@ -253,7 +251,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -253,7 +251,7 @@ int sctp_packet_transmit(sctp_packet_t *packet)
return err; return err;
/* Set up convenience variables... */ /* Set up convenience variables... */
chunk = (sctp_chunk_t *) (packet->chunks.next); chunk = (struct sctp_chunk *) (packet->chunks.next);
sk = chunk->skb->sk; sk = chunk->skb->sk;
/* Allocate the new skb. */ /* Allocate the new skb. */
...@@ -291,8 +289,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -291,8 +289,7 @@ int sctp_packet_transmit(sctp_packet_t *packet)
* [This whole comment explains WORD_ROUND() below.] * [This whole comment explains WORD_ROUND() below.]
*/ */
SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n");
while (NULL != (chunk = (sctp_chunk_t *) while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) {
skb_dequeue(&packet->chunks))) {
chunk->num_times_sent++; chunk->num_times_sent++;
chunk->sent_at = jiffies; chunk->sent_at = jiffies;
if (sctp_chunk_is_data(chunk)) { if (sctp_chunk_is_data(chunk)) {
...@@ -309,7 +306,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -309,7 +306,7 @@ int sctp_packet_transmit(sctp_packet_t *packet)
chunk->rtt_in_progress = 1; chunk->rtt_in_progress = 1;
transport->rto_pending = 1; transport->rto_pending = 1;
} }
packet_has_data = 1; has_data = 1;
} }
memcpy(skb_put(nskb, chunk->skb->len), memcpy(skb_put(nskb, chunk->skb->len),
chunk->skb->data, chunk->skb->len); chunk->skb->data, chunk->skb->len);
...@@ -399,7 +396,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -399,7 +396,7 @@ int sctp_packet_transmit(sctp_packet_t *packet)
asoc->peer.last_sent_to = transport; asoc->peer.last_sent_to = transport;
} }
if (packet_has_data) { if (has_data) {
struct timer_list *timer; struct timer_list *timer;
unsigned long timeout; unsigned long timeout;
...@@ -456,9 +453,9 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -456,9 +453,9 @@ int sctp_packet_transmit(sctp_packet_t *packet)
/* /*
* This private function resets the packet to a fresh state. * This private function resets the packet to a fresh state.
*/ */
static void sctp_packet_reset(sctp_packet_t *packet) static void sctp_packet_reset(struct sctp_packet *packet)
{ {
sctp_chunk_t *chunk = NULL; struct sctp_chunk *chunk = NULL;
packet->size = SCTP_IP_OVERHEAD; packet->size = SCTP_IP_OVERHEAD;
...@@ -473,13 +470,16 @@ static void sctp_packet_reset(sctp_packet_t *packet) ...@@ -473,13 +470,16 @@ static void sctp_packet_reset(sctp_packet_t *packet)
} }
/* This private function handles the specifics of appending DATA chunks. */ /* This private function handles the specifics of appending DATA chunks. */
static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
sctp_chunk_t *chunk) struct sctp_chunk *chunk)
{ {
sctp_xmit_t retval = SCTP_XMIT_OK; sctp_xmit_t retval = SCTP_XMIT_OK;
size_t datasize, rwnd, inflight; size_t datasize, rwnd, inflight;
struct sctp_transport *transport = packet->transport; struct sctp_transport *transport = packet->transport;
__u32 max_burst_bytes; __u32 max_burst_bytes;
struct sctp_association *asoc = transport->asoc;
struct sctp_opt *sp = sctp_sk(asoc->base.sk);
struct sctp_outq *q = &asoc->outqueue;
/* RFC 2960 6.1 Transmission of DATA Chunks /* RFC 2960 6.1 Transmission of DATA Chunks
* *
...@@ -494,8 +494,8 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, ...@@ -494,8 +494,8 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
* receiver to the data sender. * receiver to the data sender.
*/ */
rwnd = transport->asoc->peer.rwnd; rwnd = asoc->peer.rwnd;
inflight = transport->asoc->outqueue.outstanding_bytes; inflight = asoc->outqueue.outstanding_bytes;
datasize = sctp_data_size(chunk); datasize = sctp_data_size(chunk);
...@@ -517,7 +517,7 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, ...@@ -517,7 +517,7 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
* if ((flightsize + Max.Burst * MTU) < cwnd) * if ((flightsize + Max.Burst * MTU) < cwnd)
* cwnd = flightsize + Max.Burst * MTU * cwnd = flightsize + Max.Burst * MTU
*/ */
max_burst_bytes = transport->asoc->max_burst * transport->asoc->pmtu; max_burst_bytes = asoc->max_burst * asoc->pmtu;
if ((transport->flight_size + max_burst_bytes) < transport->cwnd) { if ((transport->flight_size + max_burst_bytes) < transport->cwnd) {
transport->cwnd = transport->flight_size + max_burst_bytes; transport->cwnd = transport->flight_size + max_burst_bytes;
SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: " SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: "
...@@ -543,27 +543,44 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, ...@@ -543,27 +543,44 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
* When a Fast Retransmit is being performed the sender SHOULD * When a Fast Retransmit is being performed the sender SHOULD
* ignore the value of cwnd and SHOULD NOT delay retransmission. * ignore the value of cwnd and SHOULD NOT delay retransmission.
*/ */
if (!chunk->fast_retransmit) { if (!chunk->fast_retransmit)
if (transport->flight_size >= transport->cwnd) { if (transport->flight_size >= transport->cwnd) {
retval = SCTP_XMIT_RWND_FULL; retval = SCTP_XMIT_RWND_FULL;
goto finish; goto finish;
} }
/* Nagle's algorithm to solve small-packet problem:
* Inhibit the sending of new chunks when new outgoing data arrives
* if any previously transmitted data on the connection remains
* unacknowledged.
*/
if (!sp->nodelay && SCTP_IP_OVERHEAD == packet->size &&
q->outstanding_bytes && SCTP_STATE_ESTABLISHED == asoc->state) {
unsigned len = datasize + q->out_qlen;
/* Check whether this chunk and all the rest of pending
* data will fit or delay in hopes of bundling a full
* sized packet.
*/
if (len < asoc->pmtu - SCTP_IP_OVERHEAD) {
retval = SCTP_XMIT_NAGLE_DELAY;
goto finish;
}
} }
/* Keep track of how many bytes are in flight over this transport. */ /* Keep track of how many bytes are in flight over this transport. */
transport->flight_size += datasize; transport->flight_size += datasize;
/* Keep track of how many bytes are in flight to the receiver. */ /* Keep track of how many bytes are in flight to the receiver. */
transport->asoc->outqueue.outstanding_bytes += datasize; asoc->outqueue.outstanding_bytes += datasize;
/* Update our view of the receiver's rwnd. */ /* Update our view of the receiver's rwnd. */
if (datasize < rwnd) { if (datasize < rwnd)
rwnd -= datasize; rwnd -= datasize;
} else { else
rwnd = 0; rwnd = 0;
}
transport->asoc->peer.rwnd = rwnd; asoc->peer.rwnd = rwnd;
finish: finish:
return retval; return retval;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001-2003 Intel Corp.
* Copyright (c) 2001-2003 International Business Machines Corp. * Copyright (c) 2001-2003 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -62,6 +62,43 @@ static void sctp_check_transmitted(struct sctp_outq *q, ...@@ -62,6 +62,43 @@ static void sctp_check_transmitted(struct sctp_outq *q,
sctp_sackhdr_t *sack, sctp_sackhdr_t *sack,
__u32 highest_new_tsn); __u32 highest_new_tsn);
/* Add data to the front of the queue. */
static inline void sctp_outq_head_data(struct sctp_outq *q,
struct sctp_chunk *ch)
{
__skb_queue_head(&q->out, (struct sk_buff *)ch);
q->out_qlen += ch->skb->len;
return;
}
/* Take data from the front of the queue. */
static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q)
{
struct sctp_chunk *ch;
ch = (struct sctp_chunk *)__skb_dequeue(&q->out);
if (ch)
q->out_qlen -= ch->skb->len;
return ch;
}
/* Add data chunk to the end of the queue. */
static inline void sctp_outq_tail_data(struct sctp_outq *q,
struct sctp_chunk *ch)
{
__skb_queue_tail(&q->out, (struct sk_buff *)ch);
q->out_qlen += ch->skb->len;
return;
}
/* Insert a chunk behind chunk 'pos'. */
static inline void sctp_outq_insert_data(struct sctp_outq *q,
struct sctp_chunk *ch,
struct sctp_chunk *pos)
{
__skb_insert((struct sk_buff *)ch, (struct sk_buff *)pos->prev,
(struct sk_buff *)pos, pos->list);
q->out_qlen += ch->skb->len;
}
/* Generate a new outqueue. */ /* Generate a new outqueue. */
struct sctp_outq *sctp_outq_new(sctp_association_t *asoc) struct sctp_outq *sctp_outq_new(sctp_association_t *asoc)
{ {
...@@ -97,6 +134,7 @@ void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q) ...@@ -97,6 +134,7 @@ void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q)
q->empty = 1; q->empty = 1;
q->malloced = 0; q->malloced = 0;
q->out_qlen = 0;
} }
/* Free the outqueue structure and any related pending chunks. /* Free the outqueue structure and any related pending chunks.
...@@ -133,7 +171,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -133,7 +171,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
} }
/* Throw away any leftover data chunks. */ /* Throw away any leftover data chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out))) while ((chunk = sctp_outq_dequeue_data(q)))
sctp_free_chunk(chunk); sctp_free_chunk(chunk);
/* Throw away any leftover control chunks. */ /* Throw away any leftover control chunks. */
...@@ -192,7 +230,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk) ...@@ -192,7 +230,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk)
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk"); : "Illegal Chunk");
skb_queue_tail(&q->out, (struct sk_buff *) chunk); sctp_outq_tail_data(q, chunk);
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
SCTP_INC_STATS(SctpOutUnorderChunks); SCTP_INC_STATS(SctpOutUnorderChunks);
else else
...@@ -201,7 +239,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk) ...@@ -201,7 +239,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk)
break; break;
}; };
} else { } else {
skb_queue_tail(&q->control, (struct sk_buff *) chunk); __skb_queue_tail(&q->control, (struct sk_buff *) chunk);
SCTP_INC_STATS(SctpOutCtrlChunks); SCTP_INC_STATS(SctpOutCtrlChunks);
} }
...@@ -351,7 +389,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, ...@@ -351,7 +389,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
* *
* The return value is a normal kernel error return value. * The return value is a normal kernel error return value.
*/ */
static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
int rtx_timeout, int *start_timer) int rtx_timeout, int *start_timer)
{ {
struct list_head *lqueue; struct list_head *lqueue;
...@@ -385,17 +423,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, ...@@ -385,17 +423,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt,
while (lchunk) { while (lchunk) {
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
#if 0
/* If a chunk has been tried for more than SCTP_DEF_MAX_SEND
* times, discard it, and check the empty flag of the outqueue.
*
* --xguo
*/
if (chunk->snd_count > SCTP_DEF_MAX_SEND) {
sctp_free_chunk(chunk);
continue;
}
#endif
/* Make sure that Gap Acked TSNs are not retransmitted. A /* Make sure that Gap Acked TSNs are not retransmitted. A
* simple approach is just to move such TSNs out of the * simple approach is just to move such TSNs out of the
...@@ -461,8 +488,8 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, ...@@ -461,8 +488,8 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt,
* queue. 'pos' points to the next chunk in the output queue after the * queue. 'pos' points to the next chunk in the output queue after the
* chunk that is currently in the process of fragmentation. * chunk that is currently in the process of fragmentation.
*/ */
void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, void sctp_xmit_frag(struct sctp_outq *q, struct sctp_chunk *pos,
sctp_packet_t *packet, sctp_chunk_t *frag, __u32 tsn) struct sctp_packet *packet, struct sctp_chunk *frag, __u32 tsn)
{ {
struct sctp_transport *transport = packet->transport; struct sctp_transport *transport = packet->transport;
struct sk_buff_head *queue = &q->out; struct sk_buff_head *queue = &q->out;
...@@ -480,11 +507,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, ...@@ -480,11 +507,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos,
SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. " SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. "
"adding 0x%x to outqueue\n", "adding 0x%x to outqueue\n",
ntohl(frag->subh.data_hdr->tsn)); ntohl(frag->subh.data_hdr->tsn));
if (pos) { if (pos)
skb_insert(pos, (struct sk_buff *) frag); sctp_outq_insert_data(q, frag, pos);
} else { else
skb_queue_tail(queue, (struct sk_buff *) frag); sctp_outq_tail_data(q, frag);
}
return; return;
} }
...@@ -496,11 +522,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, ...@@ -496,11 +522,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos,
SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. " SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. "
"adding 0x%x to outqueue\n", "adding 0x%x to outqueue\n",
ntohl(frag->subh.data_hdr->tsn)); ntohl(frag->subh.data_hdr->tsn));
if (pos) { if (pos)
skb_insert(pos, (struct sk_buff *) frag); sctp_outq_insert_data(q, frag, pos);
} else { else
skb_queue_tail(queue, (struct sk_buff *) frag); sctp_outq_tail_data(q, frag);
}
break; break;
case SCTP_XMIT_OK: case SCTP_XMIT_OK:
...@@ -512,11 +537,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, ...@@ -512,11 +537,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos,
SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output "
"failed. adding 0x%x to outqueue\n", "failed. adding 0x%x to outqueue\n",
ntohl(frag->subh.data_hdr->tsn)); ntohl(frag->subh.data_hdr->tsn));
if (pos) { if (pos)
skb_insert(pos, (struct sk_buff *) frag); sctp_outq_insert_data(q, frag, pos);
} else { else
skb_queue_tail(queue, (struct sk_buff *) frag); sctp_outq_tail_data(q, frag);
}
} else { } else {
SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output "
"success. 0x%x sent\n", "success. 0x%x sent\n",
...@@ -537,14 +561,14 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, ...@@ -537,14 +561,14 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos,
* The argument 'frag' point to the first fragment and it holds the list * The argument 'frag' point to the first fragment and it holds the list
* of all the other fragments in the 'frag_list' field. * of all the other fragments in the 'frag_list' field.
*/ */
void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet, void sctp_xmit_fragmented_chunks(struct sctp_outq *q, struct sctp_packet *pkt,
sctp_chunk_t *frag) sctp_chunk_t *frag)
{ {
sctp_association_t *asoc = frag->asoc; sctp_association_t *asoc = frag->asoc;
struct list_head *lfrag, *frag_list; struct list_head *lfrag, *frag_list;
__u32 tsn; __u32 tsn;
int nfrags = 1; int nfrags = 1;
struct sk_buff *pos; struct sctp_chunk *pos;
/* Count the number of fragments. */ /* Count the number of fragments. */
frag_list = &frag->frag_list; frag_list = &frag->frag_list;
...@@ -553,17 +577,17 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet, ...@@ -553,17 +577,17 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet,
} }
/* Get a TSN block of nfrags TSNs. */ /* Get a TSN block of nfrags TSNs. */
tsn = __sctp_association_get_tsn_block(asoc, nfrags); tsn = sctp_association_get_tsn_block(asoc, nfrags);
pos = skb_peek(&q->out); pos = (struct sctp_chunk *)skb_peek(&q->out);
/* Transmit the first fragment. */ /* Transmit the first fragment. */
sctp_xmit_frag(q, pos, packet, frag, tsn++); sctp_xmit_frag(q, pos, pkt, frag, tsn++);
/* Transmit the rest of fragments. */ /* Transmit the rest of fragments. */
frag_list = &frag->frag_list; frag_list = &frag->frag_list;
list_for_each(lfrag, frag_list) { list_for_each(lfrag, frag_list) {
frag = list_entry(lfrag, sctp_chunk_t, frag_list); frag = list_entry(lfrag, sctp_chunk_t, frag_list);
sctp_xmit_frag(q, pos, packet, frag, tsn++); sctp_xmit_frag(q, pos, pkt, frag, tsn++);
} }
} }
...@@ -672,15 +696,14 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -672,15 +696,14 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
* *
* Description: Send everything in q which we legally can, subject to * Description: Send everything in q which we legally can, subject to
* congestion limitations. * congestion limitations.
* * * Note: This function can be called from multiple contexts so appropriate
* Note: This function can be called from multiple contexts so appropriate
* locking concerns must be made. Today we use the sock lock to protect * locking concerns must be made. Today we use the sock lock to protect
* this function. * this function.
*/ */
int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
{ {
sctp_packet_t *packet; struct sctp_packet *packet;
sctp_packet_t singleton; struct sctp_packet singleton;
sctp_association_t *asoc = q->asoc; sctp_association_t *asoc = q->asoc;
int ecn_capable = asoc->peer.ecn_capable; int ecn_capable = asoc->peer.ecn_capable;
__u16 sport = asoc->base.bind_addr.port; __u16 sport = asoc->base.bind_addr.port;
...@@ -719,7 +742,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -719,7 +742,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
} }
queue = &q->control; queue = &q->control;
while (NULL != (chunk = (sctp_chunk_t *)skb_dequeue(queue))) { while ((chunk = (sctp_chunk_t *)skb_dequeue(queue))) {
/* Pick the right transport to use. */ /* Pick the right transport to use. */
new_transport = chunk->transport; new_transport = chunk->transport;
...@@ -852,7 +875,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -852,7 +875,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
/* Finally, transmit new packets. */ /* Finally, transmit new packets. */
start_timer = 0; start_timer = 0;
queue = &q->out; queue = &q->out;
while (NULL != (chunk = (sctp_chunk_t *) skb_dequeue(queue))) {
while (NULL != (chunk = sctp_outq_dequeue_data(q))) {
/* RFC 2960 6.5 Every DATA chunk MUST carry a valid /* RFC 2960 6.5 Every DATA chunk MUST carry a valid
* stream identifier. * stream identifier.
*/ */
...@@ -925,6 +949,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -925,6 +949,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
switch (status) { switch (status) {
case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_PMTU_FULL:
case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_RWND_FULL:
case SCTP_XMIT_NAGLE_DELAY:
/* We could not append this chunk, so put /* We could not append this chunk, so put
* the chunk back on the output queue. * the chunk back on the output queue.
*/ */
...@@ -932,7 +957,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -932,7 +957,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
"not transmit TSN: 0x%x, status: %d\n", "not transmit TSN: 0x%x, status: %d\n",
ntohl(chunk->subh.data_hdr->tsn), ntohl(chunk->subh.data_hdr->tsn),
status); status);
skb_queue_head(queue, (struct sk_buff *)chunk); sctp_outq_head_data(q, chunk);
goto sctp_flush_out; goto sctp_flush_out;
break; break;
...@@ -994,6 +1019,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -994,6 +1019,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
} }
sctp_flush_out: sctp_flush_out:
/* Before returning, examine all the transports touched in /* Before returning, examine all the transports touched in
* this call. Right now, we bluntly force clear all the * this call. Right now, we bluntly force clear all the
* transports. Things might change after we implement Nagle. * transports. Things might change after we implement Nagle.
...@@ -1163,11 +1189,10 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) ...@@ -1163,11 +1189,10 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
sack_a_rwnd = ntohl(sack->a_rwnd); sack_a_rwnd = ntohl(sack->a_rwnd);
outstanding = q->outstanding_bytes; outstanding = q->outstanding_bytes;
if (outstanding < sack_a_rwnd) { if (outstanding < sack_a_rwnd)
sack_a_rwnd -= outstanding; sack_a_rwnd -= outstanding;
} else { else
sack_a_rwnd = 0; sack_a_rwnd = 0;
}
asoc->peer.rwnd = sack_a_rwnd; asoc->peer.rwnd = sack_a_rwnd;
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
#include <net/inet_common.h> #include <net/inet_common.h>
/* Global data structures. */ /* Global data structures. */
sctp_protocol_t sctp_proto; struct sctp_protocol sctp_proto;
struct proc_dir_entry *proc_net_sctp; struct proc_dir_entry *proc_net_sctp;
DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics); DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics);
...@@ -152,7 +152,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist, ...@@ -152,7 +152,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
/* Extract our IP addresses from the system and stash them in the /* Extract our IP addresses from the system and stash them in the
* protocol structure. * protocol structure.
*/ */
static void __sctp_get_local_addr_list(sctp_protocol_t *proto) static void __sctp_get_local_addr_list(struct sctp_protocol *proto)
{ {
struct net_device *dev; struct net_device *dev;
struct list_head *pos; struct list_head *pos;
...@@ -168,7 +168,7 @@ static void __sctp_get_local_addr_list(sctp_protocol_t *proto) ...@@ -168,7 +168,7 @@ static void __sctp_get_local_addr_list(sctp_protocol_t *proto)
read_unlock(&dev_base_lock); read_unlock(&dev_base_lock);
} }
static void sctp_get_local_addr_list(sctp_protocol_t *proto) static void sctp_get_local_addr_list(struct sctp_protocol *proto)
{ {
long flags __attribute__ ((unused)); long flags __attribute__ ((unused));
...@@ -178,7 +178,7 @@ static void sctp_get_local_addr_list(sctp_protocol_t *proto) ...@@ -178,7 +178,7 @@ static void sctp_get_local_addr_list(sctp_protocol_t *proto)
} }
/* Free the existing local addresses. */ /* Free the existing local addresses. */
static void __sctp_free_local_addr_list(sctp_protocol_t *proto) static void __sctp_free_local_addr_list(struct sctp_protocol *proto)
{ {
struct sockaddr_storage_list *addr; struct sockaddr_storage_list *addr;
struct list_head *pos, *temp; struct list_head *pos, *temp;
...@@ -191,7 +191,7 @@ static void __sctp_free_local_addr_list(sctp_protocol_t *proto) ...@@ -191,7 +191,7 @@ static void __sctp_free_local_addr_list(sctp_protocol_t *proto)
} }
/* Free the existing local addresses. */ /* Free the existing local addresses. */
static void sctp_free_local_addr_list(sctp_protocol_t *proto) static void sctp_free_local_addr_list(struct sctp_protocol *proto)
{ {
long flags __attribute__ ((unused)); long flags __attribute__ ((unused));
...@@ -201,8 +201,9 @@ static void sctp_free_local_addr_list(sctp_protocol_t *proto) ...@@ -201,8 +201,9 @@ static void sctp_free_local_addr_list(sctp_protocol_t *proto)
} }
/* Copy the local addresses which are valid for 'scope' into 'bp'. */ /* Copy the local addresses which are valid for 'scope' into 'bp'. */
int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp, int sctp_copy_local_addr_list(struct sctp_protocol *proto,
sctp_scope_t scope, int priority, int copy_flags) struct sctp_bind_addr *bp, sctp_scope_t scope,
int priority, int copy_flags)
{ {
struct sockaddr_storage_list *addr; struct sockaddr_storage_list *addr;
int error = 0; int error = 0;
...@@ -479,6 +480,61 @@ void sctp_v4_get_saddr(sctp_association_t *asoc, ...@@ -479,6 +480,61 @@ void sctp_v4_get_saddr(sctp_association_t *asoc,
} }
/* Create and initialize a new sk for the socket returned by accept(). */
struct sock *sctp_v4_create_accept_sk(struct sock *sk,
struct sctp_association *asoc)
{
struct sock *newsk;
struct inet_opt *inet = inet_sk(sk);
struct inet_opt *newinet;
newsk = sk_alloc(PF_INET, GFP_KERNEL, sizeof(struct sctp_sock),
sk->slab);
if (!newsk)
goto out;
sock_init_data(NULL, newsk);
newsk->type = SOCK_STREAM;
newsk->prot = sk->prot;
newsk->no_check = sk->no_check;
newsk->reuse = sk->reuse;
newsk->destruct = inet_sock_destruct;
newsk->zapped = 0;
newsk->family = PF_INET;
newsk->protocol = IPPROTO_SCTP;
newsk->backlog_rcv = sk->prot->backlog_rcv;
newinet = inet_sk(newsk);
newinet->sport = inet->sport;
newinet->saddr = inet->saddr;
newinet->rcv_saddr = inet->saddr;
newinet->dport = asoc->peer.port;
newinet->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
newinet->pmtudisc = inet->pmtudisc;
newinet->id = 0;
newinet->ttl = sysctl_ip_default_ttl;
newinet->mc_loop = 1;
newinet->mc_ttl = 1;
newinet->mc_index = 0;
newinet->mc_list = NULL;
#ifdef INET_REFCNT_DEBUG
atomic_inc(&inet_sock_nr);
#endif
if (0 != newsk->prot->init(newsk)) {
inet_sock_release(newsk);
newsk = NULL;
}
out:
return newsk;
}
/* Event handler for inet address addition/deletion events. /* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list. * Basically, whenever there is an event, we re-build our local address list.
*/ */
...@@ -501,10 +557,13 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long event, ...@@ -501,10 +557,13 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long event,
*/ */
int sctp_ctl_sock_init(void) int sctp_ctl_sock_init(void)
{ {
int err = 0; int err;
int family = PF_INET; sa_family_t family;
SCTP_V6(family = PF_INET6;) if (sctp_get_pf_specific(PF_INET6))
family = PF_INET6;
else
family = PF_INET;
err = sock_create(family, SOCK_SEQPACKET, IPPROTO_SCTP, err = sock_create(family, SOCK_SEQPACKET, IPPROTO_SCTP,
&sctp_ctl_socket); &sctp_ctl_socket);
...@@ -630,6 +689,16 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) ...@@ -630,6 +689,16 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
return sctp_v4_available(addr); return sctp_v4_available(addr);
} }
/* Fill in Supported Address Type information for INIT and INIT-ACK
* chunks. Returns number of addresses supported.
*/
static int sctp_inet_supported_addrs(const struct sctp_opt *opt,
__u16 *types)
{
types[0] = SCTP_PARAM_IPV4_ADDRESS;
return 1;
}
/* Wrapper routine that calls the ip transmit routine. */ /* Wrapper routine that calls the ip transmit routine. */
static inline int sctp_v4_xmit(struct sk_buff *skb, static inline int sctp_v4_xmit(struct sk_buff *skb,
struct sctp_transport *transport, int ipfragok) struct sctp_transport *transport, int ipfragok)
...@@ -652,6 +721,8 @@ static struct sctp_pf sctp_pf_inet = { ...@@ -652,6 +721,8 @@ static struct sctp_pf sctp_pf_inet = {
.af_supported = sctp_inet_af_supported, .af_supported = sctp_inet_af_supported,
.cmp_addr = sctp_inet_cmp_addr, .cmp_addr = sctp_inet_cmp_addr,
.bind_verify = sctp_inet_bind_verify, .bind_verify = sctp_inet_bind_verify,
.supported_addrs = sctp_inet_supported_addrs,
.create_accept_sk = sctp_v4_create_accept_sk,
.af = &sctp_ipv4_specific, .af = &sctp_ipv4_specific,
}; };
...@@ -682,7 +753,7 @@ struct proto_ops inet_seqpacket_ops = { ...@@ -682,7 +753,7 @@ struct proto_ops inet_seqpacket_ops = {
}; };
/* Registration with AF_INET family. */ /* Registration with AF_INET family. */
struct inet_protosw sctp_protosw = { static struct inet_protosw sctp_seqpacket_protosw = {
.type = SOCK_SEQPACKET, .type = SOCK_SEQPACKET,
.protocol = IPPROTO_SCTP, .protocol = IPPROTO_SCTP,
.prot = &sctp_prot, .prot = &sctp_prot,
...@@ -691,6 +762,15 @@ struct inet_protosw sctp_protosw = { ...@@ -691,6 +762,15 @@ struct inet_protosw sctp_protosw = {
.no_check = 0, .no_check = 0,
.flags = SCTP_PROTOSW_FLAG .flags = SCTP_PROTOSW_FLAG
}; };
static struct inet_protosw sctp_stream_protosw = {
.type = SOCK_STREAM,
.protocol = IPPROTO_SCTP,
.prot = &sctp_prot,
.ops = &inet_seqpacket_ops,
.capability = -1,
.no_check = 0,
.flags = SCTP_PROTOSW_FLAG
};
/* Register with IP layer. */ /* Register with IP layer. */
static struct inet_protocol sctp_protocol = { static struct inet_protocol sctp_protocol = {
...@@ -797,8 +877,9 @@ __init int sctp_init(void) ...@@ -797,8 +877,9 @@ __init int sctp_init(void)
if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0)
return -EAGAIN; return -EAGAIN;
/* Add SCTP to inetsw linked list. */ /* Add SCTP(TCP and UDP style) to inetsw linked list. */
inet_register_protosw(&sctp_protosw); inet_register_protosw(&sctp_seqpacket_protosw);
inet_register_protosw(&sctp_stream_protosw);
/* Allocate and initialise sctp mibs. */ /* Allocate and initialise sctp mibs. */
status = init_sctp_mibs(); status = init_sctp_mibs();
...@@ -944,7 +1025,8 @@ __init int sctp_init(void) ...@@ -944,7 +1025,8 @@ __init int sctp_init(void)
cleanup_sctp_mibs(); cleanup_sctp_mibs();
err_init_mibs: err_init_mibs:
inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
inet_unregister_protosw(&sctp_protosw); inet_unregister_protosw(&sctp_seqpacket_protosw);
inet_unregister_protosw(&sctp_stream_protosw);
return status; return status;
} }
...@@ -977,7 +1059,8 @@ __exit void sctp_exit(void) ...@@ -977,7 +1059,8 @@ __exit void sctp_exit(void)
cleanup_sctp_mibs(); cleanup_sctp_mibs();
inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
inet_unregister_protosw(&sctp_protosw); inet_unregister_protosw(&sctp_seqpacket_protosw);
inet_unregister_protosw(&sctp_stream_protosw);
} }
module_init(sctp_init); module_init(sctp_init);
......
...@@ -66,29 +66,6 @@ ...@@ -66,29 +66,6 @@
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 4: This parameter, when present, specifies all the
* address types the sending endpoint can support. The absence
* of this parameter indicates that the sending endpoint can
* support any address type.
*/
static const sctp_supported_addrs_param_t sat_param = {
{
SCTP_PARAM_SUPPORTED_ADDRESS_TYPES,
__constant_htons(SCTP_SAT_LEN),
}
};
/* gcc 3.2 doesn't allow initialization of zero-length arrays. So the above
* structure is split and the address types array is initialized using a
* fixed length array.
*/
static const __u16 sat_addr_types[2] = {
SCTP_PARAM_IPV4_ADDRESS,
SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,)
};
/* RFC 2960 3.3.2 Initiation (INIT) (1) /* RFC 2960 3.3.2 Initiation (INIT) (1)
* *
* Note 2: The ECN capable field is reserved for future use of * Note 2: The ECN capable field is reserved for future use of
...@@ -174,7 +151,10 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, ...@@ -174,7 +151,10 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
union sctp_params addrs; union sctp_params addrs;
size_t chunksize; size_t chunksize;
sctp_chunk_t *retval = NULL; sctp_chunk_t *retval = NULL;
int addrs_len = 0; int num_types, addrs_len = 0;
struct sctp_opt *sp;
sctp_supported_addrs_param_t sat;
__u16 types[2];
/* RFC 2960 3.3.2 Initiation (INIT) (1) /* RFC 2960 3.3.2 Initiation (INIT) (1)
* *
...@@ -195,7 +175,11 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, ...@@ -195,7 +175,11 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
init.num_inbound_streams = htons(asoc->c.sinit_max_instreams); init.num_inbound_streams = htons(asoc->c.sinit_max_instreams);
init.initial_tsn = htonl(asoc->c.initial_tsn); init.initial_tsn = htonl(asoc->c.initial_tsn);
chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN; /* How many address types are needed? */
sp = sctp_sk(asoc->base.sk);
num_types = sp->pf->supported_addrs(sp, types);
chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
chunksize += sizeof(ecap_param); chunksize += sizeof(ecap_param);
chunksize += vparam_len; chunksize += vparam_len;
...@@ -220,8 +204,18 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, ...@@ -220,8 +204,18 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
retval->param_hdr.v = retval->param_hdr.v =
sctp_addto_chunk(retval, addrs_len, addrs.v); sctp_addto_chunk(retval, addrs_len, addrs.v);
sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &sat_param); /* RFC 2960 3.3.2 Initiation (INIT) (1)
sctp_addto_chunk(retval, sizeof(sat_addr_types), sat_addr_types); *
* Note 4: This parameter, when present, specifies all the
* address types the sending endpoint can support. The absence
* of this parameter indicates that the sending endpoint can
* support any address type.
*/
sat.param_hdr.type = SCTP_PARAM_SUPPORTED_ADDRESS_TYPES;
sat.param_hdr.length = htons(SCTP_SAT_LEN(num_types));
sctp_addto_chunk(retval, sizeof(sat), &sat);
sctp_addto_chunk(retval, num_types * sizeof(__u16), &types);
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
nodata: nodata:
if (addrs.v) if (addrs.v)
...@@ -604,7 +598,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc) ...@@ -604,7 +598,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc)
/* Initialize the SACK header. */ /* Initialize the SACK header. */
sack.cum_tsn_ack = htonl(ctsn); sack.cum_tsn_ack = htonl(ctsn);
sack.a_rwnd = htonl(asoc->rwnd); sack.a_rwnd = htonl(asoc->a_rwnd);
sack.num_gap_ack_blocks = htons(num_gabs); sack.num_gap_ack_blocks = htons(num_gabs);
sack.num_dup_tsns = htons(num_dup_tsns); sack.num_dup_tsns = htons(num_dup_tsns);
...@@ -1159,7 +1153,7 @@ int sctp_datachunks_from_user(sctp_association_t *asoc, ...@@ -1159,7 +1153,7 @@ int sctp_datachunks_from_user(sctp_association_t *asoc,
first_len = max; first_len = max;
/* Encourage Cookie-ECHO bundling. */ /* Encourage Cookie-ECHO bundling. */
if (asoc->state < SCTP_STATE_ESTABLISHED) { if (asoc->state < SCTP_STATE_COOKIE_ECHOED) {
whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN); whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
/* Account for the DATA to be bundled with the COOKIE-ECHO. */ /* Account for the DATA to be bundled with the COOKIE-ECHO. */
...@@ -1282,7 +1276,7 @@ void sctp_chunk_assign_tsn(sctp_chunk_t *chunk) ...@@ -1282,7 +1276,7 @@ void sctp_chunk_assign_tsn(sctp_chunk_t *chunk)
* assign a TSN. * assign a TSN.
*/ */
chunk->subh.data_hdr->tsn = chunk->subh.data_hdr->tsn =
htonl(__sctp_association_get_next_tsn(chunk->asoc)); htonl(sctp_association_get_next_tsn(chunk->asoc));
chunk->has_tsn = 1; chunk->has_tsn = 1;
} }
} }
......
...@@ -105,8 +105,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *, ...@@ -105,8 +105,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *,
#define DEBUG_POST_SFX \ #define DEBUG_POST_SFX \
SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \
error, asoc, \ error, asoc, \
sctp_state_tbl[sctp_id2assoc(ep->base.sk, \ sctp_state_tbl[asoc?asoc->state:SCTP_STATE_CLOSED])
sctp_assoc2id(asoc))?asoc->state:SCTP_STATE_CLOSED])
/* /*
* This is the master state machine processing function. * This is the master state machine processing function.
...@@ -256,7 +255,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -256,7 +255,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_t *cmd; sctp_cmd_t *cmd;
sctp_chunk_t *new_obj; sctp_chunk_t *new_obj;
sctp_chunk_t *chunk = NULL; sctp_chunk_t *chunk = NULL;
sctp_packet_t *packet; struct sctp_packet *packet;
struct list_head *pos; struct list_head *pos;
struct timer_list *timer; struct timer_list *timer;
unsigned long timeout; unsigned long timeout;
...@@ -716,13 +715,12 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands) ...@@ -716,13 +715,12 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands)
asoc->peer.sack_needed = 1; asoc->peer.sack_needed = 1;
goto out; goto out;
} else { } else {
if (asoc->a_rwnd > asoc->rwnd)
asoc->a_rwnd = asoc->rwnd;
sack = sctp_make_sack(asoc); sack = sctp_make_sack(asoc);
if (!sack) if (!sack)
goto nomem; goto nomem;
/* Update the last advertised rwnd value. */
asoc->a_rwnd = asoc->rwnd;
asoc->peer.sack_needed = 0; asoc->peer.sack_needed = 0;
error = sctp_outq_tail(&asoc->outqueue, sack); error = sctp_outq_tail(&asoc->outqueue, sack);
...@@ -1223,13 +1221,35 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, ...@@ -1223,13 +1221,35 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_state_t state) sctp_state_t state)
{ {
struct sock *sk = asoc->base.sk;
struct sctp_opt *sp = sctp_sk(sk);
asoc->state = state; asoc->state = state;
asoc->state_timestamp = jiffies; asoc->state_timestamp = jiffies;
/* Wake up any process waiting for the association to if ((SCTP_STATE_ESTABLISHED == asoc->state) ||
* get established. (SCTP_STATE_CLOSED == asoc->state)) {
/* Wake up any processes waiting in the asoc's wait queue in
* sctp_wait_for_connect() or sctp_wait_for_sndbuf().
*/ */
if ((SCTP_STATE_ESTABLISHED == asoc->state) && if (waitqueue_active(&asoc->wait))
(waitqueue_active(&asoc->wait)))
wake_up_interruptible(&asoc->wait); wake_up_interruptible(&asoc->wait);
/* Wake up any processes waiting in the sk's sleep queue of
* a TCP-style or UDP-style peeled-off socket in
* sctp_wait_for_accept() or sctp_wait_for_packet().
* For a UDP-style socket, the waiters are woken up by the
* notifications.
*/
if (SCTP_SOCKET_UDP != sp->type)
sk->state_change(sk);
}
/* Change the sk->state of a TCP-style socket that has sucessfully
* completed a connect() call.
*/
if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
(SCTP_SOCKET_TCP == sp->type) && (SCTP_SS_CLOSED == sk->state))
sk->state = SCTP_SS_ESTABLISHED;
} }
...@@ -189,7 +189,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -189,7 +189,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_association_t *new_asoc; sctp_association_t *new_asoc;
sctp_chunk_t *err_chunk; sctp_chunk_t *err_chunk;
sctp_packet_t *packet; struct sctp_packet *packet;
sctp_unrecognized_param_t *unk_param; sctp_unrecognized_param_t *unk_param;
int len; int len;
...@@ -354,10 +354,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, ...@@ -354,10 +354,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep,
sctp_init_chunk_t *initchunk; sctp_init_chunk_t *initchunk;
__u32 init_tag; __u32 init_tag;
sctp_chunk_t *err_chunk; sctp_chunk_t *err_chunk;
sctp_packet_t *packet; struct sctp_packet *packet;
sctp_disposition_t ret; sctp_disposition_t ret;
/* 6.10 Bundling /* 6.10 Bundling
* An endpoint MUST NOT bundle INIT, INIT ACK or * An endpoint MUST NOT bundle INIT, INIT ACK or
* SHUTDOWN COMPLETE with any other chunks. * SHUTDOWN COMPLETE with any other chunks.
...@@ -912,14 +911,14 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa, ...@@ -912,14 +911,14 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
int len; int len;
sctp_packet_t *pkt; struct sctp_packet *pkt;
sctp_addr_param_t *addrparm; sctp_addr_param_t *addrparm;
sctp_errhdr_t *errhdr; sctp_errhdr_t *errhdr;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)]; char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)];
/* Build the error on the stack. We are way to malloc /* Build the error on the stack. We are way to malloc crazy
* malloc crazy throughout the code today. * throughout the code today.
*/ */
errhdr = (sctp_errhdr_t *)buffer; errhdr = (sctp_errhdr_t *)buffer;
addrparm = (sctp_addr_param_t *)errhdr->variable; addrparm = (sctp_addr_param_t *)errhdr->variable;
...@@ -1105,11 +1104,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -1105,11 +1104,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_association_t *new_asoc; sctp_association_t *new_asoc;
sctp_chunk_t *err_chunk; sctp_chunk_t *err_chunk;
sctp_packet_t *packet; struct sctp_packet *packet;
sctp_unrecognized_param_t *unk_param; sctp_unrecognized_param_t *unk_param;
int len; int len;
/* 6.10 Bundling /* 6.10 Bundling
* An endpoint MUST NOT bundle INIT, INIT ACK or * An endpoint MUST NOT bundle INIT, INIT ACK or
* SHUTDOWN COMPLETE with any other chunks. * SHUTDOWN COMPLETE with any other chunks.
...@@ -2751,7 +2749,7 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep, ...@@ -2751,7 +2749,7 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_packet_t *packet = NULL; struct sctp_packet *packet = NULL;
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *abort; sctp_chunk_t *abort;
...@@ -2953,7 +2951,7 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, ...@@ -2953,7 +2951,7 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_packet_t *packet = NULL; struct sctp_packet *packet = NULL;
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *shut; sctp_chunk_t *shut;
...@@ -4377,13 +4375,13 @@ sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk) ...@@ -4377,13 +4375,13 @@ sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk)
/* Create an ABORT packet to be sent as a response, with the specified /* Create an ABORT packet to be sent as a response, with the specified
* error causes. * error causes.
*/ */
sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, struct sctp_packet *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
const sctp_association_t *asoc, const sctp_association_t *asoc,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
const void *payload, const void *payload,
size_t paylen) size_t paylen)
{ {
sctp_packet_t *packet; struct sctp_packet *packet;
sctp_chunk_t *abort; sctp_chunk_t *abort;
packet = sctp_ootb_pkt_new(asoc, chunk); packet = sctp_ootb_pkt_new(asoc, chunk);
...@@ -4413,10 +4411,10 @@ sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, ...@@ -4413,10 +4411,10 @@ sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
} }
/* Allocate a packet for responding in the OOTB conditions. */ /* Allocate a packet for responding in the OOTB conditions. */
sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, struct sctp_packet *sctp_ootb_pkt_new(const sctp_association_t *asoc,
const sctp_chunk_t *chunk) const sctp_chunk_t *chunk)
{ {
sctp_packet_t *packet; struct sctp_packet *packet;
struct sctp_transport *transport; struct sctp_transport *transport;
__u16 sport; __u16 sport;
__u16 dport; __u16 dport;
...@@ -4449,7 +4447,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, ...@@ -4449,7 +4447,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
goto nomem; goto nomem;
/* Allocate a new packet for sending the response. */ /* Allocate a new packet for sending the response. */
packet = t_new(sctp_packet_t, GFP_ATOMIC); packet = t_new(struct sctp_packet, GFP_ATOMIC);
if (!packet) if (!packet)
goto nomem_packet; goto nomem_packet;
...@@ -4471,7 +4469,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, ...@@ -4471,7 +4469,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
} }
/* Free the packet allocated earlier for responding in the OOTB condition. */ /* Free the packet allocated earlier for responding in the OOTB condition. */
void sctp_ootb_pkt_free(sctp_packet_t *packet) void sctp_ootb_pkt_free(struct sctp_packet *packet)
{ {
sctp_transport_free(packet->transport); sctp_transport_free(packet->transport);
sctp_packet_free(packet); sctp_packet_free(packet);
...@@ -4484,7 +4482,7 @@ void sctp_send_stale_cookie_err(const sctp_endpoint_t *ep, ...@@ -4484,7 +4482,7 @@ void sctp_send_stale_cookie_err(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands, sctp_cmd_seq_t *commands,
sctp_chunk_t *err_chunk) sctp_chunk_t *err_chunk)
{ {
sctp_packet_t *packet; struct sctp_packet *packet;
if (err_chunk) { if (err_chunk) {
packet = sctp_ootb_pkt_new(asoc, chunk); packet = sctp_ootb_pkt_new(asoc, chunk);
......
...@@ -88,12 +88,46 @@ static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, ...@@ -88,12 +88,46 @@ static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p,
int msg_len); int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo);
static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int); static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int);
static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk); static int sctp_autobind(struct sock *sk);
static void sctp_sock_migrate(struct sock *, struct sock *,
struct sctp_association *, sctp_socket_type_t);
/* Look up the association by its id. If this is not a UDP-style
* socket, the ID field is always ignored.
*/
sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{
sctp_association_t *asoc = NULL;
/* If this is not a UDP-style socket, assoc id should be
* ignored.
*/
if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) {
if (!list_empty(&sctp_sk(sk)->ep->asocs))
asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
sctp_association_t, asocs);
return asoc;
}
/* First, verify that this is a kernel address. */
if (sctp_is_valid_kaddr((unsigned long) id)) {
sctp_association_t *temp = (sctp_association_t *) id;
/* Verify that this _is_ an sctp_association_t
* data structure and if so, that the socket matches.
*/
if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) &&
(temp->base.sk == sk))
asoc = temp;
}
return asoc;
}
/* API 3.1.2 bind() - UDP Style Syntax /* API 3.1.2 bind() - UDP Style Syntax
* The syntax of bind() is, * The syntax of bind() is,
...@@ -818,19 +852,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -818,19 +852,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
} }
} else { } else {
/* For a peeled-off socket, ignore any associd specified by
* the user with SNDRCVINFO.
*/
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
if (list_empty(&ep->asocs)) {
err = -EINVAL;
goto out_unlock;
}
asoc = list_entry(ep->asocs.next, sctp_association_t,
asocs);
} else if (associd) {
asoc = sctp_id2assoc(sk, associd); asoc = sctp_id2assoc(sk, associd);
}
if (!asoc) { if (!asoc) {
err = -EINVAL; err = -EINVAL;
goto out_unlock; goto out_unlock;
...@@ -1126,17 +1148,19 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1126,17 +1148,19 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
int err = 0; int err = 0;
int skb_len; int skb_len;
SCTP_DEBUG_PRINTK("sctp_recvmsg(" SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %d, %s: %d, %s: "
"%s: %p, %s: %p, %s: %d, %s: %d, %s: " "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg,
"0x%x, %s: %p)\n", "len", len, "knoblauch", noblock,
"sk", sk, "flags", flags, "addr_len", addr_len);
"msghdr", msg,
"len", len,
"knoblauch", noblock,
"flags", flags,
"addr_len", addr_len);
sctp_lock_sock(sk); sctp_lock_sock(sk);
if ((SCTP_SOCKET_TCP == sp->type) &&
(SCTP_SS_ESTABLISHED != sk->state)) {
err = -ENOTCONN;
goto out;
}
skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); skb = sctp_skb_recv_datagram(sk, flags, noblock, &err);
if (!skb) if (!skb)
goto out; goto out;
...@@ -1207,7 +1231,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1207,7 +1231,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
return err; return err;
} }
static inline int sctp_setsockopt_disable_fragments(struct sock *sk, static int sctp_setsockopt_disable_fragments(struct sock *sk,
char *optval, int optlen) char *optval, int optlen)
{ {
int val; int val;
...@@ -1223,7 +1247,7 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk, ...@@ -1223,7 +1247,7 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk,
return 0; return 0;
} }
static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, static int sctp_setsockopt_events(struct sock *sk, char *optval,
int optlen) int optlen)
{ {
if (optlen != sizeof(struct sctp_event_subscribe)) if (optlen != sizeof(struct sctp_event_subscribe))
...@@ -1233,7 +1257,7 @@ static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, ...@@ -1233,7 +1257,7 @@ static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval,
return 0; return 0;
} }
static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, static int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
int optlen) int optlen)
{ {
struct sctp_opt *sp = sctp_sk(sk); struct sctp_opt *sp = sctp_sk(sk);
...@@ -1250,9 +1274,8 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, ...@@ -1250,9 +1274,8 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
return 0; return 0;
} }
static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, static int sctp_setsockopt_peer_addr_params(struct sock *sk,
char *optval, char *optval, int optlen)
int optlen)
{ {
struct sctp_paddrparams params; struct sctp_paddrparams params;
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -1290,8 +1313,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, ...@@ -1290,8 +1313,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans); error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans);
if (error) if (error)
return error; return error;
} } else {
else {
/* The value of the heartbeat interval, in milliseconds. A value of 0, /* The value of the heartbeat interval, in milliseconds. A value of 0,
* when modifying the parameter, specifies that the heartbeat on this * when modifying the parameter, specifies that the heartbeat on this
* address should be disabled. * address should be disabled.
...@@ -1311,7 +1333,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, ...@@ -1311,7 +1333,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
return 0; return 0;
} }
static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval, static int sctp_setsockopt_initmsg(struct sock *sk, char *optval,
int optlen) int optlen)
{ {
if (optlen != sizeof(struct sctp_initmsg)) if (optlen != sizeof(struct sctp_initmsg))
...@@ -1336,7 +1358,7 @@ static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval, ...@@ -1336,7 +1358,7 @@ static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval,
* sinfo_timetolive. The user must provide the sinfo_assoc_id field in * sinfo_timetolive. The user must provide the sinfo_assoc_id field in
* to this call if the caller is using the UDP model. * to this call if the caller is using the UDP model.
*/ */
static inline int sctp_setsockopt_set_default_send_param(struct sock *sk, static int sctp_setsockopt_default_send_param(struct sock *sk,
char *optval, int optlen) char *optval, int optlen)
{ {
struct sctp_sndrcvinfo info; struct sctp_sndrcvinfo info;
...@@ -1359,6 +1381,66 @@ static inline int sctp_setsockopt_set_default_send_param(struct sock *sk, ...@@ -1359,6 +1381,66 @@ static inline int sctp_setsockopt_set_default_send_param(struct sock *sk,
return 0; return 0;
} }
/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
*
* Requests that the local SCTP stack use the enclosed peer address as
* the association primary. The enclosed address must be one of the
* association peer's addresses.
*/
static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
{
struct sctp_setpeerprim prim;
struct sctp_association *asoc;
union sctp_addr *addr;
struct sctp_transport *trans;
if (optlen != sizeof(struct sctp_setpeerprim))
return -EINVAL;
if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim)))
return -EFAULT;
asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
if (!asoc)
return -EINVAL;
/* Find the requested address. */
addr = (union sctp_addr *) &(prim.sspp_addr);
trans = sctp_assoc_lookup_paddr(asoc, addr);
if (!trans)
return -ENOENT;
sctp_assoc_set_primary(asoc, trans);
return 0;
}
/*
*
* 7.1.5 SCTP_NODELAY
*
* Turn on/off any Nagle-like algorithm. This means that packets are
* generally sent as soon as possible and no unnecessary delays are
* introduced, at the cost of more packets in the network. Expects an
* integer boolean flag.
*/
static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
int optlen)
{
__u8 val;
if (optlen < sizeof(__u8))
return -EINVAL;
if (get_user(val, (__u8 *)optval))
return -EFAULT;
sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
return 0;
}
/* API 6.2 setsockopt(), getsockopt() /* API 6.2 setsockopt(), getsockopt()
* *
* Applications use setsockopt() and getsockopt() to set or retrieve * Applications use setsockopt() and getsockopt() to set or retrieve
...@@ -1434,7 +1516,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1434,7 +1516,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
break; break;
case SCTP_SET_EVENTS: case SCTP_SET_EVENTS:
retval = sctp_setsockopt_set_events(sk, optval, optlen); retval = sctp_setsockopt_events(sk, optval, optlen);
break; break;
case SCTP_AUTOCLOSE: case SCTP_AUTOCLOSE:
...@@ -1442,8 +1524,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1442,8 +1524,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
break; break;
case SCTP_SET_PEER_ADDR_PARAMS: case SCTP_SET_PEER_ADDR_PARAMS:
retval = sctp_setsockopt_set_peer_addr_params(sk, optval, retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
optlen);
break; break;
case SCTP_INITMSG: case SCTP_INITMSG:
...@@ -1451,8 +1532,16 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1451,8 +1532,16 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
break; break;
case SCTP_SET_DEFAULT_SEND_PARAM: case SCTP_SET_DEFAULT_SEND_PARAM:
retval = sctp_setsockopt_set_default_send_param(sk, retval = sctp_setsockopt_default_send_param(sk, optval,
optval, optlen); optlen);
break;
case SCTP_SET_PEER_PRIMARY_ADDR:
retval = sctp_setsockopt_peer_prim(sk, optval, optlen);
break;
case SCTP_NODELAY:
retval = sctp_setsockopt_nodelay(sk, optval, optlen);
break; break;
default: default:
...@@ -1503,8 +1592,14 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1503,8 +1592,14 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sp = sctp_sk(sk); sp = sctp_sk(sk);
ep = sp->ep; ep = sp->ep;
/* connect() cannot be done on a peeled-off socket. */ /* connect() cannot be done on a socket that is already in ESTABLISHED
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) { * state - UDP-style peeled off socket or a TCP-style socket that
* is already connected.
* It cannot be done even on a TCP-style listening socket.
*/
if ((SCTP_SS_ESTABLISHED == sk->state) ||
((SCTP_SOCKET_TCP == sp->type) &&
(SCTP_SS_LISTENING == sk->state))) {
err = -EISCONN; err = -EISCONN;
goto out_unlock; goto out_unlock;
} }
...@@ -1513,6 +1608,8 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1513,6 +1608,8 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
if (err) if (err)
goto out_unlock; goto out_unlock;
if (addr_len > sizeof(to))
addr_len = sizeof(to);
memcpy(&to, uaddr, addr_len); memcpy(&to, uaddr, addr_len);
to.v4.sin_port = ntohs(to.v4.sin_port); to.v4.sin_port = ntohs(to.v4.sin_port);
...@@ -1585,13 +1682,63 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags) ...@@ -1585,13 +1682,63 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
return -EOPNOTSUPP; /* STUB */ return -EOPNOTSUPP; /* STUB */
} }
/* FIXME: Write comments. */ /* 4.1.4 accept() - TCP Style Syntax
*
* Applications use accept() call to remove an established SCTP
* association from the accept queue of the endpoint. A new socket
* descriptor will be returned from accept() to represent the newly
* formed association.
*/
SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err) SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
{ {
int error = -EOPNOTSUPP; struct sctp_opt *sp;
struct sctp_endpoint *ep;
struct sock *newsk = NULL;
struct sctp_association *assoc;
long timeo;
int error = 0;
sctp_lock_sock(sk);
sp = sctp_sk(sk);
ep = sp->ep;
if (SCTP_SOCKET_TCP != sp->type) {
error = -EOPNOTSUPP;
goto out;
}
if (SCTP_SS_LISTENING != sk->state) {
error = -EINVAL;
goto out;
}
timeo = sock_rcvtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
error = sctp_wait_for_accept(sk, timeo);
if (error)
goto out;
/* We treat the list of associations on the endpoint as the accept
* queue and pick the first association on the list.
*/
assoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
newsk = sp->pf->create_accept_sk(sk, assoc);
if (!newsk) {
error = -ENOMEM;
goto out;
}
/* Populate the fields of the newsk from the oldsk and migrate the
* assoc to the newsk.
*/
sctp_sock_migrate(sk, newsk, assoc, SCTP_SOCKET_TCP);
out:
sctp_release_sock(sk);
*err = error; *err = error;
return NULL; return newsk;
} }
/* FIXME: Write Comments. */ /* FIXME: Write Comments. */
...@@ -1607,7 +1754,7 @@ SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) ...@@ -1607,7 +1754,7 @@ SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
SCTP_STATIC int sctp_init_sock(struct sock *sk) SCTP_STATIC int sctp_init_sock(struct sock *sk)
{ {
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_protocol_t *proto; struct sctp_protocol *proto;
struct sctp_opt *sp; struct sctp_opt *sp;
SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk); SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
...@@ -1617,7 +1764,16 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) ...@@ -1617,7 +1764,16 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp = sctp_sk(sk); sp = sctp_sk(sk);
/* Initialize the SCTP per socket area. */ /* Initialize the SCTP per socket area. */
switch (sk->type) {
case SOCK_SEQPACKET:
sp->type = SCTP_SOCKET_UDP; sp->type = SCTP_SOCKET_UDP;
break;
case SOCK_STREAM:
sp->type = SCTP_SOCKET_TCP;
break;
default:
return -ESOCKTNOSUPPORT;
}
/* FIXME: The next draft (04) of the SCTP Sockets Extensions /* FIXME: The next draft (04) of the SCTP Sockets Extensions
* should include a socket option for manipulating these * should include a socket option for manipulating these
...@@ -1665,7 +1821,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) ...@@ -1665,7 +1821,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->disable_fragments = 0; sp->disable_fragments = 0;
/* Turn on/off any Nagle-like algorithm. */ /* Turn on/off any Nagle-like algorithm. */
sp->nodelay = 0; sp->nodelay = 1;
/* Auto-close idle associations after the configured /* Auto-close idle associations after the configured
* number of seconds. A value of 0 disables this * number of seconds. A value of 0 disables this
...@@ -1714,11 +1870,17 @@ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how) ...@@ -1714,11 +1870,17 @@ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
/* STUB */ /* STUB */
} }
/* 7.2.1 Association Status (SCTP_STATUS)
* Applications can retrieve current status information about an
* association, including association state, peer receiver window size,
* number of unacked data chunks, and number of data chunks pending
* receipt. This information is read-only.
*/
static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
int *optlen) int *optlen)
{ {
struct sctp_status status; struct sctp_status status;
sctp_endpoint_t *ep;
sctp_association_t *assoc = NULL; sctp_association_t *assoc = NULL;
struct sctp_transport *transport; struct sctp_transport *transport;
sctp_assoc_t associd; sctp_assoc_t associd;
...@@ -1735,21 +1897,11 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1735,21 +1897,11 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
} }
associd = status.sstat_assoc_id; associd = status.sstat_assoc_id;
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) {
assoc = sctp_id2assoc(sk, associd); assoc = sctp_id2assoc(sk, associd);
if (!assoc) { if (!assoc) {
retval = -EINVAL; retval = -EINVAL;
goto out; goto out;
} }
} else {
ep = sctp_sk(sk)->ep;
if (list_empty(&ep->asocs)) {
retval = -EINVAL;
goto out;
}
assoc = list_entry(ep->asocs.next, sctp_association_t, asocs);
}
transport = assoc->peer.primary_path; transport = assoc->peer.primary_path;
...@@ -1788,7 +1940,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1788,7 +1940,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
return (retval); return (retval);
} }
static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len, static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
char *optval, int *optlen) char *optval, int *optlen)
{ {
int val; int val;
...@@ -1805,7 +1957,7 @@ static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len, ...@@ -1805,7 +1957,7 @@ static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
return 0; return 0;
} }
static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen) static int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen)
{ {
if (len != sizeof(struct sctp_event_subscribe)) if (len != sizeof(struct sctp_event_subscribe))
return -EINVAL; return -EINVAL;
...@@ -1814,7 +1966,7 @@ static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *opt ...@@ -1814,7 +1966,7 @@ static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *opt
return 0; return 0;
} }
static inline int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen) static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen)
{ {
/* Applicable to UDP-style socket only */ /* Applicable to UDP-style socket only */
if (SCTP_SOCKET_TCP == sctp_sk(sk)->type) if (SCTP_SOCKET_TCP == sctp_sk(sk)->type)
...@@ -1832,11 +1984,6 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1832,11 +1984,6 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
struct sock *oldsk = assoc->base.sk; struct sock *oldsk = assoc->base.sk;
struct sock *newsk; struct sock *newsk;
struct socket *tmpsock; struct socket *tmpsock;
sctp_endpoint_t *newep;
struct sctp_opt *oldsp = sctp_sk(oldsk);
struct sctp_opt *newsp;
struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event;
int err = 0; int err = 0;
/* An association cannot be branched off from an already peeled-off /* An association cannot be branched off from an already peeled-off
...@@ -1846,88 +1993,24 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1846,88 +1993,24 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Create a new socket. */ /* Create a new socket. */
err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock); err = sock_create(oldsk->family, SOCK_SEQPACKET, IPPROTO_SCTP,
&tmpsock);
if (err < 0) if (err < 0)
return err; return err;
newsk = tmpsock->sk; newsk = tmpsock->sk;
newsp = sctp_sk(newsk);
newep = newsp->ep;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
*/
newsk->sndbuf = oldsk->sndbuf;
newsk->rcvbuf = oldsk->rcvbuf;
*newsp = *oldsp;
/* Restore the ep value that was overwritten with the above structure
* copy.
*/
newsp->ep = newep;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(&newsk->receive_queue, skb);
}
}
/* Clean up an messages pending delivery due to partial /* Populate the fields of the newsk from the oldsk and migrate the
* delivery. Three cases: * assoc to the newsk.
* 1) No partial deliver; no work.
* 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
* 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue.
*/ */
skb_queue_head_init(&newsp->pd_lobby); sctp_sock_migrate(oldsk, newsk, assoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;;
if (sctp_sk(oldsk)->pd_mode) {
struct sk_buff_head *queue;
/* Decide which queue to move pd_lobby skbs to. */
if (assoc->ulpq.pd_mode) {
queue = &newsp->pd_lobby;
} else
queue = &newsk->receive_queue;
/* Walk through the pd_lobby, looking for skbs that
* need moved to the new socket.
*/
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(queue, skb);
}
}
/* Clear up any skbs waiting for the partial
* delivery to finish.
*/
if (assoc->ulpq.pd_mode)
sctp_clear_pd(oldsk);
}
/* Set the type of socket to indicate that it is peeled off from the
* original socket.
*/
newsp->type = SCTP_SOCKET_UDP_HIGH_BANDWIDTH;
/* Migrate the association to the new socket. */
sctp_assoc_migrate(assoc, newsk);
*newsock = tmpsock; *newsock = tmpsock;
return err; return err;
} }
static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen) static int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen)
{ {
sctp_peeloff_arg_t peeloff; sctp_peeloff_arg_t peeloff;
struct socket *newsock; struct socket *newsock;
...@@ -1970,8 +2053,8 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval ...@@ -1970,8 +2053,8 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
return retval; return retval;
} }
static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
int len, char *optval, int *optlen) char *optval, int *optlen)
{ {
struct sctp_paddrparams params; struct sctp_paddrparams params;
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -2014,7 +2097,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, ...@@ -2014,7 +2097,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
return 0; return 0;
} }
static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen) static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen)
{ {
if (len != sizeof(struct sctp_initmsg)) if (len != sizeof(struct sctp_initmsg))
return -EINVAL; return -EINVAL;
...@@ -2023,7 +2106,7 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval ...@@ -2023,7 +2106,7 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval
return 0; return 0;
} }
static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len, static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
char *optval, int *optlen) char *optval, int *optlen)
{ {
sctp_assoc_t id; sctp_assoc_t id;
...@@ -2053,7 +2136,7 @@ static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len, ...@@ -2053,7 +2136,7 @@ static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len,
return 0; return 0;
} }
static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len, static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
char *optval, int *optlen) char *optval, int *optlen)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -2093,7 +2176,7 @@ static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len, ...@@ -2093,7 +2176,7 @@ static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len,
return 0; return 0;
} }
static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len, static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
char *optval, int *optlen) char *optval, int *optlen)
{ {
sctp_assoc_t id; sctp_assoc_t id;
...@@ -2132,7 +2215,7 @@ static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len, ...@@ -2132,7 +2215,7 @@ static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len,
return 0; return 0;
} }
static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
char *optval, int *optlen) char *optval, int *optlen)
{ {
sctp_bind_addr_t *bp; sctp_bind_addr_t *bp;
...@@ -2183,6 +2266,40 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, ...@@ -2183,6 +2266,40 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len,
return 0; return 0;
} }
/* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
*
* Requests that the local SCTP stack use the enclosed peer address as
* the association primary. The enclosed address must be one of the
* association peer's addresses.
*/
static int sctp_getsockopt_peer_prim(struct sock *sk, int len,
char *optval, int *optlen)
{
struct sctp_setpeerprim prim;
struct sctp_association *asoc;
if (len != sizeof(struct sctp_setpeerprim))
return -EINVAL;
if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim)))
return -EFAULT;
asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
if (!asoc)
return -EINVAL;
if (!asoc->peer.primary_path)
return -ENOTCONN;
memcpy(&prim.sspp_addr, &asoc->peer.primary_path->ipaddr,
sizeof(union sctp_addr));
if (copy_to_user(optval, &prim, sizeof(struct sctp_setpeerprim)))
return -EFAULT;
return 0;
}
/* /*
* *
* 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM) * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM)
...@@ -2200,7 +2317,7 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, ...@@ -2200,7 +2317,7 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len,
* *
* For getsockopt, it get the default sctp_sndrcvinfo structure. * For getsockopt, it get the default sctp_sndrcvinfo structure.
*/ */
static inline int sctp_getsockopt_set_default_send_param(struct sock *sk, static int sctp_getsockopt_default_send_param(struct sock *sk,
int len, char *optval, int *optlen) int len, char *optval, int *optlen)
{ {
struct sctp_sndrcvinfo info; struct sctp_sndrcvinfo info;
...@@ -2227,6 +2344,33 @@ static inline int sctp_getsockopt_set_default_send_param(struct sock *sk, ...@@ -2227,6 +2344,33 @@ static inline int sctp_getsockopt_set_default_send_param(struct sock *sk,
return 0; return 0;
} }
/*
*
* 7.1.5 SCTP_NODELAY
*
* Turn on/off any Nagle-like algorithm. This means that packets are
* generally sent as soon as possible and no unnecessary delays are
* introduced, at the cost of more packets in the network. Expects an
* integer boolean flag.
*/
static int sctp_getsockopt_nodelay(struct sock *sk, int len,
char *optval, int *optlen)
{
__u8 val;
if (len < sizeof(__u8))
return -EINVAL;
len = sizeof(__u8);
val = (sctp_sk(sk)->nodelay == 1);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen) char *optval, int *optlen)
{ {
...@@ -2257,58 +2401,52 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, ...@@ -2257,58 +2401,52 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_STATUS: case SCTP_STATUS:
retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen); retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
break; break;
case SCTP_DISABLE_FRAGMENTS: case SCTP_DISABLE_FRAGMENTS:
retval = sctp_getsockopt_disable_fragments(sk, len, optval, retval = sctp_getsockopt_disable_fragments(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_SET_EVENTS: case SCTP_SET_EVENTS:
retval = sctp_getsockopt_set_events(sk, len, optval, optlen); retval = sctp_getsockopt_set_events(sk, len, optval, optlen);
break; break;
case SCTP_AUTOCLOSE: case SCTP_AUTOCLOSE:
retval = sctp_getsockopt_autoclose(sk, len, optval, optlen); retval = sctp_getsockopt_autoclose(sk, len, optval, optlen);
break; break;
case SCTP_SOCKOPT_PEELOFF: case SCTP_SOCKOPT_PEELOFF:
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
break; break;
case SCTP_GET_PEER_ADDR_PARAMS: case SCTP_GET_PEER_ADDR_PARAMS:
retval = sctp_getsockopt_get_peer_addr_params(sk, len, optval, retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_INITMSG: case SCTP_INITMSG:
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen); retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break; break;
case SCTP_GET_PEER_ADDRS_NUM: case SCTP_GET_PEER_ADDRS_NUM:
retval = sctp_getsockopt_get_peer_addrs_num(sk, len, optval, retval = sctp_getsockopt_peer_addrs_num(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_GET_LOCAL_ADDRS_NUM: case SCTP_GET_LOCAL_ADDRS_NUM:
retval = sctp_getsockopt_get_local_addrs_num(sk, len, optval, retval = sctp_getsockopt_local_addrs_num(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_GET_PEER_ADDRS: case SCTP_GET_PEER_ADDRS:
retval = sctp_getsockopt_get_peer_addrs(sk, len, optval, retval = sctp_getsockopt_peer_addrs(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_GET_LOCAL_ADDRS: case SCTP_GET_LOCAL_ADDRS:
retval = sctp_getsockopt_get_local_addrs(sk, len, optval, retval = sctp_getsockopt_local_addrs(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_SET_DEFAULT_SEND_PARAM: case SCTP_SET_DEFAULT_SEND_PARAM:
retval = sctp_getsockopt_set_default_send_param(sk, len, retval = sctp_getsockopt_default_send_param(sk, len,
optval, optlen); optval, optlen);
break; break;
case SCTP_SET_PEER_PRIMARY_ADDR:
retval = sctp_getsockopt_peer_prim(sk, len, optval, optlen);
break;
case SCTP_NODELAY:
retval = sctp_getsockopt_nodelay(sk, len, optval, optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
...@@ -2331,7 +2469,7 @@ static void sctp_unhash(struct sock *sk) ...@@ -2331,7 +2469,7 @@ static void sctp_unhash(struct sock *sk)
/* Check if port is acceptable. Possibly find first available port. /* Check if port is acceptable. Possibly find first available port.
* *
* The port hash table (contained in the 'global' SCTP protocol storage * The port hash table (contained in the 'global' SCTP protocol storage
* returned by sctp_protocol_t * sctp_get_protocol()). The hash * returned by struct sctp_protocol *sctp_get_protocol()). The hash
* table is an array of 4096 lists (sctp_bind_hashbucket_t). Each * table is an array of 4096 lists (sctp_bind_hashbucket_t). Each
* list (the list number is the port number hashed out, so as you * list (the list number is the port number hashed out, so as you
* would expect from a hash function, all the ports in a given list have * would expect from a hash function, all the ports in a given list have
...@@ -2346,7 +2484,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -2346,7 +2484,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
{ {
sctp_bind_hashbucket_t *head; /* hash list */ sctp_bind_hashbucket_t *head; /* hash list */
sctp_bind_bucket_t *pp; /* hash list port iterator */ sctp_bind_bucket_t *pp; /* hash list port iterator */
sctp_protocol_t *sctp = sctp_get_protocol(); struct sctp_protocol *sctp = sctp_get_protocol();
unsigned short snum; unsigned short snum;
int ret; int ret;
...@@ -2543,6 +2681,9 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) ...@@ -2543,6 +2681,9 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
if (SCTP_SOCKET_UDP != sp->type) if (SCTP_SOCKET_UDP != sp->type)
return -EINVAL; return -EINVAL;
if (sk->state == SCTP_SS_LISTENING)
return 0;
/* /*
* If a bind() or sctp_bindx() is not called prior to a listen() * If a bind() or sctp_bindx() is not called prior to a listen()
* call that allows new associations to be accepted, the system * call that allows new associations to be accepted, the system
...@@ -2562,6 +2703,40 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) ...@@ -2562,6 +2703,40 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
return 0; return 0;
} }
/*
* 4.1.3 listen() - TCP Style Syntax
*
* Applications uses listen() to ready the SCTP endpoint for accepting
* inbound associations.
*/
SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
{
struct sctp_opt *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep;
if (sk->state == SCTP_SS_LISTENING)
return 0;
/*
* If a bind() or sctp_bindx() is not called prior to a listen()
* call that allows new associations to be accepted, the system
* picks an ephemeral port and will choose an address set equivalent
* to binding with a wildcard address.
*
* This is not currently spelled out in the SCTP sockets
* extensions draft, but follows the practice as seen in TCP
* sockets.
*/
if (!ep->base.bind_addr.port) {
if (sctp_autobind(sk))
return -EAGAIN;
}
sk->state = SCTP_SS_LISTENING;
sk->max_ack_backlog = backlog;
sctp_hash_endpoint(ep);
return 0;
}
/* /*
* Move a socket to LISTENING state. * Move a socket to LISTENING state.
*/ */
...@@ -2579,10 +2754,9 @@ int sctp_inet_listen(struct socket *sock, int backlog) ...@@ -2579,10 +2754,9 @@ int sctp_inet_listen(struct socket *sock, int backlog)
case SOCK_SEQPACKET: case SOCK_SEQPACKET:
err = sctp_seqpacket_listen(sk, backlog); err = sctp_seqpacket_listen(sk, backlog);
break; break;
case SOCK_STREAM: case SOCK_STREAM:
/* FIXME for TCP-style sockets. */ err = sctp_stream_listen(sk, backlog);
err = -EOPNOTSUPP; break;
default: default:
goto out; goto out;
...@@ -2684,7 +2858,7 @@ static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsi ...@@ -2684,7 +2858,7 @@ static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsi
/* FIXME: Commments! */ /* FIXME: Commments! */
static __inline__ void __sctp_put_port(struct sock *sk) static __inline__ void __sctp_put_port(struct sock *sk)
{ {
sctp_protocol_t *sctp_proto = sctp_get_protocol(); struct sctp_protocol *sctp_proto = sctp_get_protocol();
sctp_bind_hashbucket_t *head = sctp_bind_hashbucket_t *head =
&sctp_proto->port_hashtable[sctp_phashfn(inet_sk(sk)->num)]; &sctp_proto->port_hashtable[sctp_phashfn(inet_sk(sk)->num)];
sctp_bind_bucket_t *pp; sctp_bind_bucket_t *pp;
...@@ -2967,7 +3141,8 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int no ...@@ -2967,7 +3141,8 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int no
} }
/* Verify that this is a valid address. */ /* Verify that this is a valid address. */
static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len) static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
int len)
{ {
struct sctp_af *af; struct sctp_af *af;
...@@ -3213,7 +3388,7 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p) ...@@ -3213,7 +3388,7 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
return err; return err;
do_error: do_error:
err = -ECONNABORTED; err = -ECONNREFUSED;
goto out; goto out;
do_interrupted: do_interrupted:
...@@ -3225,6 +3400,131 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p) ...@@ -3225,6 +3400,131 @@ static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
goto out; goto out;
} }
static int sctp_wait_for_accept(struct sock *sk, long timeo)
{
struct sctp_endpoint *ep;
int err = 0;
DECLARE_WAITQUEUE(wait, current);
ep = sctp_sk(sk)->ep;
add_wait_queue_exclusive(sk->sleep, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&ep->asocs)) {
sctp_release_sock(sk);
timeo = schedule_timeout(timeo);
sctp_lock_sock(sk);
}
err = -EINVAL;
if (sk->state != SCTP_SS_LISTENING)
break;
err = 0;
if (!list_empty(&ep->asocs))
break;
err = sock_intr_errno(timeo);
if (signal_pending(current))
break;
err = -EAGAIN;
if (!timeo)
break;
}
remove_wait_queue(sk->sleep, &wait);
__set_current_state(TASK_RUNNING);
return err;
}
/* Populate the fields of the newsk from the oldsk and migrate the assoc
* and its messages to the newsk.
*/
void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
struct sctp_association *assoc, sctp_socket_type_t type)
{
struct sctp_opt *oldsp = sctp_sk(oldsk);
struct sctp_opt *newsp = sctp_sk(newsk);
sctp_endpoint_t *newep = newsp->ep;
struct sk_buff *skb, *tmp;
struct sctp_ulpevent *event;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
*/
newsk->sndbuf = oldsk->sndbuf;
newsk->rcvbuf = oldsk->rcvbuf;
*newsp = *oldsp;
/* Restore the ep value that was overwritten with the above structure
* copy.
*/
newsp->ep = newep;
/* Move any messages in the old socket's receive queue that are for the
* peeled off association to the new socket's receive queue.
*/
sctp_skb_for_each(skb, &oldsk->receive_queue, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(&newsk->receive_queue, skb);
}
}
/* Clean up any messages pending delivery due to partial
* delivery. Three cases:
* 1) No partial deliver; no work.
* 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
* 3) Peeling off non-partial delivery; move pd_lobby to recieve_queue.
*/
skb_queue_head_init(&newsp->pd_lobby);
sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;;
if (sctp_sk(oldsk)->pd_mode) {
struct sk_buff_head *queue;
/* Decide which queue to move pd_lobby skbs to. */
if (assoc->ulpq.pd_mode) {
queue = &newsp->pd_lobby;
} else
queue = &newsk->receive_queue;
/* Walk through the pd_lobby, looking for skbs that
* need moved to the new socket.
*/
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb);
if (event->asoc == assoc) {
__skb_unlink(skb, skb->list);
__skb_queue_tail(queue, skb);
}
}
/* Clear up any skbs waiting for the partial
* delivery to finish.
*/
if (assoc->ulpq.pd_mode)
sctp_clear_pd(oldsk);
}
/* Set the type of socket to indicate that it is peeled off from the
* original UDP-style socket or created with the accept() call on a
* TCP-style socket..
*/
newsp->type = type;
/* Migrate the association to the new socket. */
sctp_assoc_migrate(assoc, newsk);
newsk->state = SCTP_SS_ESTABLISHED;
}
/* This proto struct describes the ULP interface for SCTP. */ /* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = { struct proto sctp_prot = {
.name = "SCTP", .name = "SCTP",
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
#include <net/sctp/structs.h> #include <net/sctp/structs.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
extern sctp_protocol_t sctp_proto; extern struct sctp_protocol sctp_proto;
static ctl_table sctp_table[] = { static ctl_table sctp_table[] = {
{ {
......
...@@ -83,7 +83,7 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, ...@@ -83,7 +83,7 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
const union sctp_addr *addr, const union sctp_addr *addr,
int priority) int priority)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); struct sctp_protocol *proto = sctp_get_protocol();
/* Copy in the address. */ /* Copy in the address. */
peer->ipaddr = *addr; peer->ipaddr = *addr;
...@@ -262,7 +262,7 @@ void sctp_transport_put(struct sctp_transport *transport) ...@@ -262,7 +262,7 @@ void sctp_transport_put(struct sctp_transport *transport)
/* Update transport's RTO based on the newly calculated RTT. */ /* Update transport's RTO based on the newly calculated RTT. */
void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); struct sctp_protocol *proto = sctp_get_protocol();
/* Check for valid transport. */ /* Check for valid transport. */
SCTP_ASSERT(tp, "NULL transport", return); SCTP_ASSERT(tp, "NULL transport", return);
......
...@@ -250,7 +250,7 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map, ...@@ -250,7 +250,7 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
/* The Gap Ack Block happens to end at the end of the /* The Gap Ack Block happens to end at the end of the
* overflow map. * overflow map.
*/ */
if (started & !ended) { if (started && !ended) {
ended++; ended++;
_end = map->len + map->len - 1; _end = map->len + map->len - 1;
} }
......
...@@ -220,7 +220,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) ...@@ -220,7 +220,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
if (sctp_event2skb(event)->list) if (sctp_event2skb(event)->list)
sctp_skb_list_tail(sctp_event2skb(event)->list, queue); sctp_skb_list_tail(sctp_event2skb(event)->list, queue);
else else
skb_queue_tail(queue, sctp_event2skb(event)); __skb_queue_tail(queue, sctp_event2skb(event));
/* Did we just complete partial delivery and need to get /* Did we just complete partial delivery and need to get
* rolling again? Move pending data to the receive * rolling again? Move pending data to the receive
...@@ -230,7 +230,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) ...@@ -230,7 +230,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
sctp_ulpq_clear_pd(ulpq); sctp_ulpq_clear_pd(ulpq);
if (queue == &sk->receive_queue) if (queue == &sk->receive_queue)
wake_up_interruptible(sk->sleep); sk->data_ready(sk, 0);
return 1; return 1;
out_free: out_free:
...@@ -247,14 +247,14 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) ...@@ -247,14 +247,14 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq, static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
struct sctp_ulpevent *event) struct sctp_ulpevent *event)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos;
struct sctp_ulpevent *cevent; struct sctp_ulpevent *cevent;
__u32 tsn, ctsn; __u32 tsn, ctsn;
tsn = event->sndrcvinfo.sinfo_tsn; tsn = event->sndrcvinfo.sinfo_tsn;
/* Find the right place in this list. We store them by TSN. */ /* Find the right place in this list. We store them by TSN. */
sctp_skb_for_each(pos, &ulpq->reasm, tmp) { skb_queue_walk(&ulpq->reasm, pos) {
cevent = sctp_skb2event(pos); cevent = sctp_skb2event(pos);
ctsn = cevent->sndrcvinfo.sinfo_tsn; ctsn = cevent->sndrcvinfo.sinfo_tsn;
...@@ -334,7 +334,7 @@ static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff * ...@@ -334,7 +334,7 @@ static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *
*/ */
static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq) static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos;
struct sctp_ulpevent *cevent; struct sctp_ulpevent *cevent;
struct sk_buff *first_frag = NULL; struct sk_buff *first_frag = NULL;
__u32 ctsn, next_tsn; __u32 ctsn, next_tsn;
...@@ -355,7 +355,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u ...@@ -355,7 +355,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u
* fragment in order. If not, first_frag is reset to NULL and we * fragment in order. If not, first_frag is reset to NULL and we
* start the next pass when we find another first fragment. * start the next pass when we find another first fragment.
*/ */
sctp_skb_for_each(pos, &ulpq->reasm, tmp) { skb_queue_walk(&ulpq->reasm, pos) {
cevent = sctp_skb2event(pos); cevent = sctp_skb2event(pos);
ctsn = cevent->sndrcvinfo.sinfo_tsn; ctsn = cevent->sndrcvinfo.sinfo_tsn;
...@@ -374,29 +374,26 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u ...@@ -374,29 +374,26 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u
case SCTP_DATA_LAST_FRAG: case SCTP_DATA_LAST_FRAG:
if (first_frag && (ctsn == next_tsn)) if (first_frag && (ctsn == next_tsn))
retval = sctp_make_reassembled_event( goto found;
first_frag, pos);
else else
first_frag = NULL; first_frag = NULL;
break; break;
}; };
/* We have the reassembled event. There is no need to look
* further.
*/
if (retval) {
retval->msg_flags |= MSG_EOR;
break;
} }
} done:
return retval; return retval;
found:
retval = sctp_make_reassembled_event(first_frag, pos);
if (retval)
retval->msg_flags |= MSG_EOR;
goto done;
} }
/* Retrieve the next set of fragments of a partial message. */ /* Retrieve the next set of fragments of a partial message. */
static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq) static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
{ {
struct sk_buff *pos, *tmp, *last_frag, *first_frag; struct sk_buff *pos, *last_frag, *first_frag;
struct sctp_ulpevent *cevent; struct sctp_ulpevent *cevent;
__u32 ctsn, next_tsn; __u32 ctsn, next_tsn;
int is_last; int is_last;
...@@ -415,7 +412,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq ...@@ -415,7 +412,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq
next_tsn = 0; next_tsn = 0;
is_last = 0; is_last = 0;
sctp_skb_for_each(pos, &ulpq->reasm, tmp) { skb_queue_walk(&ulpq->reasm, pos) {
cevent = sctp_skb2event(pos); cevent = sctp_skb2event(pos);
ctsn = cevent->sndrcvinfo.sinfo_tsn; ctsn = cevent->sndrcvinfo.sinfo_tsn;
...@@ -448,7 +445,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq ...@@ -448,7 +445,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq
*/ */
done: done:
retval = sctp_make_reassembled_event(first_frag, last_frag); retval = sctp_make_reassembled_event(first_frag, last_frag);
if (is_last) if (retval && is_last)
retval->msg_flags |= MSG_EOR; retval->msg_flags |= MSG_EOR;
return retval; return retval;
...@@ -490,7 +487,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq, ...@@ -490,7 +487,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
/* Retrieve the first part (sequential fragments) for partial delivery. */ /* Retrieve the first part (sequential fragments) for partial delivery. */
static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq) static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
{ {
struct sk_buff *pos, *tmp, *last_frag, *first_frag; struct sk_buff *pos, *last_frag, *first_frag;
struct sctp_ulpevent *cevent; struct sctp_ulpevent *cevent;
__u32 ctsn, next_tsn; __u32 ctsn, next_tsn;
struct sctp_ulpevent *retval; struct sctp_ulpevent *retval;
...@@ -507,7 +504,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *u ...@@ -507,7 +504,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *u
retval = NULL; retval = NULL;
next_tsn = 0; next_tsn = 0;
sctp_skb_for_each(pos, &ulpq->reasm, tmp) { skb_queue_walk(&ulpq->reasm, pos) {
cevent = sctp_skb2event(pos); cevent = sctp_skb2event(pos);
ctsn = cevent->sndrcvinfo.sinfo_tsn; ctsn = cevent->sndrcvinfo.sinfo_tsn;
...@@ -590,7 +587,7 @@ static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq, ...@@ -590,7 +587,7 @@ static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq, static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
struct sctp_ulpevent *event) struct sctp_ulpevent *event)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos;
struct sctp_ulpevent *cevent; struct sctp_ulpevent *cevent;
__u16 sid, csid; __u16 sid, csid;
__u16 ssn, cssn; __u16 ssn, cssn;
...@@ -601,7 +598,7 @@ static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq, ...@@ -601,7 +598,7 @@ static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
/* Find the right place in this list. We store them by /* Find the right place in this list. We store them by
* stream ID and then by SSN. * stream ID and then by SSN.
*/ */
sctp_skb_for_each(pos, &ulpq->lobby, tmp) { skb_queue_walk(&ulpq->lobby, pos) {
cevent = (struct sctp_ulpevent *) pos->cb; cevent = (struct sctp_ulpevent *) pos->cb;
csid = cevent->sndrcvinfo.sinfo_stream; csid = cevent->sndrcvinfo.sinfo_stream;
cssn = cevent->sndrcvinfo.sinfo_ssn; cssn = cevent->sndrcvinfo.sinfo_ssn;
...@@ -786,9 +783,9 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority) ...@@ -786,9 +783,9 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority)
SCTP_PARTIAL_DELIVERY_ABORTED, SCTP_PARTIAL_DELIVERY_ABORTED,
priority); priority);
if (ev) if (ev)
skb_queue_tail(&sk->receive_queue, sctp_event2skb(ev)); __skb_queue_tail(&sk->receive_queue, sctp_event2skb(ev));
/* If there is data waiting, send it up the socket now. */ /* If there is data waiting, send it up the socket now. */
if (sctp_ulpq_clear_pd(ulpq) || ev) if (sctp_ulpq_clear_pd(ulpq) || ev)
wake_up_interruptible(sk->sleep); sk->data_ready(sk, 0);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment