diff options
| author | Jon Grimm <jgrimm@touki.austin.ibm.com> | 2003-03-17 19:59:29 -0600 |
|---|---|---|
| committer | Jon Grimm <jgrimm@touki.austin.ibm.com> | 2003-03-17 19:59:29 -0600 |
| commit | c219cdceabe23ad0168bb00a61c98f3835c1298a (patch) | |
| tree | d6c017659aa46bab7c36eea985cf6f21a387f8d4 | |
| parent | b486a58170b495d67f3c5a57e3cdd0c89e089b54 (diff) | |
| parent | 74f53f9a1fa2832e89e28d77fa3d4b2217d0949b (diff) | |
Merge touki.austin.ibm.com:/home/jgrimm/bk/linux-2.5.65
into touki.austin.ibm.com:/home/jgrimm/bk/lksctp-2.5.work
| -rw-r--r-- | include/net/sctp/command.h | 16 | ||||
| -rw-r--r-- | include/net/sctp/constants.h | 22 | ||||
| -rw-r--r-- | include/net/sctp/sctp.h | 49 | ||||
| -rw-r--r-- | include/net/sctp/sm.h | 46 | ||||
| -rw-r--r-- | include/net/sctp/structs.h | 56 | ||||
| -rw-r--r-- | include/net/sctp/user.h | 2 | ||||
| -rw-r--r-- | net/sctp/associola.c | 71 | ||||
| -rw-r--r-- | net/sctp/bind_addr.c | 2 | ||||
| -rw-r--r-- | net/sctp/endpointola.c | 7 | ||||
| -rw-r--r-- | net/sctp/ipv6.c | 91 | ||||
| -rw-r--r-- | net/sctp/output.c | 103 | ||||
| -rw-r--r-- | net/sctp/outqueue.c | 135 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 131 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 54 | ||||
| -rw-r--r-- | net/sctp/sm_sideeffect.c | 41 | ||||
| -rw-r--r-- | net/sctp/sm_statefuns.c | 34 | ||||
| -rw-r--r-- | net/sctp/socket.c | 670 | ||||
| -rw-r--r-- | net/sctp/sysctl.c | 2 | ||||
| -rw-r--r-- | net/sctp/transport.c | 4 | ||||
| -rw-r--r-- | net/sctp/tsnmap.c | 4 | ||||
| -rw-r--r-- | net/sctp/ulpqueue.c | 47 |
21 files changed, 1058 insertions, 529 deletions
diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 6603202d91f7..f730dd55f5ad 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -110,13 +110,13 @@ typedef union { sctp_event_timeout_t to; sctp_counter_t counter; void *ptr; - sctp_chunk_t *chunk; - sctp_association_t *asoc; + struct sctp_chunk *chunk; + struct sctp_association *asoc; struct sctp_transport *transport; - sctp_bind_addr_t *bp; + struct sctp_bind_addr *bp; sctp_init_chunk_t *init; struct sctp_ulpevent *ulpevent; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_sackhdr_t *sackh; } sctp_arg_t; @@ -158,13 +158,13 @@ SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state) SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter) SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr) -SCTP_ARG_CONSTRUCTOR(CHUNK, sctp_chunk_t *, chunk) -SCTP_ARG_CONSTRUCTOR(ASOC, sctp_association_t *, asoc) +SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk) +SCTP_ARG_CONSTRUCTOR(ASOC, struct sctp_association *, asoc) 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(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) typedef struct { diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 3cc94d900f95..8ddc88ed69fd 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -210,14 +210,19 @@ typedef enum { /* These are values for sk->state. * 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_UNCONNECTED 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 and can initiate the creation of new ones. - * A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off - * socket with one association. + * - 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 and can initiate the creation of new ones. + * - A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off + * 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 { SCTP_SS_CLOSED = TCP_CLOSE, @@ -345,6 +350,7 @@ typedef enum { SCTP_XMIT_PMTU_FULL, SCTP_XMIT_RWND_FULL, SCTP_XMIT_MUST_FRAG, + SCTP_XMIT_NAGLE_DELAY, } sctp_xmit_t; /* These are the commands for manipulating transports. */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index b2e19ebde563..27a69518c2f9 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -121,9 +121,10 @@ /* * sctp_protocol.c */ -extern sctp_protocol_t sctp_proto; +extern struct sctp_protocol sctp_proto; 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); extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); extern int sctp_register_pf(struct sctp_pf *, sa_family_t); @@ -312,30 +313,21 @@ static inline void sctp_sysctl_unregister(void) { return; } #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) extern int sctp_v6_init(void); extern void sctp_v6_exit(void); - static inline int sctp_ipv6_addr_type(const 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) */ #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 void sctp_v6_exit(void) { return; } @@ -348,25 +340,10 @@ static inline sctp_assoc_t sctp_assoc2id(const sctp_association_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 - * data structure and if so, that the socket matches. - */ - if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) && - (temp->base.sk == sk)) - asoc = temp; - } +sctp_association_t *sctp_id2assoc(struct sock *sk, sctp_assoc_t id); - return asoc; -} /* A macro to walk a list of skbs. */ #define sctp_skb_for_each(pos, head, tmp) \ @@ -494,7 +471,7 @@ extern void sctp_put_port(struct sock *sk); /* Static inline functions. */ /* 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; } @@ -523,21 +500,21 @@ static inline int ipver2af(__u8 ipver) /* This is the hash function for the SCTP port hash table. */ 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)); } /* This is the hash function for the endpoint hash table. */ 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)); } /* This is the hash function for the association hash table. */ 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; h ^= h>>8; return (h & (sctp_proto->assoc_hashsize - 1)); @@ -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) { - sctp_protocol_t *sctp_proto = sctp_get_protocol(); + struct sctp_protocol *sctp_proto = sctp_get_protocol(); int h = (lport << 16) + rport; h ^= vtag; return (h & (sctp_proto->assoc_hashsize-1)); diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 7b08e90a102a..16737eda8d8c 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -3,40 +3,40 @@ * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001-2002 International Business Machines Corp. - * + * * This file is part of the SCTP kernel reference Implementation - * + * * This file is part of the implementation of the add-IP extension, * based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001, * for the SCTP kernel reference Implementation. - * + * * These are definitions needed by the state machine. - * - * The SCTP reference implementation is free software; - * you can redistribute it and/or modify it under the terms of + * + * The SCTP reference implementation is free software; + * you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. - * - * The SCTP reference implementation is distributed in the hope that it + * + * The SCTP reference implementation is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * ************************ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with GNU CC; see the file COPYING. If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * + * Boston, MA 02111-1307, USA. + * * Please send any bug reports or fixes you make to the * email addresses: * lksctp developers <lksctp-developers@lists.sourceforge.net> - * + * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp * - * Written or modified by: + * Written or modified by: * La Monte H.P. Yarroll <piggy@acm.org> * Karl Knutson <karl@athena.chicago.il.us> * Xingang Guo <xingang.guo@intel.com> @@ -313,18 +313,18 @@ void sctp_generate_t3_rtx_event(unsigned long peer); void sctp_generate_heartbeat_event(unsigned long peer); sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *); -sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep, - const sctp_association_t *asoc, - sctp_chunk_t *chunk, - const void *payload, - size_t paylen); -sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, - const sctp_chunk_t *chunk); -void sctp_ootb_pkt_free(sctp_packet_t *packet); +struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *, + const struct sctp_association *, + struct sctp_chunk *chunk, + const void *payload, + size_t paylen); +struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *, + const struct sctp_chunk *); +void sctp_ootb_pkt_free(struct sctp_packet *); sctp_cookie_param_t * -sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *, - const sctp_chunk_t *, int *cookie_len, +sctp_pack_cookie(const struct sctp_endpoint *, const struct sctp_association *, + const struct sctp_chunk *, int *cookie_len, const __u8 *, int addrs_len); sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *, const sctp_association_t *, diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index cf6cec92f88f..913c4769b343 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -86,10 +86,8 @@ struct sctp_opt; struct sctp_endpoint_common; struct sctp_ssnmap; -typedef struct sctp_protocol sctp_protocol_t; typedef struct sctp_endpoint sctp_endpoint_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_bind_addr sctp_bind_addr_t; typedef struct sctp_endpoint_common sctp_endpoint_common_t; @@ -222,7 +220,7 @@ struct sctp_af { void (*get_saddr) (struct sctp_association *asoc, struct dst_entry *dst, union sctp_addr *daddr, - union sctp_addr *saddr); + union sctp_addr *saddr); void (*copy_addrlist) (struct list_head *, struct net_device *); void (*dst_saddr) (union sctp_addr *saddr, @@ -262,6 +260,9 @@ struct sctp_pf { const union sctp_addr *, struct sctp_opt *); 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; }; @@ -366,8 +367,6 @@ typedef struct sctp_signed_cookie { sctp_cookie_t c; } sctp_signed_cookie_t; - - /* This is another convenience type to allocate memory for address * params for the maximum size and pass such structures around * internally. @@ -604,26 +603,26 @@ struct sctp_packet { typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *); typedef int (sctp_outq_ehandler_t)(struct sctp_outq *); -typedef sctp_packet_t *(sctp_outq_ohandler_init_t) - (sctp_packet_t *, +typedef struct sctp_packet *(sctp_outq_ohandler_init_t) + (struct sctp_packet *, struct sctp_transport *, __u16 sport, __u16 dport); -typedef sctp_packet_t *(sctp_outq_ohandler_config_t) - (sctp_packet_t *, +typedef struct sctp_packet *(sctp_outq_ohandler_config_t) + (struct sctp_packet *, __u32 vtag, int ecn_capable, 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 *); -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_config_t sctp_packet_config; sctp_outq_ohandler_t sctp_packet_append_chunk; sctp_outq_ohandler_t sctp_packet_transmit_chunk; 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. @@ -789,7 +788,7 @@ struct sctp_transport { struct list_head transmitted; /* 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. */ struct list_head send_ready; @@ -865,12 +864,11 @@ void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *); struct sctp_outq { sctp_association_t *asoc; - /* BUG: This really should be an array of streams. - * This really holds a list of chunks (one stream). - * FIXME: If true, why so? - */ + /* Data pending that has never been transmitted. */ struct sk_buff_head out; + unsigned out_qlen; /* Total length of queued data chunks. */ + /* These are control chunks we want to send. */ struct sk_buff_head control; @@ -885,7 +883,7 @@ struct sctp_outq { struct list_head retransmit; /* 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. */ sctp_outq_ohandler_init_t *init_output; @@ -1098,8 +1096,9 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base) } /* These are function signatures for manipulating endpoints. */ -sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *, struct sock *, int); -sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *, sctp_protocol_t *, +sctp_endpoint_t *sctp_endpoint_new(struct sctp_protocol *, struct sock *, int); +sctp_endpoint_t *sctp_endpoint_init(struct sctp_endpoint *, + struct sctp_protocol *, struct sock *, int priority); void sctp_endpoint_free(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, int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *); sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, const union sctp_addr *); - int sctp_has_association(const union sctp_addr *laddr, const union sctp_addr *paddr); @@ -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 *, const union sctp_addr *address, const int priority); -void sctp_assoc_control_transport(sctp_association_t *, +void sctp_assoc_control_transport(struct sctp_association *, struct sctp_transport *, sctp_transport_cmd_t, sctp_sn_error_t); 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 *, void sctp_assoc_migrate(sctp_association_t *, struct sock *); 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_tsn_block(sctp_association_t *, 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); +__u32 sctp_association_get_next_tsn(struct sctp_association *); +__u32 sctp_association_get_tsn_block(struct sctp_association *, 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_cookie(sctp_association_t *, sctp_cookie_t *, int); diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 3ca725e132bd..e8cf2aedec87 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -108,6 +108,8 @@ enum sctp_optname { #define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */ #define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS + SCTP_NODELAY, /* Get/set nodelay option. */ +#define SCTP_NODELAY SCTP_NODELAY }; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 230b5602004d..ff98ab3f7fda 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -181,7 +181,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, else asoc->rwnd = sk->rcvbuf; - asoc->a_rwnd = 0; + asoc->a_rwnd = asoc->rwnd; asoc->rwnd_over = 0; @@ -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. */ -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, int priority) { @@ -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 * 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); - } else { + else asoc->pmtu = peer->pmtu; - } SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " "%d\n", asoc, asoc->pmtu); - asoc->frag_point = asoc->pmtu - - (SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t)); + asoc->frag_point = asoc->pmtu; + asoc->frag_point -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk); /* The asoc->peer.port might not be meaningful yet, but * initialize the packet structure anyway. @@ -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 (NULL == asoc->peer.primary_path) { - asoc->peer.primary_path = peer; - /* Set a default msg_name for events. */ - memcpy(&asoc->peer.primary_addr, &peer->ipaddr, - sizeof(union sctp_addr)); - asoc->peer.active_path = peer; + sctp_assoc_set_primary(asoc, peer); asoc->peer.retran_path = peer; } @@ -603,7 +614,7 @@ void sctp_association_put(sctp_association_t *asoc) /* Allocate the next TSN, Transmission Sequence Number, for the given * 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: * Transmission Sequence Numbers wrap around when they reach @@ -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. */ -__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; @@ -942,7 +953,7 @@ struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t * { /* If this is the first time SHUTDOWN is sent, use the active path, * else use the retran path. If the last SHUTDOWN was sent over the - * retran path, update the retran path and use it. + * retran path, update the retran path and use it. */ if (!asoc->shutdown_last_sent_to) return asoc->peer.active_path; @@ -983,6 +994,24 @@ void sctp_assoc_sync_pmtu(sctp_association_t *asoc) __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. */ 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 * Section 4.2.3.3 of RFC 1122. */ - if ((asoc->state == SCTP_STATE_ESTABLISHED) && - (asoc->rwnd > asoc->a_rwnd) && - ((asoc->rwnd - asoc->a_rwnd) >= - min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) { + if (sctp_peer_needs_update(asoc)) { + asoc->a_rwnd = asoc->rwnd; SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p " "rwnd: %u a_rwnd: %u\n", __FUNCTION__, asoc, asoc->rwnd, asoc->a_rwnd); @@ -1020,9 +1047,6 @@ void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len) if (!sack) return; - /* Update the last advertised rwnd value. */ - asoc->a_rwnd = asoc->rwnd; - asoc->peer.sack_needed = 0; sctp_outq_tail(&asoc->outqueue, sack); @@ -1046,7 +1070,8 @@ void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len) asoc->rwnd = 0; } 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 diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 2ae655f2c775..26c62125226b 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -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, 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; if (sctp_is_any(addr)) { diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 8efbd4af013e..1f4cdc25d81c 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -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. * 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) { sctp_endpoint_t *ep; @@ -89,7 +89,8 @@ fail: /* * 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 sctp_opt *sp = sctp_sk(sk); @@ -194,6 +195,8 @@ void sctp_endpoint_destroy(sctp_endpoint_t *ep) { 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! */ sctp_unhash_endpoint(ep); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index e0703d0727fa..1133d3fd93bb 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -432,6 +432,62 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) 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. */ 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) 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 = { .family = PF_INET6, .release = inet6_release, @@ -583,7 +653,7 @@ static struct proto_ops inet6_seqpacket_ops = { .mmap = sock_no_mmap, }; -static struct inet_protosw sctpv6_protosw = { +static struct inet_protosw sctpv6_seqpacket_protosw = { .type = SOCK_SEQPACKET, .protocol = IPPROTO_SCTP, .prot = &sctp_prot, @@ -592,6 +662,15 @@ static struct inet_protosw sctpv6_protosw = { .no_check = 0, .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 = { .handler = sctp_rcv, @@ -626,6 +705,8 @@ static struct sctp_pf sctp_pf_inet6_specific = { .af_supported = sctp_inet6_af_supported, .cmp_addr = sctp_inet6_cmp_addr, .bind_verify = sctp_inet6_bind_verify, + .supported_addrs = sctp_inet6_supported_addrs, + .create_accept_sk = sctp_v6_create_accept_sk, .af = &sctp_ipv6_specific, }; @@ -636,8 +717,9 @@ int sctp_v6_init(void) if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) return -EAGAIN; - /* Add SCTPv6 to inetsw6 linked list. */ - inet6_register_protosw(&sctpv6_protosw); + /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */ + inet6_register_protosw(&sctpv6_seqpacket_protosw); + inet6_register_protosw(&sctpv6_stream_protosw); /* Register the SCTP specfic PF_INET6 functions. */ sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); @@ -656,6 +738,7 @@ void sctp_v6_exit(void) { list_del(&sctp_ipv6_specific.list); 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); } diff --git a/net/sctp/output.c b/net/sctp/output.c index d7826c2216e6..c02f99d602f0 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -62,17 +62,16 @@ #include <net/sctp/sm.h> /* Forward declarations for private helpers. */ -static void sctp_packet_reset(sctp_packet_t *packet); -static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, - sctp_chunk_t *chunk); +static void sctp_packet_reset(struct sctp_packet *packet); +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk); /* Config a packet. * This appears to be a followup set of initializations.) */ -sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, - __u32 vtag, - int ecn_capable, - sctp_packet_phandler_t *prepend_handler) +struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, + __u32 vtag, int ecn_capable, + sctp_packet_phandler_t *prepend_handler) { int packet_empty = (packet->size == SCTP_IP_OVERHEAD); @@ -89,10 +88,9 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, } /* Initialize the packet structure. */ -sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, - struct sctp_transport *transport, - __u16 sport, - __u16 dport) +struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, + struct sctp_transport *transport, + __u16 sport, __u16 dport) { packet->transport = transport; packet->source_port = sport; @@ -109,14 +107,12 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *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 != - (chunk = (sctp_chunk_t *)skb_dequeue(&packet->chunks))) { + while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) sctp_free_chunk(chunk); - } if (packet->malloced) kfree(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 * packet can be sent only after receiving the COOKIE_ACK. */ -sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, - sctp_chunk_t *chunk) +sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) { sctp_xmit_t retval; int error = 0; @@ -152,6 +148,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(sctp_packet_t *packet, case SCTP_XMIT_MUST_FRAG: case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_OK: + case SCTP_XMIT_NAGLE_DELAY: break; }; @@ -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 * 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; __u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length)); @@ -182,7 +180,7 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) /* Both control chunks and data chunks with TSNs are * non-fragmentable. */ - int fragmentable = sctp_chunk_is_data(chunk) && + int fragmentable = sctp_chunk_is_data(chunk) && (!chunk->has_tsn); if (packet_empty) { if (fragmentable) { @@ -223,7 +221,7 @@ append: } /* 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; finish: return retval; @@ -234,18 +232,18 @@ finish: * * 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; - sctp_association_t *asoc = transport->asoc; + struct sctp_association *asoc = transport->asoc; struct sctphdr *sh; __u32 crc32; struct sk_buff *nskb; - sctp_chunk_t *chunk; + struct sctp_chunk *chunk; struct sock *sk; int err = 0; int padding; /* How much padding do we need? */ - __u8 packet_has_data = 0; + __u8 has_data = 0; struct dst_entry *dst; /* Do NOT generate a chunkless packet... */ @@ -253,7 +251,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) return err; /* Set up convenience variables... */ - chunk = (sctp_chunk_t *) (packet->chunks.next); + chunk = (struct sctp_chunk *) (packet->chunks.next); sk = chunk->skb->sk; /* Allocate the new skb. */ @@ -291,8 +289,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) * [This whole comment explains WORD_ROUND() below.] */ SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); - while (NULL != (chunk = (sctp_chunk_t *) - skb_dequeue(&packet->chunks))) { + while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) { chunk->num_times_sent++; chunk->sent_at = jiffies; if (sctp_chunk_is_data(chunk)) { @@ -309,7 +306,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) chunk->rtt_in_progress = 1; transport->rto_pending = 1; } - packet_has_data = 1; + has_data = 1; } memcpy(skb_put(nskb, chunk->skb->len), chunk->skb->data, chunk->skb->len); @@ -399,7 +396,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) asoc->peer.last_sent_to = transport; } - if (packet_has_data) { + if (has_data) { struct timer_list *timer; unsigned long timeout; @@ -456,9 +453,9 @@ no_route: /* * 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; @@ -473,13 +470,16 @@ static void sctp_packet_reset(sctp_packet_t *packet) } /* This private function handles the specifics of appending DATA chunks. */ -static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, - sctp_chunk_t *chunk) +static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk) { sctp_xmit_t retval = SCTP_XMIT_OK; size_t datasize, rwnd, inflight; struct sctp_transport *transport = packet->transport; __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 * @@ -494,8 +494,8 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, * receiver to the data sender. */ - rwnd = transport->asoc->peer.rwnd; - inflight = transport->asoc->outqueue.outstanding_bytes; + rwnd = asoc->peer.rwnd; + inflight = asoc->outqueue.outstanding_bytes; datasize = sctp_data_size(chunk); @@ -517,7 +517,7 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, * if ((flightsize + Max.Burst * MTU) < cwnd) * 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) { transport->cwnd = transport->flight_size + max_burst_bytes; 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, * When a Fast Retransmit is being performed the sender SHOULD * ignore the value of cwnd and SHOULD NOT delay retransmission. */ - if (!chunk->fast_retransmit) { + if (!chunk->fast_retransmit) if (transport->flight_size >= transport->cwnd) { retval = SCTP_XMIT_RWND_FULL; 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. */ transport->flight_size += datasize; /* 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. */ - if (datasize < rwnd) { + if (datasize < rwnd) rwnd -= datasize; - } else { + else rwnd = 0; - } - transport->asoc->peer.rwnd = rwnd; + asoc->peer.rwnd = rwnd; finish: return retval; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index b5697e4cc2a3..d2fb050019e5 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1,7 +1,7 @@ /* SCTP kernel reference Implementation * Copyright (c) 1999-2000 Cisco, 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. * * This file is part of the SCTP kernel reference Implementation @@ -62,6 +62,43 @@ static void sctp_check_transmitted(struct sctp_outq *q, sctp_sackhdr_t *sack, __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. */ 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) q->empty = 1; q->malloced = 0; + q->out_qlen = 0; } /* Free the outqueue structure and any related pending chunks. @@ -125,7 +163,7 @@ void sctp_outq_teardown(struct sctp_outq *q) sctp_free_chunk(chunk); } - /* Throw away any chunks in the retransmit queue. */ + /* Throw away any chunks in the retransmit queue. */ list_for_each_safe(lchunk, temp, &q->retransmit) { list_del(lchunk); chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); @@ -133,7 +171,7 @@ void sctp_outq_teardown(struct sctp_outq *q) } /* 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); /* Throw away any leftover control chunks. */ @@ -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)) : "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) SCTP_INC_STATS(SctpOutUnorderChunks); else @@ -201,7 +239,7 @@ int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk) break; }; } else { - skb_queue_tail(&q->control, (struct sk_buff *) chunk); + __skb_queue_tail(&q->control, (struct sk_buff *) chunk); SCTP_INC_STATS(SctpOutCtrlChunks); } @@ -241,7 +279,7 @@ void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q) } /* Mark all the eligible packets on a transport for retransmission. */ -void sctp_retransmit_mark(struct sctp_outq *q, +void sctp_retransmit_mark(struct sctp_outq *q, struct sctp_transport *transport, __u8 fast_retransmit) { @@ -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. */ -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) { struct list_head *lqueue; @@ -385,17 +423,6 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt, while (lchunk) { 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 * 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, * queue. 'pos' points to the next chunk in the output queue after the * chunk that is currently in the process of fragmentation. */ -void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, - sctp_packet_t *packet, sctp_chunk_t *frag, __u32 tsn) +void sctp_xmit_frag(struct sctp_outq *q, struct sctp_chunk *pos, + struct sctp_packet *packet, struct sctp_chunk *frag, __u32 tsn) { struct sctp_transport *transport = packet->transport; struct sk_buff_head *queue = &q->out; @@ -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. " "adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) { - skb_insert(pos, (struct sk_buff *) frag); - } else { - skb_queue_tail(queue, (struct sk_buff *) frag); - } + if (pos) + sctp_outq_insert_data(q, frag, pos); + else + sctp_outq_tail_data(q, frag); return; } @@ -496,11 +522,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. " "adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) { - skb_insert(pos, (struct sk_buff *) frag); - } else { - skb_queue_tail(queue, (struct sk_buff *) frag); - } + if (pos) + sctp_outq_insert_data(q, frag, pos); + else + sctp_outq_tail_data(q, frag); break; case SCTP_XMIT_OK: @@ -512,11 +537,10 @@ void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos, SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " "failed. adding 0x%x to outqueue\n", ntohl(frag->subh.data_hdr->tsn)); - if (pos) { - skb_insert(pos, (struct sk_buff *) frag); - } else { - skb_queue_tail(queue, (struct sk_buff *) frag); - } + if (pos) + sctp_outq_insert_data(q, frag, pos); + else + sctp_outq_tail_data(q, frag); } else { SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output " "success. 0x%x sent\n", @@ -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 * 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_association_t *asoc = frag->asoc; struct list_head *lfrag, *frag_list; __u32 tsn; int nfrags = 1; - struct sk_buff *pos; + struct sctp_chunk *pos; /* Count the number of fragments. */ frag_list = &frag->frag_list; @@ -553,17 +577,17 @@ void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet, } /* 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. */ - sctp_xmit_frag(q, pos, packet, frag, tsn++); + sctp_xmit_frag(q, pos, pkt, frag, tsn++); /* Transmit the rest of fragments. */ frag_list = &frag->frag_list; list_for_each(lfrag, 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++); } } @@ -595,7 +619,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, old_flags = chunk->chunk_hdr->flags; if (old_flags & SCTP_DATA_FIRST_FRAG) flags = SCTP_DATA_FIRST_FRAG; - else + else flags = SCTP_DATA_MIDDLE_FRAG; /* Make the first fragment. */ @@ -672,15 +696,14 @@ err: * * Description: Send everything in q which we legally can, subject to * 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 * this function. */ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) { - sctp_packet_t *packet; - sctp_packet_t singleton; + struct sctp_packet *packet; + struct sctp_packet singleton; sctp_association_t *asoc = q->asoc; int ecn_capable = asoc->peer.ecn_capable; __u16 sport = asoc->base.bind_addr.port; @@ -719,7 +742,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) } 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. */ new_transport = chunk->transport; @@ -852,7 +875,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) /* Finally, transmit new packets. */ start_timer = 0; 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 * stream identifier. */ @@ -925,6 +949,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) switch (status) { case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_RWND_FULL: + case SCTP_XMIT_NAGLE_DELAY: /* We could not append this chunk, so put * the chunk back on the output queue. */ @@ -932,7 +957,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) "not transmit TSN: 0x%x, status: %d\n", ntohl(chunk->subh.data_hdr->tsn), status); - skb_queue_head(queue, (struct sk_buff *)chunk); + sctp_outq_head_data(q, chunk); goto sctp_flush_out; break; @@ -994,6 +1019,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) } sctp_flush_out: + /* Before returning, examine all the transports touched in * this call. Right now, we bluntly force clear all the * transports. Things might change after we implement Nagle. @@ -1003,7 +1029,7 @@ sctp_flush_out: */ while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) { struct sctp_transport *t = list_entry(ltransport, - struct sctp_transport, + struct sctp_transport, send_ready); if (t != transport) transport = t; @@ -1125,7 +1151,7 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) * This is a MASSIVE candidate for optimization. */ list_for_each(pos, transport_list) { - transport = list_entry(pos, struct sctp_transport, + transport = list_entry(pos, struct sctp_transport, transports); sctp_check_transmitted(q, &transport->transmitted, transport, sack, highest_new_tsn); @@ -1163,11 +1189,10 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) sack_a_rwnd = ntohl(sack->a_rwnd); outstanding = q->outstanding_bytes; - if (outstanding < sack_a_rwnd) { + if (outstanding < sack_a_rwnd) sack_a_rwnd -= outstanding; - } else { + else sack_a_rwnd = 0; - } asoc->peer.rwnd = sack_a_rwnd; @@ -1179,7 +1204,7 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack) goto finish; list_for_each(pos, transport_list) { - transport = list_entry(pos, struct sctp_transport, + transport = list_entry(pos, struct sctp_transport, transports); q->empty = q->empty && list_empty(&transport->transmitted); if (!q->empty) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 85a5a2941af5..a1c98e3618cc 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -58,7 +58,7 @@ #include <net/inet_common.h> /* Global data structures. */ -sctp_protocol_t sctp_proto; +struct sctp_protocol sctp_proto; struct proc_dir_entry *proc_net_sctp; DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics); @@ -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 * 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 list_head *pos; @@ -168,7 +168,7 @@ static void __sctp_get_local_addr_list(sctp_protocol_t *proto) 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)); @@ -178,7 +178,7 @@ static void sctp_get_local_addr_list(sctp_protocol_t *proto) } /* 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 list_head *pos, *temp; @@ -191,7 +191,7 @@ static void __sctp_free_local_addr_list(sctp_protocol_t *proto) } /* 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)); @@ -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'. */ -int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp, - sctp_scope_t scope, int priority, int copy_flags) +int sctp_copy_local_addr_list(struct sctp_protocol *proto, + struct sctp_bind_addr *bp, sctp_scope_t scope, + int priority, int copy_flags) { struct sockaddr_storage_list *addr; int error = 0; @@ -331,7 +332,7 @@ static int sctp_v4_addr_valid(union sctp_addr *addr) static int sctp_v4_available(const union sctp_addr *addr) { int ret = inet_addr_type(addr->v4.sin_addr.s_addr); - + /* FIXME: ip_nonlocal_bind sysctl support. */ if (addr->v4.sin_addr.s_addr != INADDR_ANY && ret != RTN_LOCAL) @@ -380,7 +381,7 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) /* Returns a valid dst cache entry for the given source and destination ip * addresses. If an association is passed, trys to get a dst entry with a - * source adddress that matches an address in the bind address list. + * source adddress that matches an address in the bind address list. */ struct dst_entry *sctp_v4_get_dst(sctp_association_t *asoc, union sctp_addr *daddr, @@ -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. * 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, */ int sctp_ctl_sock_init(void) { - int err = 0; - int family = PF_INET; + int err; + 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, &sctp_ctl_socket); @@ -630,6 +689,16 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *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. */ static inline int sctp_v4_xmit(struct sk_buff *skb, struct sctp_transport *transport, int ipfragok) @@ -652,6 +721,8 @@ static struct sctp_pf sctp_pf_inet = { .af_supported = sctp_inet_af_supported, .cmp_addr = sctp_inet_cmp_addr, .bind_verify = sctp_inet_bind_verify, + .supported_addrs = sctp_inet_supported_addrs, + .create_accept_sk = sctp_v4_create_accept_sk, .af = &sctp_ipv4_specific, }; @@ -682,7 +753,7 @@ struct proto_ops inet_seqpacket_ops = { }; /* Registration with AF_INET family. */ -struct inet_protosw sctp_protosw = { +static struct inet_protosw sctp_seqpacket_protosw = { .type = SOCK_SEQPACKET, .protocol = IPPROTO_SCTP, .prot = &sctp_prot, @@ -691,6 +762,15 @@ struct inet_protosw sctp_protosw = { .no_check = 0, .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. */ static struct inet_protocol sctp_protocol = { @@ -756,7 +836,7 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family) static int __init init_sctp_mibs(void) { int i; - + sctp_statistics[0] = kmalloc_percpu(sizeof (struct sctp_mib), GFP_KERNEL); if (!sctp_statistics[0]) @@ -778,7 +858,7 @@ static int __init init_sctp_mibs(void) } } return 0; - + } static void cleanup_sctp_mibs(void) @@ -797,14 +877,15 @@ __init int sctp_init(void) if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) return -EAGAIN; - /* Add SCTP to inetsw linked list. */ - inet_register_protosw(&sctp_protosw); + /* Add SCTP(TCP and UDP style) to inetsw linked list. */ + inet_register_protosw(&sctp_seqpacket_protosw); + inet_register_protosw(&sctp_stream_protosw); /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(); - if (status) + if (status) goto err_init_mibs; - + /* Initialize proc fs directory. */ sctp_proc_init(); @@ -831,7 +912,7 @@ __init int sctp_init(void) /* Valid.Cookie.Life - 60 seconds */ sctp_proto.valid_cookie_life = 60 * HZ; - /* Whether Cookie Preservative is enabled(1) or not(0) */ + /* Whether Cookie Preservative is enabled(1) or not(0) */ sctp_proto.cookie_preserve_enable = 1; /* Max.Burst - 4 */ @@ -920,7 +1001,7 @@ __init int sctp_init(void) INIT_LIST_HEAD(&sctp_proto.local_addr_list); sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED; - /* Register notifier for inet address additions/deletions. */ + /* Register notifier for inet address additions/deletions. */ register_inetaddr_notifier(&sctp_inetaddr_notifier); sctp_get_local_addr_list(&sctp_proto); @@ -942,9 +1023,10 @@ err_ahash_alloc: sctp_dbg_objcnt_exit(); sctp_proc_exit(); cleanup_sctp_mibs(); -err_init_mibs: +err_init_mibs: 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; } @@ -977,7 +1059,8 @@ __exit void sctp_exit(void) cleanup_sctp_mibs(); 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); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0677dbbbd802..0f3f35affe8e 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -68,29 +68,6 @@ /* 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) - * * Note 2: The ECN capable field is reserved for future use of * Explicit Congestion Notification. */ @@ -174,7 +151,10 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, union sctp_params addrs; size_t chunksize; 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) * @@ -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.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 += vparam_len; @@ -220,8 +204,18 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, retval->param_hdr.v = sctp_addto_chunk(retval, addrs_len, addrs.v); - sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &sat_param); - sctp_addto_chunk(retval, sizeof(sat_addr_types), sat_addr_types); + /* 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. + */ + 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); nodata: if (addrs.v) @@ -604,7 +598,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc) /* Initialize the SACK header. */ 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_dup_tsns = htons(num_dup_tsns); @@ -1159,7 +1153,7 @@ int sctp_datachunks_from_user(sctp_association_t *asoc, first_len = max; /* 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); /* Account for the DATA to be bundled with the COOKIE-ECHO. */ @@ -1282,7 +1276,7 @@ void sctp_chunk_assign_tsn(sctp_chunk_t *chunk) * assign a 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; } } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 1228f55dfdfb..004b6d2f0b03 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -105,8 +105,8 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *, #define DEBUG_POST_SFX \ SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ error, asoc, \ - sctp_state_tbl[sctp_id2assoc(ep->base.sk, \ - sctp_assoc2id(asoc))?asoc->state:SCTP_STATE_CLOSED]) + sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ + sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED]) /* * This is the master state machine processing function. @@ -256,7 +256,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, sctp_cmd_t *cmd; sctp_chunk_t *new_obj; sctp_chunk_t *chunk = NULL; - sctp_packet_t *packet; + struct sctp_packet *packet; struct list_head *pos; struct timer_list *timer; unsigned long timeout; @@ -716,13 +716,12 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands) asoc->peer.sack_needed = 1; goto out; } else { + if (asoc->a_rwnd > asoc->rwnd) + asoc->a_rwnd = asoc->rwnd; sack = sctp_make_sack(asoc); if (!sack) goto nomem; - /* Update the last advertised rwnd value. */ - asoc->a_rwnd = asoc->rwnd; - asoc->peer.sack_needed = 0; error = sctp_outq_tail(&asoc->outqueue, sack); @@ -1223,13 +1222,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, sctp_state_t state) { + + struct sock *sk = asoc->base.sk; + struct sctp_opt *sp = sctp_sk(sk); + asoc->state = state; asoc->state_timestamp = jiffies; - /* Wake up any process waiting for the association to - * get established. + if ((SCTP_STATE_ESTABLISHED == asoc->state) || + (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 (waitqueue_active(&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) && - (waitqueue_active(&asoc->wait))) - wake_up_interruptible(&asoc->wait); + (SCTP_SOCKET_TCP == sp->type) && (SCTP_SS_CLOSED == sk->state)) + sk->state = SCTP_SS_ESTABLISHED; } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index e2709bd8e59f..9e2862892013 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -189,7 +189,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, sctp_chunk_t *repl; sctp_association_t *new_asoc; sctp_chunk_t *err_chunk; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_unrecognized_param_t *unk_param; int len; @@ -354,10 +354,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, sctp_init_chunk_t *initchunk; __u32 init_tag; sctp_chunk_t *err_chunk; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_disposition_t ret; - /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -912,14 +911,14 @@ static int sctp_sf_send_restart_abort(union sctp_addr *ssa, sctp_cmd_seq_t *commands) { int len; - sctp_packet_t *pkt; + struct sctp_packet *pkt; sctp_addr_param_t *addrparm; sctp_errhdr_t *errhdr; sctp_endpoint_t *ep; char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)]; - /* Build the error on the stack. We are way to malloc - * malloc crazy throughout the code today. + /* Build the error on the stack. We are way to malloc crazy + * throughout the code today. */ errhdr = (sctp_errhdr_t *)buffer; addrparm = (sctp_addr_param_t *)errhdr->variable; @@ -1105,11 +1104,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( sctp_chunk_t *repl; sctp_association_t *new_asoc; sctp_chunk_t *err_chunk; - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_unrecognized_param_t *unk_param; int len; - /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -2351,7 +2349,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, * room. Note: Playing nice with a confused sender. A * malicious sender can still eat up all our buffer * space and in the future we may want to detect and - * do more drastic reneging. + * do more drastic reneging. */ if (sctp_tsnmap_has_gap(&asoc->peer.tsn_map) && (sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1) == tsn) { @@ -2751,7 +2749,7 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { - sctp_packet_t *packet = NULL; + struct sctp_packet *packet = NULL; sctp_chunk_t *chunk = arg; sctp_chunk_t *abort; @@ -2953,7 +2951,7 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, void *arg, sctp_cmd_seq_t *commands) { - sctp_packet_t *packet = NULL; + struct sctp_packet *packet = NULL; sctp_chunk_t *chunk = arg; sctp_chunk_t *shut; @@ -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 * 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, sctp_chunk_t *chunk, const void *payload, size_t paylen) { - sctp_packet_t *packet; + struct sctp_packet *packet; sctp_chunk_t *abort; packet = sctp_ootb_pkt_new(asoc, chunk); @@ -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. */ -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) { - sctp_packet_t *packet; + struct sctp_packet *packet; struct sctp_transport *transport; __u16 sport; __u16 dport; @@ -4449,7 +4447,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, goto nomem; /* 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) goto nomem_packet; @@ -4471,7 +4469,7 @@ nomem: } /* 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_packet_free(packet); @@ -4484,7 +4482,7 @@ void sctp_send_stale_cookie_err(const sctp_endpoint_t *ep, sctp_cmd_seq_t *commands, sctp_chunk_t *err_chunk) { - sctp_packet_t *packet; + struct sctp_packet *packet; if (err_chunk) { packet = sctp_ootb_pkt_new(asoc, chunk); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 85fcc4fa6ee9..bb91784b0c68 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -88,12 +88,46 @@ static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, int msg_len); static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); static int sctp_wait_for_connect(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 int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int); 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 * The syntax of bind() is, @@ -818,19 +852,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } } } 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) { err = -EINVAL; goto out_unlock; @@ -1007,7 +1029,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, sctp_set_owner_w(chunk); /* This flag, in the UDP model, requests the SCTP stack to - * override the primary destination address with the + * override the primary destination address with the * address found with the sendto/sendmsg call. */ if (sinfo_flags & MSG_ADDR_OVER) { @@ -1126,17 +1148,19 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr int err = 0; int skb_len; - SCTP_DEBUG_PRINTK("sctp_recvmsg(" - "%s: %p, %s: %p, %s: %d, %s: %d, %s: " - "0x%x, %s: %p)\n", - "sk", sk, - "msghdr", msg, - "len", len, - "knoblauch", noblock, - "flags", flags, - "addr_len", addr_len); + SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %d, %s: %d, %s: " + "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg, + "len", len, "knoblauch", noblock, + "flags", flags, "addr_len", addr_len); 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); if (!skb) goto out; @@ -1207,7 +1231,7 @@ out: 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) { int val; @@ -1223,8 +1247,8 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk, return 0; } -static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, - int optlen) +static int sctp_setsockopt_events(struct sock *sk, char *optval, + int optlen) { if (optlen != sizeof(struct sctp_event_subscribe)) return -EINVAL; @@ -1233,7 +1257,7 @@ static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, 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) { struct sctp_opt *sp = sctp_sk(sk); @@ -1250,9 +1274,8 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, return 0; } -static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, - char *optval, - int optlen) +static int sctp_setsockopt_peer_addr_params(struct sock *sk, + char *optval, int optlen) { struct sctp_paddrparams params; sctp_association_t *asoc; @@ -1290,8 +1313,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans); if (error) return error; - } - else { + } else { /* The value of the heartbeat interval, in milliseconds. A value of 0, * when modifying the parameter, specifies that the heartbeat on this * address should be disabled. @@ -1311,7 +1333,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, return 0; } -static inline int sctp_setsockopt_initmsg(struct sock *sk, char *optval, +static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen) { if (optlen != sizeof(struct sctp_initmsg)) @@ -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 * 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) { struct sctp_sndrcvinfo info; @@ -1359,6 +1381,66 @@ static inline int sctp_setsockopt_set_default_send_param(struct sock *sk, 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() * * 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, break; case SCTP_SET_EVENTS: - retval = sctp_setsockopt_set_events(sk, optval, optlen); + retval = sctp_setsockopt_events(sk, optval, optlen); break; case SCTP_AUTOCLOSE: @@ -1442,8 +1524,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, break; case SCTP_SET_PEER_ADDR_PARAMS: - retval = sctp_setsockopt_set_peer_addr_params(sk, optval, - optlen); + retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen); break; case SCTP_INITMSG: @@ -1451,8 +1532,16 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, break; case SCTP_SET_DEFAULT_SEND_PARAM: - retval = sctp_setsockopt_set_default_send_param(sk, - optval, optlen); + retval = sctp_setsockopt_default_send_param(sk, optval, + 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; default: @@ -1503,8 +1592,14 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, sp = sctp_sk(sk); ep = sp->ep; - /* connect() cannot be done on a peeled-off socket. */ - if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) { + /* connect() cannot be done on a socket that is already in ESTABLISHED + * 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; goto out_unlock; } @@ -1513,6 +1608,8 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, if (err) goto out_unlock; + if (addr_len > sizeof(to)) + addr_len = sizeof(to); memcpy(&to, uaddr, addr_len); to.v4.sin_port = ntohs(to.v4.sin_port); @@ -1585,13 +1682,63 @@ SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags) 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) { - 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); - *err = error; - return NULL; + 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; + return newsk; } /* FIXME: Write Comments. */ @@ -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_endpoint_t *ep; - sctp_protocol_t *proto; + struct sctp_protocol *proto; struct sctp_opt *sp; SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk); @@ -1617,7 +1764,16 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp = sctp_sk(sk); /* Initialize the SCTP per socket area. */ - sp->type = SCTP_SOCKET_UDP; + switch (sk->type) { + case SOCK_SEQPACKET: + 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 * should include a socket option for manipulating these @@ -1665,7 +1821,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) sp->disable_fragments = 0; /* Turn on/off any Nagle-like algorithm. */ - sp->nodelay = 0; + sp->nodelay = 1; /* Auto-close idle associations after the configured * number of seconds. A value of 0 disables this @@ -1714,11 +1870,17 @@ SCTP_STATIC void sctp_shutdown(struct sock *sk, int how) /* 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, int *optlen) { struct sctp_status status; - sctp_endpoint_t *ep; sctp_association_t *assoc = NULL; struct sctp_transport *transport; sctp_assoc_t associd; @@ -1735,20 +1897,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, } associd = status.sstat_assoc_id; - if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) { - assoc = sctp_id2assoc(sk, associd); - if (!assoc) { - retval = -EINVAL; - goto out; - } - } 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); + assoc = sctp_id2assoc(sk, associd); + if (!assoc) { + retval = -EINVAL; + goto out; } transport = assoc->peer.primary_path; @@ -1788,7 +1940,7 @@ out: 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) { int val; @@ -1805,7 +1957,7 @@ static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len, 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)) return -EINVAL; @@ -1814,7 +1966,7 @@ static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *opt 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 */ 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 struct sock *oldsk = assoc->base.sk; struct sock *newsk; 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; /* 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 return -EOPNOTSUPP; /* 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) return err; 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 - * 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 socket. - */ - newsp->type = SCTP_SOCKET_UDP_HIGH_BANDWIDTH; - - /* Migrate the association to the new socket. */ - sctp_assoc_migrate(assoc, newsk); + /* Populate the fields of the newsk from the oldsk and migrate the + * assoc to the newsk. + */ + sctp_sock_migrate(oldsk, newsk, assoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH); *newsock = tmpsock; 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; struct socket *newsock; @@ -1970,8 +2053,8 @@ out: return retval; } -static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, - int len, char *optval, int *optlen) +static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, + char *optval, int *optlen) { struct sctp_paddrparams params; sctp_association_t *asoc; @@ -2014,7 +2097,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, return 0; } -static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen) +static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen) { if (len != sizeof(struct sctp_initmsg)) return -EINVAL; @@ -2023,8 +2106,8 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval return 0; } -static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len, - char *optval, int *optlen) +static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len, + char *optval, int *optlen) { sctp_assoc_t id; sctp_association_t *asoc; @@ -2053,7 +2136,7 @@ static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len, 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) { sctp_association_t *asoc; @@ -2093,8 +2176,8 @@ static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len, - char *optval, int *optlen) +static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, + char *optval, int *optlen) { sctp_assoc_t id; sctp_bind_addr_t *bp; @@ -2132,8 +2215,8 @@ static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len, return 0; } -static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, - char *optval, int *optlen) +static int sctp_getsockopt_local_addrs(struct sock *sk, int len, + char *optval, int *optlen) { sctp_bind_addr_t *bp; sctp_association_t *asoc; @@ -2183,6 +2266,40 @@ static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len, 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) @@ -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. */ -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) { struct sctp_sndrcvinfo info; @@ -2227,6 +2344,33 @@ static inline int sctp_getsockopt_set_default_send_param(struct sock *sk, 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, char *optval, int *optlen) { @@ -2257,58 +2401,52 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_STATUS: retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen); break; - case SCTP_DISABLE_FRAGMENTS: retval = sctp_getsockopt_disable_fragments(sk, len, optval, optlen); break; - case SCTP_SET_EVENTS: retval = sctp_getsockopt_set_events(sk, len, optval, optlen); break; - case SCTP_AUTOCLOSE: retval = sctp_getsockopt_autoclose(sk, len, optval, optlen); break; - case SCTP_SOCKOPT_PEELOFF: retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); break; - case SCTP_GET_PEER_ADDR_PARAMS: - retval = sctp_getsockopt_get_peer_addr_params(sk, len, optval, - optlen); + retval = sctp_getsockopt_peer_addr_params(sk, len, optval, + optlen); break; - case SCTP_INITMSG: retval = sctp_getsockopt_initmsg(sk, len, optval, optlen); break; - case SCTP_GET_PEER_ADDRS_NUM: - retval = sctp_getsockopt_get_peer_addrs_num(sk, len, optval, - optlen); + retval = sctp_getsockopt_peer_addrs_num(sk, len, optval, + optlen); break; - case SCTP_GET_LOCAL_ADDRS_NUM: - retval = sctp_getsockopt_get_local_addrs_num(sk, len, optval, - optlen); + retval = sctp_getsockopt_local_addrs_num(sk, len, optval, + optlen); break; - case SCTP_GET_PEER_ADDRS: - retval = sctp_getsockopt_get_peer_addrs(sk, len, optval, - optlen); + retval = sctp_getsockopt_peer_addrs(sk, len, optval, + optlen); break; - case SCTP_GET_LOCAL_ADDRS: - retval = sctp_getsockopt_get_local_addrs(sk, len, optval, - optlen); + retval = sctp_getsockopt_local_addrs(sk, len, optval, + optlen); break; - case SCTP_SET_DEFAULT_SEND_PARAM: - retval = sctp_getsockopt_set_default_send_param(sk, len, - optval, optlen); + retval = sctp_getsockopt_default_send_param(sk, len, + optval, optlen); + 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: retval = -ENOPROTOOPT; break; @@ -2331,7 +2469,7 @@ static void sctp_unhash(struct sock *sk) /* Check if port is acceptable. Possibly find first available port. * * 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 * 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 @@ -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_bucket_t *pp; /* hash list port iterator */ - sctp_protocol_t *sctp = sctp_get_protocol(); + struct sctp_protocol *sctp = sctp_get_protocol(); unsigned short snum; int ret; @@ -2543,6 +2681,9 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) if (SCTP_SOCKET_UDP != sp->type) return -EINVAL; + 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 @@ -2563,6 +2704,40 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog) } /* + * 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. */ 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: err = sctp_seqpacket_listen(sk, backlog); break; - case SOCK_STREAM: - /* FIXME for TCP-style sockets. */ - err = -EOPNOTSUPP; + err = sctp_stream_listen(sk, backlog); + break; default: goto out; @@ -2684,7 +2858,7 @@ static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsi /* FIXME: Commments! */ 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_proto->port_hashtable[sctp_phashfn(inet_sk(sk)->num)]; sctp_bind_bucket_t *pp; @@ -2967,7 +3141,8 @@ no_packet: } /* 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; @@ -3213,7 +3388,7 @@ out: return err; do_error: - err = -ECONNABORTED; + err = -ECONNREFUSED; goto out; do_interrupted: @@ -3225,6 +3400,131 @@ do_nonblock: 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. */ struct proto sctp_prot = { .name = "SCTP", diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index f2fcce00c6ed..1e54322277e6 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -42,7 +42,7 @@ #include <net/sctp/structs.h> #include <linux/sysctl.h> -extern sctp_protocol_t sctp_proto; +extern struct sctp_protocol sctp_proto; static ctl_table sctp_table[] = { { diff --git a/net/sctp/transport.c b/net/sctp/transport.c index b9d68744a621..6ee6ca94aa6b 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -83,7 +83,7 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, const union sctp_addr *addr, int priority) { - sctp_protocol_t *proto = sctp_get_protocol(); + struct sctp_protocol *proto = sctp_get_protocol(); /* Copy in the address. */ peer->ipaddr = *addr; @@ -262,7 +262,7 @@ void sctp_transport_put(struct sctp_transport *transport) /* Update transport's RTO based on the newly calculated 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. */ SCTP_ASSERT(tp, "NULL transport", return); diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index 8773a7ee3ead..310c7f0b8c1b 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -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 * overflow map. */ - if (started & !ended) { + if (started && !ended) { ended++; _end = map->len + map->len - 1; } @@ -395,7 +395,7 @@ void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn) return; if (!TSN_lt(tsn, map->base_tsn + map->len + map->len)) return; - + /* Assert: TSN is in range. */ gap = tsn - map->base_tsn; diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 125946bdec10..e367b0735824 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -220,7 +220,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) if (sctp_event2skb(event)->list) sctp_skb_list_tail(sctp_event2skb(event)->list, queue); 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 * 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) sctp_ulpq_clear_pd(ulpq); if (queue == &sk->receive_queue) - wake_up_interruptible(sk->sleep); + sk->data_ready(sk, 0); return 1; out_free: @@ -247,14 +247,14 @@ out_free: static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) { - struct sk_buff *pos, *tmp; + struct sk_buff *pos; struct sctp_ulpevent *cevent; __u32 tsn, ctsn; tsn = event->sndrcvinfo.sinfo_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); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -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) { - struct sk_buff *pos, *tmp; + struct sk_buff *pos; struct sctp_ulpevent *cevent; struct sk_buff *first_frag = NULL; __u32 ctsn, next_tsn; @@ -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 * 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); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -374,29 +374,26 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u case SCTP_DATA_LAST_FRAG: if (first_frag && (ctsn == next_tsn)) - retval = sctp_make_reassembled_event( - first_frag, pos); + goto found; else first_frag = NULL; break; }; - /* We have the reassembled event. There is no need to look - * further. - */ - if (retval) { - retval->msg_flags |= MSG_EOR; - break; - } } - +done: 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. */ 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; __u32 ctsn, next_tsn; int is_last; @@ -415,7 +412,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq next_tsn = 0; is_last = 0; - sctp_skb_for_each(pos, &ulpq->reasm, tmp) { + skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -448,7 +445,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq */ done: retval = sctp_make_reassembled_event(first_frag, last_frag); - if (is_last) + if (retval && is_last) retval->msg_flags |= MSG_EOR; return retval; @@ -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. */ 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; __u32 ctsn, next_tsn; struct sctp_ulpevent *retval; @@ -507,7 +504,7 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *u retval = NULL; next_tsn = 0; - sctp_skb_for_each(pos, &ulpq->reasm, tmp) { + skb_queue_walk(&ulpq->reasm, pos) { cevent = sctp_skb2event(pos); ctsn = cevent->sndrcvinfo.sinfo_tsn; @@ -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, struct sctp_ulpevent *event) { - struct sk_buff *pos, *tmp; + struct sk_buff *pos; struct sctp_ulpevent *cevent; __u16 sid, csid; __u16 ssn, cssn; @@ -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 * 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; csid = cevent->sndrcvinfo.sinfo_stream; cssn = cevent->sndrcvinfo.sinfo_ssn; @@ -786,9 +783,9 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, int priority) SCTP_PARTIAL_DELIVERY_ABORTED, priority); 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 (sctp_ulpq_clear_pd(ulpq) || ev) - wake_up_interruptible(sk->sleep); + sk->data_ready(sk, 0); } |
