Commit 9b10f4ba authored by David S. Miller's avatar David S. Miller

Merge nuts.davemloft.net:/disk1/BK/network-2.6

into nuts.davemloft.net:/disk1/BK/net-2.6
parents 28f95425 9e130292
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003 * (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
...@@ -93,6 +93,9 @@ typedef enum { ...@@ -93,6 +93,9 @@ typedef enum {
SCTP_CID_ECN_CWR = 13, SCTP_CID_ECN_CWR = 13,
SCTP_CID_SHUTDOWN_COMPLETE = 14, SCTP_CID_SHUTDOWN_COMPLETE = 14,
/* PR-SCTP Sec 3.2 */
SCTP_CID_FWD_TSN = 0xC0,
/* Use hex, as defined in ADDIP sec. 3.1 */ /* Use hex, as defined in ADDIP sec. 3.1 */
SCTP_CID_ASCONF = 0xC1, SCTP_CID_ASCONF = 0xC1,
SCTP_CID_ASCONF_ACK = 0x80, SCTP_CID_ASCONF_ACK = 0x80,
...@@ -168,6 +171,9 @@ typedef enum { ...@@ -168,6 +171,9 @@ typedef enum {
SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12), SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12),
SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000), SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000),
/* PR-SCTP Sec 3.1 */
SCTP_PARAM_FWD_TSN_SUPPORT = __constant_htons(0xc000),
/* Add-IP Extension. Section 3.2 */ /* Add-IP Extension. Section 3.2 */
SCTP_PARAM_ADD_IP = __constant_htons(0xc001), SCTP_PARAM_ADD_IP = __constant_htons(0xc001),
SCTP_PARAM_DEL_IP = __constant_htons(0xc002), SCTP_PARAM_DEL_IP = __constant_htons(0xc002),
...@@ -472,9 +478,67 @@ typedef struct sctp_cwr_chunk { ...@@ -472,9 +478,67 @@ typedef struct sctp_cwr_chunk {
sctp_cwrhdr_t cwr_hdr; sctp_cwrhdr_t cwr_hdr;
} __attribute__((packed)) sctp_cwr_chunk_t; } __attribute__((packed)) sctp_cwr_chunk_t;
/* /* PR-SCTP
* ADDIP Section 3.1 New Chunk Types * 3.2 Forward Cumulative TSN Chunk Definition (FORWARD TSN)
*
* Forward Cumulative TSN chunk has the following format:
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 192 | Flags = 0x00 | Length = Variable |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | New Cumulative TSN |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Stream-1 | Stream Sequence-1 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* \ /
* / \
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Stream-N | Stream Sequence-N |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Chunk Flags:
*
* Set to all zeros on transmit and ignored on receipt.
*
* New Cumulative TSN: 32 bit u_int
*
* This indicates the new cumulative TSN to the data receiver. Upon
* the reception of this value, the data receiver MUST consider
* any missing TSNs earlier than or equal to this value as received
* and stop reporting them as gaps in any subsequent SACKs.
*
* Stream-N: 16 bit u_int
*
* This field holds a stream number that was skipped by this
* FWD-TSN.
*
* Stream Sequence-N: 16 bit u_int
* This field holds the sequence number associated with the stream
* that was skipped. The stream sequence field holds the largest stream
* sequence number in this stream being skipped. The receiver of
* the FWD-TSN's can use the Stream-N and Stream Sequence-N fields
* to enable delivery of any stranded TSN's that remain on the stream
* re-ordering queues. This field MUST NOT report TSN's corresponding
* to DATA chunk that are marked as unordered. For ordered DATA
* chunks this field MUST be filled in.
*/ */
struct sctp_fwdtsn_skip {
__u16 stream;
__u16 ssn;
} __attribute__((packed));
struct sctp_fwdtsn_hdr {
__u32 new_cum_tsn;
struct sctp_fwdtsn_skip skip[0];
} __attribute((packed));
struct sctp_fwdtsn_chunk {
struct sctp_chunkhdr chunk_hdr;
struct sctp_fwdtsn_hdr fwdtsn_hdr;
} __attribute((packed));
/* ADDIP /* ADDIP
* Section 3.1.1 Address Configuration Change Chunk (ASCONF) * Section 3.1.1 Address Configuration Change Chunk (ASCONF)
......
...@@ -602,8 +602,7 @@ enum { ...@@ -602,8 +602,7 @@ enum {
NET_SCTP_PRESERVE_ENABLE = 11, NET_SCTP_PRESERVE_ENABLE = 11,
NET_SCTP_MAX_BURST = 12, NET_SCTP_MAX_BURST = 12,
NET_SCTP_ADDIP_ENABLE = 13, NET_SCTP_ADDIP_ENABLE = 13,
NET_SCTP_RMEM = 14, NET_SCTP_PRSCTP_ENABLE = 14,
NET_SCTP_WMEM = 15,
}; };
/* /proc/sys/net/bridge */ /* /proc/sys/net/bridge */
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003 * (C) Copyright IBM Corp. 2001, 2004
* Copyright (C) 1999-2001 Cisco, Motorola * Copyright (C) 1999-2001 Cisco, Motorola
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us> * Karl Knutson <karl@athena.chicago.il.us>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -90,6 +91,8 @@ typedef enum { ...@@ -90,6 +91,8 @@ typedef enum {
SCTP_CMD_RENEGE, /* Renege data on an association. */ SCTP_CMD_RENEGE, /* Renege data on an association. */
SCTP_CMD_SETUP_T4, /* ADDIP, setup T4 RTO timer parms. */ SCTP_CMD_SETUP_T4, /* ADDIP, setup T4 RTO timer parms. */
SCTP_CMD_PROCESS_OPERR, /* Process an ERROR chunk. */ SCTP_CMD_PROCESS_OPERR, /* Process an ERROR chunk. */
SCTP_CMD_REPORT_FWDTSN, /* Report new cumulative TSN Ack. */
SCTP_CMD_PROCESS_FWDTSN, /* Skips were reported, so process further. */
SCTP_CMD_LAST SCTP_CMD_LAST
} sctp_verb_t; } sctp_verb_t;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003 * (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
...@@ -57,15 +57,6 @@ enum { SCTP_MAX_STREAM = 0xffff }; ...@@ -57,15 +57,6 @@ enum { SCTP_MAX_STREAM = 0xffff };
enum { SCTP_DEFAULT_OUTSTREAMS = 10 }; enum { SCTP_DEFAULT_OUTSTREAMS = 10 };
enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
/* Define the amount of space to reserve for SCTP, IP, LL.
* There is a little bit of waste that we are always allocating
* for ipv6 headers, but this seems worth the simplicity.
*/
#define SCTP_IP_OVERHEAD ((sizeof(struct sctphdr)\
+ sizeof(struct ipv6hdr)\
+ MAX_HEADER))
/* Since CIDs are sparse, we need all four of the following /* Since CIDs are sparse, we need all four of the following
* symbols. CIDs are dense through SCTP_CID_BASE_MAX. * symbols. CIDs are dense through SCTP_CID_BASE_MAX.
*/ */
...@@ -77,6 +68,8 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; ...@@ -77,6 +68,8 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
#define SCTP_NUM_ADDIP_CHUNK_TYPES 2 #define SCTP_NUM_ADDIP_CHUNK_TYPES 2
#define SCTP_NUM_PRSCTP_CHUNK_TYPES 1
/* These are the different flavours of event. */ /* These are the different flavours of event. */
typedef enum { typedef enum {
...@@ -355,7 +348,6 @@ typedef enum { ...@@ -355,7 +348,6 @@ typedef enum {
SCTP_XMIT_OK, SCTP_XMIT_OK,
SCTP_XMIT_PMTU_FULL, SCTP_XMIT_PMTU_FULL,
SCTP_XMIT_RWND_FULL, SCTP_XMIT_RWND_FULL,
SCTP_XMIT_MUST_FRAG,
SCTP_XMIT_NAGLE_DELAY, SCTP_XMIT_NAGLE_DELAY,
} sctp_xmit_t; } sctp_xmit_t;
......
...@@ -437,12 +437,15 @@ static inline __s32 sctp_jitter(__u32 rto) ...@@ -437,12 +437,15 @@ static inline __s32 sctp_jitter(__u32 rto)
static inline int sctp_frag_point(const struct sctp_opt *sp, int pmtu) static inline int sctp_frag_point(const struct sctp_opt *sp, int pmtu)
{ {
int frag = pmtu; int frag = pmtu;
frag -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk);
frag -= sizeof(struct sctp_sack_chunk); frag -= sp->pf->af->net_header_len;
frag -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk);
if (sp->user_frag) if (sp->user_frag)
frag = min_t(int, frag, sp->user_frag); frag = min_t(int, frag, sp->user_frag);
frag = min_t(int, frag, SCTP_MAX_CHUNK_LEN);
return frag; return frag;
} }
...@@ -472,6 +475,14 @@ for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \ ...@@ -472,6 +475,14 @@ for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
err = (sctp_errhdr_t *)((void *)err + \ err = (sctp_errhdr_t *)((void *)err + \
WORD_ROUND(ntohs(err->length)))) WORD_ROUND(ntohs(err->length))))
#define sctp_walk_fwdtsn(pos, chunk)\
_sctp_walk_fwdtsn((pos), (chunk), ntohs((chunk)->chunk_hdr->length) - sizeof(struct sctp_fwdtsn_chunk))
#define _sctp_walk_fwdtsn(pos, chunk, end)\
for (pos = chunk->subh.fwdtsn_hdr->skip;\
(void *)pos <= (void *)chunk->subh.fwdtsn_hdr->skip + end - sizeof(struct sctp_fwdtsn_skip);\
pos++)
/* Round an int up to the next multiple of 4. */ /* Round an int up to the next multiple of 4. */
#define WORD_ROUND(s) (((s)+3)&~3) #define WORD_ROUND(s) (((s)+3)&~3)
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003 * (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
...@@ -141,6 +141,9 @@ sctp_state_fn_t sctp_sf_cookie_echoed_err; ...@@ -141,6 +141,9 @@ sctp_state_fn_t sctp_sf_cookie_echoed_err;
sctp_state_fn_t sctp_sf_do_5_2_6_stale; sctp_state_fn_t sctp_sf_do_5_2_6_stale;
sctp_state_fn_t sctp_sf_do_asconf; sctp_state_fn_t sctp_sf_do_asconf;
sctp_state_fn_t sctp_sf_do_asconf_ack; sctp_state_fn_t sctp_sf_do_asconf_ack;
sctp_state_fn_t sctp_sf_do_9_2_reshutack;
sctp_state_fn_t sctp_sf_eat_fwd_tsn;
sctp_state_fn_t sctp_sf_eat_fwd_tsn_fast;
/* Prototypes for primitive event state functions. */ /* Prototypes for primitive event state functions. */
sctp_state_fn_t sctp_sf_do_prm_asoc; sctp_state_fn_t sctp_sf_do_prm_asoc;
...@@ -170,25 +173,6 @@ sctp_state_fn_t sctp_sf_do_6_3_3_rtx; ...@@ -170,25 +173,6 @@ sctp_state_fn_t sctp_sf_do_6_3_3_rtx;
sctp_state_fn_t sctp_sf_do_6_2_sack; sctp_state_fn_t sctp_sf_do_6_2_sack;
sctp_state_fn_t sctp_sf_autoclose_timer_expire; sctp_state_fn_t sctp_sf_autoclose_timer_expire;
/* These are state functions which are either obsolete or not in use yet.
* If any of these functions needs to be revived, it should be renamed with
* the "sctp_sf_xxx" prefix, and be moved to the above prototype groups.
*/
/* Prototypes for chunk state functions. Not in use. */
sctp_state_fn_t sctp_sf_do_9_2_reshutack;
sctp_state_fn_t sctp_sf_do_9_2_reshut;
sctp_state_fn_t sctp_sf_do_9_2_shutack;
/* Prototypes for timeout event state functions. Not in use. */
sctp_state_fn_t sctp_do_4_2_reinit;
sctp_state_fn_t sctp_do_4_3_reecho;
sctp_state_fn_t sctp_do_9_2_reshut;
sctp_state_fn_t sctp_do_9_2_reshutack;
sctp_state_fn_t sctp_do_8_3_hb_err;
sctp_state_fn_t sctp_heartoff;
/* Prototypes for utility support functions. */ /* Prototypes for utility support functions. */
__u8 sctp_get_chunk_type(struct sctp_chunk *chunk); __u8 sctp_get_chunk_type(struct sctp_chunk *chunk);
const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t, const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t,
...@@ -277,6 +261,9 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, ...@@ -277,6 +261,9 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf); struct sctp_chunk *asconf);
int sctp_process_asconf_ack(struct sctp_association *asoc, int sctp_process_asconf_ack(struct sctp_association *asoc,
struct sctp_chunk *asconf_ack); struct sctp_chunk *asconf_ack);
struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
__u32 new_cum_tsn, size_t nstreams,
struct sctp_fwdtsn_skip *skiplist);
void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *);
......
...@@ -193,6 +193,9 @@ extern struct sctp_globals { ...@@ -193,6 +193,9 @@ extern struct sctp_globals {
/* Flag to indicate if addip is enabled. */ /* Flag to indicate if addip is enabled. */
int addip_enable; int addip_enable;
/* Flag to indicate if PR-SCTP is enabled. */
int prsctp_enable;
} sctp_globals; } sctp_globals;
#define sctp_rto_initial (sctp_globals.rto_initial) #define sctp_rto_initial (sctp_globals.rto_initial)
...@@ -221,6 +224,7 @@ extern struct sctp_globals { ...@@ -221,6 +224,7 @@ extern struct sctp_globals {
#define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_list (sctp_globals.local_addr_list)
#define sctp_local_addr_lock (sctp_globals.local_addr_lock) #define sctp_local_addr_lock (sctp_globals.local_addr_lock)
#define sctp_addip_enable (sctp_globals.addip_enable) #define sctp_addip_enable (sctp_globals.addip_enable)
#define sctp_prsctp_enable (sctp_globals.prsctp_enable)
/* SCTP Socket type: UDP or TCP style. */ /* SCTP Socket type: UDP or TCP style. */
typedef enum { typedef enum {
...@@ -317,6 +321,8 @@ struct sctp_cookie { ...@@ -317,6 +321,8 @@ struct sctp_cookie {
/* This holds the originating address of the INIT packet. */ /* This holds the originating address of the INIT packet. */
union sctp_addr peer_addr; union sctp_addr peer_addr;
__u8 prsctp_capable;
/* This is a shim for my peer's INIT packet, followed by /* This is a shim for my peer's INIT packet, followed by
* a copy of the raw address list of the association. * a copy of the raw address list of the association.
* The length of the raw address list is saved in the * The length of the raw address list is saved in the
...@@ -413,6 +419,13 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id) ...@@ -413,6 +419,13 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
return stream->ssn[id]++; return stream->ssn[id]++;
} }
/* Skip over this ssn and all below. */
static inline void sctp_ssn_skip(struct sctp_stream *stream, __u16 id,
__u16 ssn)
{
stream->ssn[id] = ssn+1;
}
/* /*
* Pointers to address related SCTP functions. * Pointers to address related SCTP functions.
* (i.e. things that depend on the address family.) * (i.e. things that depend on the address family.)
...@@ -514,8 +527,8 @@ struct sctp_datamsg { ...@@ -514,8 +527,8 @@ struct sctp_datamsg {
/* Did the messenge fail to send? */ /* Did the messenge fail to send? */
int send_error; int send_error;
char send_failed; char send_failed;
/* Control whether fragments from this message can expire. */ /* Control whether chunks from this message can be abandoned. */
char can_expire; char can_abandon;
}; };
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *, struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *,
...@@ -527,8 +540,8 @@ void sctp_datamsg_hold(struct sctp_datamsg *); ...@@ -527,8 +540,8 @@ void sctp_datamsg_hold(struct sctp_datamsg *);
void sctp_datamsg_free(struct sctp_datamsg *); void sctp_datamsg_free(struct sctp_datamsg *);
void sctp_datamsg_track(struct sctp_chunk *); void sctp_datamsg_track(struct sctp_chunk *);
void sctp_datamsg_assign(struct sctp_datamsg *, struct sctp_chunk *); void sctp_datamsg_assign(struct sctp_datamsg *, struct sctp_chunk *);
void sctp_datamsg_fail(struct sctp_chunk *, int error); void sctp_chunk_fail(struct sctp_chunk *, int error);
int sctp_datamsg_expires(struct sctp_chunk *); int sctp_chunk_abandoned(struct sctp_chunk *);
/* RFC2960 1.4 Key Terms /* RFC2960 1.4 Key Terms
...@@ -583,6 +596,7 @@ struct sctp_chunk { ...@@ -583,6 +596,7 @@ struct sctp_chunk {
struct sctp_cwrhdr *ecn_cwr_hdr; struct sctp_cwrhdr *ecn_cwr_hdr;
struct sctp_errhdr *err_hdr; struct sctp_errhdr *err_hdr;
struct sctp_addiphdr *addip_hdr; struct sctp_addiphdr *addip_hdr;
struct sctp_fwdtsn_hdr *fwdtsn_hdr;
} subh; } subh;
__u8 *chunk_end; __u8 *chunk_end;
...@@ -667,6 +681,9 @@ struct sctp_packet { ...@@ -667,6 +681,9 @@ struct sctp_packet {
/* This contains the payload chunks. */ /* This contains the payload chunks. */
struct sk_buff_head chunks; struct sk_buff_head chunks;
/* This is the overhead of the sctp and ip headers. */
size_t overhead;
/* This is the total size of all chunks INCLUDING padding. */ /* This is the total size of all chunks INCLUDING padding. */
size_t size; size_t size;
...@@ -676,16 +693,6 @@ struct sctp_packet { ...@@ -676,16 +693,6 @@ struct sctp_packet {
*/ */
struct sctp_transport *transport; struct sctp_transport *transport;
/* Allow a callback for getting a high priority chunk
* bundled early into the packet (This is used for ECNE).
*/
sctp_packet_phandler_t *get_prepend_chunk;
/* This packet should advertise ECN capability to the network
* via the ECT bit.
*/
char ecn_capable;
/* This packet contains a COOKIE-ECHO chunk. */ /* This packet contains a COOKIE-ECHO chunk. */
char has_cookie_echo; char has_cookie_echo;
...@@ -698,29 +705,21 @@ struct sctp_packet { ...@@ -698,29 +705,21 @@ struct sctp_packet {
int malloced; int malloced;
}; };
typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *); struct sctp_packet *sctp_packet_init(struct sctp_packet *,
typedef int (sctp_outq_ehandler_t)(struct sctp_outq *);
typedef struct sctp_packet *(sctp_outq_ohandler_init_t)
(struct sctp_packet *,
struct sctp_transport *, struct sctp_transport *,
__u16 sport, __u16 sport, __u16 dport);
__u16 dport); struct sctp_packet *sctp_packet_config(struct sctp_packet *, __u32 vtag, int);
typedef struct sctp_packet *(sctp_outq_ohandler_config_t) sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *,
(struct sctp_packet *,
__u32 vtag,
int ecn_capable,
sctp_packet_phandler_t *get_prepend_chunk);
typedef sctp_xmit_t (sctp_outq_ohandler_t)(struct sctp_packet *,
struct sctp_chunk *); struct sctp_chunk *);
typedef int (sctp_outq_ohandler_force_t)(struct sctp_packet *); sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *,
struct sctp_chunk *);
sctp_outq_ohandler_init_t sctp_packet_init; int sctp_packet_transmit(struct sctp_packet *);
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(struct sctp_packet *); void sctp_packet_free(struct sctp_packet *);
static inline int sctp_packet_empty(struct sctp_packet *packet)
{
return (packet->size == packet->overhead);
}
/* This represents a remote transport address. /* This represents a remote transport address.
* For local transport addresses, we just use union sctp_addr. * For local transport addresses, we just use union sctp_addr.
...@@ -905,7 +904,7 @@ struct sctp_transport { ...@@ -905,7 +904,7 @@ struct sctp_transport {
/* A flag which indicates the occurrence of a changeover */ /* A flag which indicates the occurrence of a changeover */
char changeover_active; char changeover_active;
/* A glag which indicates whether the change of primary is /* A flag which indicates whether the change of primary is
* the first switch to this destination address during an * the first switch to this destination address during an
* active switch. * active switch.
*/ */
...@@ -1008,15 +1007,10 @@ struct sctp_outq { ...@@ -1008,15 +1007,10 @@ struct sctp_outq {
*/ */
struct list_head retransmit; struct list_head retransmit;
/* Call these functions to send chunks down to the next lower /* Put chunks on this list to save them for FWD TSN processing as
* layer. This is always sctp_packet, but we separate the two * they were abandoned.
* structures to make testing simpler.
*/ */
sctp_outq_ohandler_init_t *init_output; struct list_head abandoned;
sctp_outq_ohandler_config_t *config_output;
sctp_outq_ohandler_t *append_output;
sctp_outq_ohandler_t *build_output;
sctp_outq_ohandler_force_t *force_output;
/* How many unackd bytes do we have in-flight? */ /* How many unackd bytes do we have in-flight? */
__u32 outstanding_bytes; __u32 outstanding_bytes;
...@@ -1039,12 +1033,6 @@ int sctp_outq_tail(struct sctp_outq *, struct sctp_chunk *chunk); ...@@ -1039,12 +1033,6 @@ int sctp_outq_tail(struct sctp_outq *, struct sctp_chunk *chunk);
int sctp_outq_flush(struct sctp_outq *, int); int sctp_outq_flush(struct sctp_outq *, int);
int sctp_outq_sack(struct sctp_outq *, struct sctp_sackhdr *); int sctp_outq_sack(struct sctp_outq *, struct sctp_sackhdr *);
int sctp_outq_is_empty(const struct sctp_outq *); int sctp_outq_is_empty(const struct sctp_outq *);
int sctp_outq_set_output_handlers(struct sctp_outq *,
sctp_outq_ohandler_init_t init,
sctp_outq_ohandler_config_t config,
sctp_outq_ohandler_t append,
sctp_outq_ohandler_t build,
sctp_outq_ohandler_force_t force);
void sctp_outq_restart(struct sctp_outq *); void sctp_outq_restart(struct sctp_outq *);
void sctp_retransmit(struct sctp_outq *, struct sctp_transport *, void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
...@@ -1387,16 +1375,25 @@ struct sctp_association { ...@@ -1387,16 +1375,25 @@ struct sctp_association {
struct sctp_tsnmap tsn_map; struct sctp_tsnmap tsn_map;
__u8 _map[sctp_tsnmap_storage_size(SCTP_TSN_MAP_SIZE)]; __u8 _map[sctp_tsnmap_storage_size(SCTP_TSN_MAP_SIZE)];
/* Do we need to sack the peer? */ /* Ack State : This flag indicates if the next received
__u8 sack_needed; * : packet is to be responded to with a
* : SACK. This is initializedto 0. When a packet
* : is received it is incremented. If this value
* : reaches 2 or more, a SACK is sent and the
* : value is reset to 0. Note: This is used only
* : when no DATA chunks are received out of
* : order. When DATA chunks are out of order,
* : SACK's are not delayed (see Section 6).
*/
__u8 sack_needed; /* Do we need to sack the peer? */
/* These are capabilities which our peer advertised. */ /* These are capabilities which our peer advertised. */
__u8 ecn_capable; /* Can peer do ECN? */ __u8 ecn_capable; /* Can peer do ECN? */
__u8 ipv4_address; /* Peer understands IPv4 addresses? */ __u8 ipv4_address; /* Peer understands IPv4 addresses? */
__u8 ipv6_address; /* Peer understands IPv6 addresses? */ __u8 ipv6_address; /* Peer understands IPv6 addresses? */
__u8 hostname_address;/* Peer understands DNS addresses? */ __u8 hostname_address;/* Peer understands DNS addresses? */
__u8 asconf_capable; /* Does peer support ADDIP? */
/* Does peer support ADDIP? */ __u8 prsctp_capable; /* Can peer do PR-SCTP? */
__u8 asconf_capable;
/* This mask is used to disable sending the ASCONF chunk /* This mask is used to disable sending the ASCONF chunk
* with specified parameter to peer. * with specified parameter to peer.
...@@ -1489,6 +1486,9 @@ struct sctp_association { ...@@ -1489,6 +1486,9 @@ struct sctp_association {
__u32 ctsn_ack_point; __u32 ctsn_ack_point;
/* PR-SCTP Advanced.Peer.Ack.Point */
__u32 adv_peer_ack_point;
/* Highest TSN that is acknowledged by incoming SACKs. */ /* Highest TSN that is acknowledged by incoming SACKs. */
__u32 highest_sacked; __u32 highest_sacked;
...@@ -1529,19 +1529,7 @@ struct sctp_association { ...@@ -1529,19 +1529,7 @@ struct sctp_association {
/* The message size at which SCTP fragmentation will occur. */ /* The message size at which SCTP fragmentation will occur. */
__u32 frag_point; __u32 frag_point;
/* Ack State : This flag indicates if the next received /* Currently only one counter is used to count INIT errors. */
* : packet is to be responded to with a
* : SACK. This is initializedto 0. When a packet
* : is received it is incremented. If this value
* : reaches 2 or more, a SACK is sent and the
* : value is reset to 0. Note: This is used only
* : when no DATA chunks are received out of
* : order. When DATA chunks are out of order,
* : SACK's are not delayed (see Section 6).
*/
/* Do we need to send an ack?
* When counters[SctpCounterAckState] is above 1 we do!
*/
int counters[SCTP_NUMBER_COUNTERS]; int counters[SCTP_NUMBER_COUNTERS];
/* Default send parameters. */ /* Default send parameters. */
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us> * Karl Knutson <karl@athena.chicago.il.us>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -145,6 +146,9 @@ int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn); ...@@ -145,6 +146,9 @@ int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn);
/* Mark this TSN as seen. */ /* Mark this TSN as seen. */
void sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn); void sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn);
/* Mark this TSN and all lower as seen. */
void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn);
/* Retrieve the Cumulative TSN ACK Point. */ /* Retrieve the Cumulative TSN ACK Point. */
static inline __u32 sctp_tsnmap_get_ctsn(const struct sctp_tsnmap *map) static inline __u32 sctp_tsnmap_get_ctsn(const struct sctp_tsnmap *map)
{ {
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc. * Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
* Written or modified by: * Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -79,6 +80,9 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *, int); ...@@ -79,6 +80,9 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *, int);
/* Clear the partial data delivery condition on this socket. */ /* Clear the partial data delivery condition on this socket. */
int sctp_clear_pd(struct sock *sk); int sctp_clear_pd(struct sock *sk);
/* Skip over an SSN. */
void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn);
#endif /* __sctp_ulpqueue_h__ */ #endif /* __sctp_ulpqueue_h__ */
......
...@@ -210,6 +210,7 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc, ...@@ -210,6 +210,7 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
asoc->next_tsn = asoc->c.initial_tsn; asoc->next_tsn = asoc->c.initial_tsn;
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
asoc->highest_sacked = asoc->ctsn_ack_point; asoc->highest_sacked = asoc->ctsn_ack_point;
asoc->last_cwr_tsn = asoc->ctsn_ack_point; asoc->last_cwr_tsn = asoc->ctsn_ack_point;
asoc->unack_data = 0; asoc->unack_data = 0;
...@@ -261,12 +262,6 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc, ...@@ -261,12 +262,6 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
/* Create an output queue. */ /* Create an output queue. */
sctp_outq_init(asoc, &asoc->outqueue); sctp_outq_init(asoc, &asoc->outqueue);
sctp_outq_set_output_handlers(&asoc->outqueue,
sctp_packet_init,
sctp_packet_config,
sctp_packet_append_chunk,
sctp_packet_transmit_chunk,
sctp_packet_transmit);
if (!sctp_ulpq_init(&asoc->ulpq, asoc)) if (!sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init; goto fail_init;
...@@ -482,9 +477,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -482,9 +477,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* The asoc->peer.port might not be meaningful yet, but /* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway. * initialize the packet structure anyway.
*/ */
(asoc->outqueue.init_output)(&peer->packet, sctp_packet_init(&peer->packet, peer, asoc->base.bind_addr.port,
peer,
asoc->base.bind_addr.port,
asoc->peer.port); asoc->peer.port);
/* 7.2.1 Slow-Start /* 7.2.1 Slow-Start
...@@ -967,6 +960,7 @@ void sctp_assoc_update(struct sctp_association *asoc, ...@@ -967,6 +960,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
asoc->next_tsn = new->next_tsn; asoc->next_tsn = new->next_tsn;
asoc->ctsn_ack_point = new->ctsn_ack_point; asoc->ctsn_ack_point = new->ctsn_ack_point;
asoc->adv_peer_ack_point = new->adv_peer_ack_point;
/* Reinitialize SSN for both local streams /* Reinitialize SSN for both local streams
* and peer's streams. * and peer's streams.
...@@ -975,6 +969,7 @@ void sctp_assoc_update(struct sctp_association *asoc, ...@@ -975,6 +969,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
} else { } else {
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
if (!asoc->ssnmap) { if (!asoc->ssnmap) {
/* Move the ssnmap. */ /* Move the ssnmap. */
asoc->ssnmap = new->ssnmap; asoc->ssnmap = new->ssnmap;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 2003 International Business Machines Corp. * (C) Copyright IBM Corp. 2003, 2004
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* *
* Written or modified by: * Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -55,7 +56,8 @@ void sctp_datamsg_init(struct sctp_datamsg *msg) ...@@ -55,7 +56,8 @@ void sctp_datamsg_init(struct sctp_datamsg *msg)
atomic_set(&msg->refcnt, 1); atomic_set(&msg->refcnt, 1);
msg->send_failed = 0; msg->send_failed = 0;
msg->send_error = 0; msg->send_error = 0;
msg->can_expire = 0; msg->can_abandon = 0;
msg->expires_at = 0;
INIT_LIST_HEAD(&msg->chunks); INIT_LIST_HEAD(&msg->chunks);
} }
...@@ -182,34 +184,17 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -182,34 +184,17 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
* have the same expiration. * have the same expiration.
*/ */
if (sinfo->sinfo_timetolive) { if (sinfo->sinfo_timetolive) {
struct timeval tv;
__u32 ttl = sinfo->sinfo_timetolive;
/* sinfo_timetolive is in milliseconds */ /* sinfo_timetolive is in milliseconds */
tv.tv_sec = ttl / 1000; msg->expires_at = jiffies +
tv.tv_usec = ttl % 1000 * 1000; MSECS_TO_JIFFIES(sinfo->sinfo_timetolive);
msg->expires_at = jiffies + timeval_to_jiffies(&tv); msg->can_abandon = 1;
msg->can_expire = 1; SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld\n",
__FUNCTION__, msg, msg->expires_at, jiffies);
} }
/* What is a reasonable fragmentation point right now? */ max = asoc->frag_point;
max = asoc->pmtu;
if (max < SCTP_MIN_PMTU)
max = SCTP_MIN_PMTU;
max -= SCTP_IP_OVERHEAD;
/* Make sure not beyond maximum chunk size. */
if (max > SCTP_MAX_CHUNK_LEN)
max = SCTP_MAX_CHUNK_LEN;
/* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk);
whole = 0; whole = 0;
/* If user has specified smaller fragmentation, make it so. */
if (sctp_sk(asoc->base.sk)->user_frag)
max = min_t(int, max, sctp_sk(asoc->base.sk)->user_frag);
first_len = max; first_len = max;
/* Encourage Cookie-ECHO bundling. */ /* Encourage Cookie-ECHO bundling. */
...@@ -303,14 +288,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -303,14 +288,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
} }
/* Check whether this message has expired. */ /* Check whether this message has expired. */
int sctp_datamsg_expires(struct sctp_chunk *chunk) int sctp_chunk_abandoned(struct sctp_chunk *chunk)
{ {
struct sctp_datamsg *msg = chunk->msg; struct sctp_datamsg *msg = chunk->msg;
/* FIXME: When PR-SCTP is supported we can make this if (!msg->can_abandon)
* check more lenient.
*/
if (!msg->can_expire)
return 0; return 0;
if (time_after(jiffies, msg->expires_at)) if (time_after(jiffies, msg->expires_at))
...@@ -320,7 +302,7 @@ int sctp_datamsg_expires(struct sctp_chunk *chunk) ...@@ -320,7 +302,7 @@ int sctp_datamsg_expires(struct sctp_chunk *chunk)
} }
/* This chunk (and consequently entire message) has failed in its sending. */ /* This chunk (and consequently entire message) has failed in its sending. */
void sctp_datamsg_fail(struct sctp_chunk *chunk, int error) void sctp_chunk_fail(struct sctp_chunk *chunk, int error)
{ {
chunk->msg->send_failed = 1; chunk->msg->send_failed = 1;
chunk->msg->send_error = error; chunk->msg->send_error = error;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
* Xingang Guo <xingang.guo@intel.com> * Xingang Guo <xingang.guo@intel.com>
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -88,6 +89,9 @@ const char *sctp_cname(const sctp_subtype_t cid) ...@@ -88,6 +89,9 @@ const char *sctp_cname(const sctp_subtype_t cid)
case SCTP_CID_ASCONF_ACK: case SCTP_CID_ASCONF_ACK:
return "ASCONF_ACK"; return "ASCONF_ACK";
case SCTP_CID_FWD_TSN:
return "FWD_TSN";
default: default:
return "unknown chunk"; return "unknown chunk";
}; };
......
...@@ -62,29 +62,35 @@ ...@@ -62,29 +62,35 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Forward declarations for private helpers. */ /* Forward declarations for private helpers. */
static void sctp_packet_reset(struct sctp_packet *packet);
static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
struct sctp_chunk *chunk); struct sctp_chunk *chunk);
/* Config a packet. /* Config a packet.
* This appears to be a followup set of initializations.) * This appears to be a followup set of initializations.
*/ */
struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
__u32 vtag, int ecn_capable, __u32 vtag, int ecn_capable)
sctp_packet_phandler_t *prepend_handler)
{ {
int packet_empty = (packet->size == SCTP_IP_OVERHEAD); struct sctp_chunk *chunk = NULL;
SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __FUNCTION__,
packet, vtag);
packet->vtag = vtag; packet->vtag = vtag;
packet->ecn_capable = ecn_capable;
packet->get_prepend_chunk = prepend_handler;
packet->has_cookie_echo = 0; packet->has_cookie_echo = 0;
packet->has_sack = 0; packet->has_sack = 0;
packet->ipfragok = 0; packet->ipfragok = 0;
/* We might need to call the prepend_handler right away. */ if (ecn_capable && sctp_packet_empty(packet)) {
if (packet_empty) chunk = sctp_get_ecne_prepend(packet->transport->asoc);
sctp_packet_reset(packet);
/* If there a is a prepend chunk stick it on the list before
* any other chunks get appended.
*/
if (chunk)
sctp_packet_append_chunk(packet, chunk);
}
return packet; return packet;
} }
...@@ -93,19 +99,30 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, ...@@ -93,19 +99,30 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
struct sctp_transport *transport, struct sctp_transport *transport,
__u16 sport, __u16 dport) __u16 sport, __u16 dport)
{ {
struct sctp_association *asoc = transport->asoc;
size_t overhead;
SCTP_DEBUG_PRINTK("%s: packet:%p transport:%p\n", __FUNCTION__,
packet, transport);
packet->transport = transport; packet->transport = transport;
packet->source_port = sport; packet->source_port = sport;
packet->destination_port = dport; packet->destination_port = dport;
skb_queue_head_init(&packet->chunks); skb_queue_head_init(&packet->chunks);
packet->size = SCTP_IP_OVERHEAD; if (asoc) {
struct sctp_opt *sp = sctp_sk(asoc->base.sk);
overhead = sp->pf->af->net_header_len;
} else {
overhead = sizeof(struct ipv6hdr);
}
overhead += sizeof(struct sctphdr);
packet->overhead = overhead;
packet->size = overhead;
packet->vtag = 0; packet->vtag = 0;
packet->ecn_capable = 0;
packet->get_prepend_chunk = NULL;
packet->has_cookie_echo = 0; packet->has_cookie_echo = 0;
packet->has_sack = 0; packet->has_sack = 0;
packet->ipfragok = 0; packet->ipfragok = 0;
packet->malloced = 0; packet->malloced = 0;
sctp_packet_reset(packet);
return packet; return packet;
} }
...@@ -114,6 +131,8 @@ void sctp_packet_free(struct sctp_packet *packet) ...@@ -114,6 +131,8 @@ void sctp_packet_free(struct sctp_packet *packet)
{ {
struct sctp_chunk *chunk; struct sctp_chunk *chunk;
SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks)))
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
...@@ -134,6 +153,9 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, ...@@ -134,6 +153,9 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
sctp_xmit_t retval; sctp_xmit_t retval;
int error = 0; int error = 0;
SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__,
packet, chunk);
switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) { switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_PMTU_FULL:
if (!packet->has_cookie_echo) { if (!packet->has_cookie_echo) {
...@@ -148,7 +170,6 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, ...@@ -148,7 +170,6 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
} }
break; break;
case SCTP_XMIT_MUST_FRAG:
case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_RWND_FULL:
case SCTP_XMIT_OK: case SCTP_XMIT_OK:
case SCTP_XMIT_NAGLE_DELAY: case SCTP_XMIT_NAGLE_DELAY:
...@@ -201,6 +222,9 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, ...@@ -201,6 +222,9 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
size_t pmtu; size_t pmtu;
int too_big; int too_big;
SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__, packet,
chunk);
retval = sctp_packet_bundle_sack(packet, chunk); retval = sctp_packet_bundle_sack(packet, chunk);
psize = packet->size; psize = packet->size;
...@@ -215,17 +239,14 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, ...@@ -215,17 +239,14 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
/* Decide if we need to fragment or resubmit later. */ /* Decide if we need to fragment or resubmit later. */
if (too_big) { if (too_big) {
int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
/* Both control chunks and data chunks with TSNs are /* Both control chunks and data chunks with TSNs are
* non-fragmentable. * non-fragmentable.
*/ */
if (packet_empty || !sctp_chunk_is_data(chunk)) { if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk)) {
/* We no longer do re-fragmentation. /* We no longer do re-fragmentation.
* Just fragment at the IP layer, if we * Just fragment at the IP layer, if we
* actually hit this condition * actually hit this condition
*/ */
packet->ipfragok = 1; packet->ipfragok = 1;
goto append; goto append;
...@@ -233,9 +254,6 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, ...@@ -233,9 +254,6 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
retval = SCTP_XMIT_PMTU_FULL; retval = SCTP_XMIT_PMTU_FULL;
goto finish; goto finish;
} }
} else {
/* The chunk fits in the packet. */
goto append;
} }
append: append:
...@@ -260,6 +278,7 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, ...@@ -260,6 +278,7 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
/* It is OK to send this chunk. */ /* It is OK to send this chunk. */
__skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk); __skb_queue_tail(&packet->chunks, (struct sk_buff *)chunk);
packet->size += chunk_len; packet->size += chunk_len;
chunk->transport = packet->transport;
finish: finish:
return retval; return retval;
} }
...@@ -283,6 +302,8 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -283,6 +302,8 @@ int sctp_packet_transmit(struct sctp_packet *packet)
__u8 has_data = 0; __u8 has_data = 0;
struct dst_entry *dst; struct dst_entry *dst;
SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
/* Do NOT generate a chunkless packet. */ /* Do NOT generate a chunkless packet. */
chunk = (struct sctp_chunk *)skb_peek(&packet->chunks); chunk = (struct sctp_chunk *)skb_peek(&packet->chunks);
if (unlikely(!chunk)) if (unlikely(!chunk))
...@@ -297,7 +318,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -297,7 +318,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
goto nomem; goto nomem;
/* Make sure the outbound skb has enough header room reserved. */ /* Make sure the outbound skb has enough header room reserved. */
skb_reserve(nskb, SCTP_IP_OVERHEAD); skb_reserve(nskb, packet->overhead);
/* Set the owning socket so that we know where to get the /* Set the owning socket so that we know where to get the
* destination IP address. * destination IP address.
...@@ -471,7 +492,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -471,7 +492,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
(*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok); (*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
out: out:
packet->size = SCTP_IP_OVERHEAD; packet->size = packet->overhead;
return err; return err;
no_route: no_route:
kfree_skb(nskb); kfree_skb(nskb);
...@@ -497,7 +518,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -497,7 +518,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
goto out; goto out;
nomem: nomem:
err = -ENOMEM; err = -ENOMEM;
printk("%s alloc_skb failed.\n", __FUNCTION__);
goto err; goto err;
} }
...@@ -505,25 +525,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) ...@@ -505,25 +525,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* 2nd Level Abstractions * 2nd Level Abstractions
********************************************************************/ ********************************************************************/
/*
* This private function resets the packet to a fresh state.
*/
static void sctp_packet_reset(struct sctp_packet *packet)
{
struct sctp_chunk *chunk = NULL;
packet->size = SCTP_IP_OVERHEAD;
if (packet->get_prepend_chunk)
chunk = packet->get_prepend_chunk(packet->transport->asoc);
/* If there a is a prepend chunk stick it on the list before
* any other chunks get appended.
*/
if (chunk)
sctp_packet_append_chunk(packet, chunk);
}
/* This private function handles the specifics of appending DATA chunks. */ /* This private function handles the specifics of appending DATA chunks. */
static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
struct sctp_chunk *chunk) struct sctp_chunk *chunk)
...@@ -609,7 +610,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, ...@@ -609,7 +610,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
* if any previously transmitted data on the connection remains * if any previously transmitted data on the connection remains
* unacknowledged. * unacknowledged.
*/ */
if (!sp->nodelay && SCTP_IP_OVERHEAD == packet->size && if (!sp->nodelay && sctp_packet_empty(packet) &&
q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) { q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) {
unsigned len = datasize + q->out_qlen; unsigned len = datasize + q->out_qlen;
...@@ -617,7 +618,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, ...@@ -617,7 +618,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
* data will fit or delay in hopes of bundling a full * data will fit or delay in hopes of bundling a full
* sized packet. * sized packet.
*/ */
if (len < asoc->pmtu - SCTP_IP_OVERHEAD) { if (len < asoc->pmtu - packet->overhead) {
retval = SCTP_XMIT_NAGLE_DELAY; retval = SCTP_XMIT_NAGLE_DELAY;
goto finish; goto finish;
} }
...@@ -637,7 +638,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet, ...@@ -637,7 +638,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
asoc->peer.rwnd = rwnd; asoc->peer.rwnd = rwnd;
/* Has been accepted for transmission. */ /* Has been accepted for transmission. */
chunk->msg->can_expire = 0; if (!asoc->peer.prsctp_capable)
chunk->msg->can_abandon = 0;
finish: finish:
return retval; return retval;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 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 * This file is part of the SCTP kernel reference Implementation
* *
...@@ -69,6 +69,8 @@ static void sctp_mark_missing(struct sctp_outq *q, ...@@ -69,6 +69,8 @@ static void sctp_mark_missing(struct sctp_outq *q,
__u32 highest_new_tsn, __u32 highest_new_tsn,
int count_of_newacks); int count_of_newacks);
static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 sack_ctsn);
/* Add data to the front of the queue. */ /* Add data to the front of the queue. */
static inline void sctp_outq_head_data(struct sctp_outq *q, static inline void sctp_outq_head_data(struct sctp_outq *q,
struct sctp_chunk *ch) struct sctp_chunk *ch)
...@@ -222,12 +224,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q) ...@@ -222,12 +224,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
skb_queue_head_init(&q->control); skb_queue_head_init(&q->control);
INIT_LIST_HEAD(&q->retransmit); INIT_LIST_HEAD(&q->retransmit);
INIT_LIST_HEAD(&q->sacked); INIT_LIST_HEAD(&q->sacked);
INIT_LIST_HEAD(&q->abandoned);
q->init_output = NULL;
q->config_output = NULL;
q->append_output = NULL;
q->build_output = NULL;
q->force_output = NULL;
q->outstanding_bytes = 0; q->outstanding_bytes = 0;
q->empty = 1; q->empty = 1;
...@@ -252,7 +249,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -252,7 +249,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
/* Mark as part of a failed message. */ /* Mark as part of a failed message. */
sctp_datamsg_fail(chunk, q->error); sctp_chunk_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
} }
...@@ -262,7 +259,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -262,7 +259,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del_init(lchunk); list_del_init(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
sctp_datamsg_fail(chunk, q->error); sctp_chunk_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
...@@ -271,7 +268,16 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -271,7 +268,16 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del_init(lchunk); list_del_init(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
sctp_datamsg_fail(chunk, q->error); sctp_chunk_fail(chunk, q->error);
sctp_chunk_free(chunk);
}
/* Throw away any chunks that are in the abandoned queue. */
list_for_each_safe(lchunk, temp, &q->abandoned) {
list_del_init(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_chunk_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
...@@ -279,7 +285,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -279,7 +285,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
while ((chunk = sctp_outq_dequeue_data(q))) { while ((chunk = sctp_outq_dequeue_data(q))) {
/* Mark as send failure. */ /* Mark as send failure. */
sctp_datamsg_fail(chunk, q->error); sctp_chunk_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
...@@ -363,32 +369,30 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) ...@@ -363,32 +369,30 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
return error; return error;
} }
/* Insert a chunk into the retransmit queue. Chunks on the retransmit /* Insert a chunk into the sorted list based on the TSNs. The retransmit list
* queue are kept in order, based on the TSNs. * and the abandoned list are in ascending order.
*/ */
void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q) void sctp_insert_list(struct list_head *head, struct list_head *new)
{ {
struct list_head *rlchunk; struct list_head *pos;
struct sctp_chunk *tchunk, *rchunk; struct sctp_chunk *nchunk, *lchunk;
__u32 ttsn, rtsn; __u32 ntsn, ltsn;
int done = 0; int done = 0;
tchunk = list_entry(tlchunk, struct sctp_chunk, transmitted_list); nchunk = list_entry(new, struct sctp_chunk, transmitted_list);
ttsn = ntohl(tchunk->subh.data_hdr->tsn); ntsn = ntohl(nchunk->subh.data_hdr->tsn);
list_for_each(rlchunk, &q->retransmit) { list_for_each(pos, head) {
rchunk = list_entry(rlchunk, struct sctp_chunk, lchunk = list_entry(pos, struct sctp_chunk, transmitted_list);
transmitted_list); ltsn = ntohl(lchunk->subh.data_hdr->tsn);
rtsn = ntohl(rchunk->subh.data_hdr->tsn); if (TSN_lt(ntsn, ltsn)) {
if (TSN_lt(ttsn, rtsn)) { list_add(new, pos->prev);
list_add(tlchunk, rlchunk->prev);
done = 1; done = 1;
break; break;
} }
} }
if (!done) { if (!done)
list_add_tail(tlchunk, &q->retransmit); list_add_tail(new, head);
}
} }
/* Mark all the eligible packets on a transport for retransmission. */ /* Mark all the eligible packets on a transport for retransmission. */
...@@ -404,6 +408,13 @@ void sctp_retransmit_mark(struct sctp_outq *q, ...@@ -404,6 +408,13 @@ void sctp_retransmit_mark(struct sctp_outq *q,
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
/* If the chunk is abandoned, move it to abandoned list. */
if (sctp_chunk_abandoned(chunk)) {
list_del_init(lchunk);
sctp_insert_list(&q->abandoned, lchunk);
continue;
}
/* If we are doing retransmission due to a fast retransmit, /* If we are doing retransmission due to a fast retransmit,
* only the chunk's that are marked for fast retransmit * only the chunk's that are marked for fast retransmit
* should be added to the retransmit queue. If we are doing * should be added to the retransmit queue. If we are doing
...@@ -444,10 +455,10 @@ void sctp_retransmit_mark(struct sctp_outq *q, ...@@ -444,10 +455,10 @@ void sctp_retransmit_mark(struct sctp_outq *q,
} }
/* Move the chunk to the retransmit queue. The chunks /* Move the chunk to the retransmit queue. The chunks
* on the retransmit queue is always kept in order. * on the retransmit queue are always kept in order.
*/ */
list_del_init(lchunk); list_del_init(lchunk);
sctp_retransmit_insert(lchunk, q); sctp_insert_list(&q->retransmit, lchunk);
} }
} }
...@@ -490,6 +501,12 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, ...@@ -490,6 +501,12 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
sctp_retransmit_mark(q, transport, fast_retransmit); sctp_retransmit_mark(q, transport, fast_retransmit);
/* PR-SCTP A5) Any time the T3-rtx timer expires, on any destination,
* the sender SHOULD try to advance the "Advanced.Peer.Ack.Point" by
* following the procedures outlined in C1 - C5.
*/
sctp_generate_fwdtsn(q, q->asoc->ctsn_ack_point);
error = sctp_outq_flush(q, /* rtx_timeout */ 1); error = sctp_outq_flush(q, /* rtx_timeout */ 1);
if (error) if (error)
...@@ -552,12 +569,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -552,12 +569,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
} }
/* Attempt to append this chunk to the packet. */ /* Attempt to append this chunk to the packet. */
status = (*q->append_output)(pkt, chunk); status = sctp_packet_append_chunk(pkt, chunk);
switch (status) { switch (status) {
case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_PMTU_FULL:
/* Send this packet. */ /* Send this packet. */
if ((error = (*q->force_output)(pkt)) == 0) if ((error = sctp_packet_transmit(pkt)) == 0)
*start_timer = 1; *start_timer = 1;
/* If we are retransmitting, we should only /* If we are retransmitting, we should only
...@@ -573,7 +590,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -573,7 +590,7 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_RWND_FULL:
/* Send this packet. */ /* Send this packet. */
if ((error = (*q->force_output)(pkt)) == 0) if ((error = sctp_packet_transmit(pkt)) == 0)
*start_timer = 1; *start_timer = 1;
/* Stop sending DATA as there is no more room /* Stop sending DATA as there is no more room
...@@ -583,6 +600,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -583,6 +600,16 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
lchunk = NULL; lchunk = NULL;
break; break;
case SCTP_XMIT_NAGLE_DELAY:
/* Send this packet. */
if ((error = sctp_packet_transmit(pkt)) == 0)
*start_timer = 1;
/* Stop sending DATA because of nagle delay. */
list_add(lchunk, lqueue);
lchunk = NULL;
break;
default: default:
/* The append was successful, so add this chunk to /* The append was successful, so add this chunk to
* the transmitted list. * the transmitted list.
...@@ -625,13 +652,9 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -625,13 +652,9 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
struct sctp_packet *packet; struct sctp_packet *packet;
struct sctp_packet singleton; struct sctp_packet singleton;
struct sctp_association *asoc = q->asoc; struct sctp_association *asoc = q->asoc;
int ecn_capable = asoc->peer.ecn_capable;
__u16 sport = asoc->base.bind_addr.port; __u16 sport = asoc->base.bind_addr.port;
__u16 dport = asoc->peer.port; __u16 dport = asoc->peer.port;
__u32 vtag = asoc->peer.i.init_tag; __u32 vtag = asoc->peer.i.init_tag;
/* This is the ECNE handler for singleton packets. */
sctp_packet_phandler_t *s_ecne_handler = NULL;
sctp_packet_phandler_t *ecne_handler = NULL;
struct sk_buff_head *queue; struct sk_buff_head *queue;
struct sctp_transport *transport = NULL; struct sctp_transport *transport = NULL;
struct sctp_transport *new_transport; struct sctp_transport *new_transport;
...@@ -656,10 +679,6 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -656,10 +679,6 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
* within a SCTP packet in increasing order of TSN. * within a SCTP packet in increasing order of TSN.
* ... * ...
*/ */
if (ecn_capable) {
s_ecne_handler = &sctp_get_no_prepend;
ecne_handler = &sctp_get_ecne_prepend;
}
queue = &q->control; queue = &q->control;
while ((chunk = (struct sctp_chunk *)skb_dequeue(queue))) { while ((chunk = (struct sctp_chunk *)skb_dequeue(queue))) {
...@@ -686,8 +705,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -686,8 +705,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
&transport_list); &transport_list);
} }
packet = &transport->packet; packet = &transport->packet;
(*q->config_output)(packet, vtag, sctp_packet_config(packet, vtag,
ecn_capable, ecne_handler); asoc->peer.ecn_capable);
} }
switch (chunk->chunk_hdr->type) { switch (chunk->chunk_hdr->type) {
...@@ -700,11 +719,10 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -700,11 +719,10 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
case SCTP_CID_INIT: case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK: case SCTP_CID_INIT_ACK:
case SCTP_CID_SHUTDOWN_COMPLETE: case SCTP_CID_SHUTDOWN_COMPLETE:
(*q->init_output)(&singleton, transport, sport, dport); sctp_packet_init(&singleton, transport, sport, dport);
(*q->config_output)(&singleton, vtag, ecn_capable, sctp_packet_config(&singleton, vtag, 0);
s_ecne_handler); sctp_packet_append_chunk(&singleton, chunk);
(void) (*q->build_output)(&singleton, chunk); error = sctp_packet_transmit(&singleton);
error = (*q->force_output)(&singleton);
if (error < 0) if (error < 0)
return error; return error;
break; break;
...@@ -720,12 +738,10 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -720,12 +738,10 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
case SCTP_CID_COOKIE_ACK: case SCTP_CID_COOKIE_ACK:
case SCTP_CID_ECN_ECNE: case SCTP_CID_ECN_ECNE:
case SCTP_CID_ECN_CWR: case SCTP_CID_ECN_CWR:
(void) (*q->build_output)(packet, chunk);
break;
case SCTP_CID_ASCONF: case SCTP_CID_ASCONF:
case SCTP_CID_ASCONF_ACK: case SCTP_CID_ASCONF_ACK:
(void) (*q->build_output)(packet, chunk); case SCTP_CID_FWD_TSN:
sctp_packet_transmit_chunk(packet, chunk);
break; break;
default: default:
...@@ -770,8 +786,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -770,8 +786,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
} }
packet = &transport->packet; packet = &transport->packet;
(*q->config_output)(packet, vtag, sctp_packet_config(packet, vtag,
ecn_capable, ecne_handler); asoc->peer.ecn_capable);
retran: retran:
error = sctp_outq_flush_rtx(q, packet, error = sctp_outq_flush_rtx(q, packet,
rtx_timeout, &start_timer); rtx_timeout, &start_timer);
...@@ -803,15 +819,15 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -803,15 +819,15 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
if (chunk->sinfo.sinfo_stream >= if (chunk->sinfo.sinfo_stream >=
asoc->c.sinit_num_ostreams) { asoc->c.sinit_num_ostreams) {
/* Mark as s failed send. */ /* Mark as failed send. */
sctp_datamsg_fail(chunk, SCTP_ERROR_INV_STRM); sctp_chunk_fail(chunk, SCTP_ERROR_INV_STRM);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
continue; continue;
} }
/* Has this chunk expired? */ /* Has this chunk expired? */
if (sctp_datamsg_expires(chunk)) { if (sctp_chunk_abandoned(chunk)) {
sctp_datamsg_fail(chunk, 0); sctp_chunk_fail(chunk, 0);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
continue; continue;
} }
...@@ -836,11 +852,11 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -836,11 +852,11 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
} }
packet = &transport->packet; packet = &transport->packet;
(*q->config_output)(packet, vtag, sctp_packet_config(packet, vtag,
ecn_capable, ecne_handler); asoc->peer.ecn_capable);
} }
SCTP_DEBUG_PRINTK("sctp_transmit_packet(%p, %p[%s]), ", SCTP_DEBUG_PRINTK("sctp_outq_flush(%p, %p[%s]), ",
q, chunk, q, chunk,
chunk && chunk->chunk_hdr ? chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK( sctp_cname(SCTP_ST_CHUNK(
...@@ -855,7 +871,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -855,7 +871,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
atomic_read(&chunk->skb->users) : -1); atomic_read(&chunk->skb->users) : -1);
/* Add the chunk to the packet. */ /* Add the chunk to the packet. */
status = (*q->build_output)(packet, chunk); status = sctp_packet_transmit_chunk(packet, chunk);
switch (status) { switch (status) {
case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_PMTU_FULL:
...@@ -879,7 +895,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -879,7 +895,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
BUG(); BUG();
} }
/* BUG: We assume that the (*q->force_output()) /* BUG: We assume that the sctp_packet_transmit()
* call below will succeed all the time and add the * call below will succeed all the time and add the
* chunk to the transmitted list and restart the * chunk to the transmitted list and restart the
* timers. * timers.
...@@ -922,33 +938,14 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -922,33 +938,14 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
struct sctp_transport *t = list_entry(ltransport, struct sctp_transport *t = list_entry(ltransport,
struct sctp_transport, struct sctp_transport,
send_ready); send_ready);
if (t != transport) packet = &t->packet;
transport = t; if (!sctp_packet_empty(packet))
error = sctp_packet_transmit(packet);
packet = &transport->packet;
if (packet->size != SCTP_IP_OVERHEAD)
error = (*q->force_output)(packet);
} }
return error; return error;
} }
/* Set the various output handling callbacks. */
int sctp_outq_set_output_handlers(struct sctp_outq *q,
sctp_outq_ohandler_init_t init,
sctp_outq_ohandler_config_t config,
sctp_outq_ohandler_t append,
sctp_outq_ohandler_t build,
sctp_outq_ohandler_force_t force)
{
q->init_output = init;
q->config_output = config;
q->append_output = append;
q->build_output = build;
q->force_output = force;
return 0;
}
/* Update unack_data based on the incoming SACK chunk */ /* Update unack_data based on the incoming SACK chunk */
static void sctp_sack_update_unack_data(struct sctp_association *assoc, static void sctp_sack_update_unack_data(struct sctp_association *assoc,
struct sctp_sackhdr *sack) struct sctp_sackhdr *sack)
...@@ -1007,7 +1004,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack) ...@@ -1007,7 +1004,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
{ {
struct sctp_association *asoc = q->asoc; struct sctp_association *asoc = q->asoc;
struct sctp_transport *transport; struct sctp_transport *transport;
struct sctp_chunk *tchunk; struct sctp_chunk *tchunk = NULL;
struct list_head *lchunk, *transport_list, *pos, *temp; struct list_head *lchunk, *transport_list, *pos, *temp;
sctp_sack_variable_t *frags = sack->variable; sctp_sack_variable_t *frags = sack->variable;
__u32 sack_ctsn, ctsn, tsn; __u32 sack_ctsn, ctsn, tsn;
...@@ -1110,11 +1107,6 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack) ...@@ -1110,11 +1107,6 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
ctsn = asoc->ctsn_ack_point; ctsn = asoc->ctsn_ack_point;
SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
__FUNCTION__, sack_ctsn);
SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association "
"%p is 0x%x.\n", __FUNCTION__, asoc, ctsn);
/* Throw away stuff rotting on the sack queue. */ /* Throw away stuff rotting on the sack queue. */
list_for_each_safe(lchunk, temp, &q->sacked) { list_for_each_safe(lchunk, temp, &q->sacked) {
tchunk = list_entry(lchunk, struct sctp_chunk, tchunk = list_entry(lchunk, struct sctp_chunk,
...@@ -1139,10 +1131,19 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack) ...@@ -1139,10 +1131,19 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
asoc->peer.rwnd = sack_a_rwnd; asoc->peer.rwnd = sack_a_rwnd;
sctp_generate_fwdtsn(q, sack_ctsn);
SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n",
__FUNCTION__, sack_ctsn);
SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association, "
"%p is 0x%x. Adv peer ack point: 0x%x\n",
__FUNCTION__, asoc, ctsn, asoc->adv_peer_ack_point);
/* See if all chunks are acked. /* See if all chunks are acked.
* Make sure the empty queue handler will get run later. * Make sure the empty queue handler will get run later.
*/ */
q->empty = skb_queue_empty(&q->out) && list_empty(&q->retransmit); q->empty = skb_queue_empty(&q->out) && skb_queue_empty(&q->control) &&
list_empty(&q->retransmit);
if (!q->empty) if (!q->empty)
goto finish; goto finish;
...@@ -1218,6 +1219,12 @@ static void sctp_check_transmitted(struct sctp_outq *q, ...@@ -1218,6 +1219,12 @@ static void sctp_check_transmitted(struct sctp_outq *q,
tchunk = list_entry(lchunk, struct sctp_chunk, tchunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
if (sctp_chunk_abandoned(tchunk)) {
/* Move the chunk to abandoned list. */
sctp_insert_list(&q->abandoned, lchunk);
continue;
}
tsn = ntohl(tchunk->subh.data_hdr->tsn); tsn = ntohl(tchunk->subh.data_hdr->tsn);
if (sctp_acked(sack, tsn)) { if (sctp_acked(sack, tsn)) {
/* If this queue is the retransmit queue, the /* If this queue is the retransmit queue, the
...@@ -1599,3 +1606,123 @@ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn) ...@@ -1599,3 +1606,123 @@ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn)
pass: pass:
return 1; return 1;
} }
static inline int sctp_get_skip_pos(struct sctp_fwdtsn_skip *skiplist,
int nskips, __u16 stream)
{
int i;
for (i = 0; i < nskips; i++) {
if (skiplist[i].stream == stream)
return i;
}
return i;
}
/* Create and add a fwdtsn chunk to the outq's control queue if needed. */
static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 ctsn)
{
struct sctp_association *asoc = q->asoc;
struct sctp_chunk *ftsn_chunk = NULL;
struct sctp_fwdtsn_skip ftsn_skip_arr[10];
int nskips = 0;
int skip_pos = 0;
__u32 tsn;
struct sctp_chunk *chunk;
struct list_head *lchunk, *temp;
/* PR-SCTP C1) Let SackCumAck be the Cumulative TSN ACK carried in the
* received SACK.
*
* If (Advanced.Peer.Ack.Point < SackCumAck), then update
* Advanced.Peer.Ack.Point to be equal to SackCumAck.
*/
if (TSN_lt(asoc->adv_peer_ack_point, ctsn))
asoc->adv_peer_ack_point = ctsn;
/* PR-SCTP C2) Try to further advance the "Advanced.Peer.Ack.Point"
* locally, that is, to move "Advanced.Peer.Ack.Point" up as long as
* the chunk next in the out-queue space is marked as "abandoned" as
* shown in the following example:
*
* Assuming that a SACK arrived with the Cumulative TSN ACK 102
* and the Advanced.Peer.Ack.Point is updated to this value:
*
* out-queue at the end of ==> out-queue after Adv.Ack.Point
* normal SACK processing local advancement
* ... ...
* Adv.Ack.Pt-> 102 acked 102 acked
* 103 abandoned 103 abandoned
* 104 abandoned Adv.Ack.P-> 104 abandoned
* 105 105
* 106 acked 106 acked
* ... ...
*
* In this example, the data sender successfully advanced the
* "Advanced.Peer.Ack.Point" from 102 to 104 locally.
*/
list_for_each_safe(lchunk, temp, &q->abandoned) {
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
tsn = ntohl(chunk->subh.data_hdr->tsn);
/* Remove any chunks in the abandoned queue that are acked by
* the ctsn.
*/
if (TSN_lte(tsn, ctsn)) {
list_del_init(lchunk);
if (!chunk->tsn_gap_acked) {
chunk->transport->flight_size -=
sctp_data_size(chunk);
q->outstanding_bytes -= sctp_data_size(chunk);
}
sctp_chunk_free(chunk);
} else {
if (TSN_lte(tsn, asoc->adv_peer_ack_point+1)) {
asoc->adv_peer_ack_point = tsn;
if (chunk->chunk_hdr->flags &
SCTP_DATA_UNORDERED)
continue;
skip_pos = sctp_get_skip_pos(&ftsn_skip_arr[0],
nskips,
chunk->subh.data_hdr->stream);
ftsn_skip_arr[skip_pos].stream =
chunk->subh.data_hdr->stream;
ftsn_skip_arr[skip_pos].ssn =
chunk->subh.data_hdr->ssn;
if (skip_pos == nskips)
nskips++;
if (nskips == 10)
break;
} else
break;
}
}
/* PR-SCTP C3) If, after step C1 and C2, the "Advanced.Peer.Ack.Point"
* is greater than the Cumulative TSN ACK carried in the received
* SACK, the data sender MUST send the data receiver a FORWARD TSN
* chunk containing the latest value of the
* "Advanced.Peer.Ack.Point".
*
* C4) For each "abandoned" TSN the sender of the FORWARD TSN SHOULD
* list each stream and sequence number in the forwarded TSN. This
* information will enable the receiver to easily find any
* stranded TSN's waiting on stream reorder queues. Each stream
* SHOULD only be reported once; this means that if multiple
* abandoned messages occur in the same stream then only the
* highest abandoned stream sequence number is reported. If the
* total size of the FORWARD TSN does NOT fit in a single MTU then
* the sender of the FORWARD TSN SHOULD lower the
* Advanced.Peer.Ack.Point to the last TSN that will fit in a
* single MTU.
*/
if (asoc->adv_peer_ack_point > ctsn)
ftsn_chunk = sctp_make_fwdtsn(asoc, asoc->adv_peer_ack_point,
nskips, &ftsn_skip_arr[0]);
if (ftsn_chunk) {
__skb_queue_tail(&q->control, (struct sk_buff *)ftsn_chunk);
SCTP_INC_STATS(SctpOutCtrlChunks);
}
}
...@@ -1004,7 +1004,9 @@ __init int sctp_init(void) ...@@ -1004,7 +1004,9 @@ __init int sctp_init(void)
goto err_init_mibs; goto err_init_mibs;
/* Initialize proc fs directory. */ /* Initialize proc fs directory. */
sctp_proc_init(); status = sctp_proc_init();
if (status)
goto err_init_proc;
/* Initialize object count debugging. */ /* Initialize object count debugging. */
sctp_dbg_objcnt_init(); sctp_dbg_objcnt_init();
...@@ -1127,6 +1129,9 @@ __init int sctp_init(void) ...@@ -1127,6 +1129,9 @@ __init int sctp_init(void)
/* Disable ADDIP by default. */ /* Disable ADDIP by default. */
sctp_addip_enable = 0; sctp_addip_enable = 0;
/* Enable PR-SCTP by default. */
sctp_prsctp_enable = 1;
sctp_sysctl_register(); sctp_sysctl_register();
INIT_LIST_HEAD(&sctp_address_families); INIT_LIST_HEAD(&sctp_address_families);
...@@ -1171,6 +1176,7 @@ __init int sctp_init(void) ...@@ -1171,6 +1176,7 @@ __init int sctp_init(void)
sizeof(struct sctp_hashbucket))); sizeof(struct sctp_hashbucket)));
err_ahash_alloc: err_ahash_alloc:
sctp_dbg_objcnt_exit(); sctp_dbg_objcnt_exit();
err_init_proc:
sctp_proc_exit(); sctp_proc_exit();
cleanup_sctp_mibs(); cleanup_sctp_mibs();
err_init_mibs: err_init_mibs:
......
...@@ -6,10 +6,6 @@ ...@@ -6,10 +6,6 @@
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
* This file includes 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 functions work with the state functions in sctp_sm_statefuns.c * These functions work with the state functions in sctp_sm_statefuns.c
* to implement the state operations. These functions implement the * to implement the state operations. These functions implement the
* steps which require modifying existing data structures. * steps which require modifying existing data structures.
...@@ -89,11 +85,13 @@ int sctp_chunk_iif(const struct sctp_chunk *chunk) ...@@ -89,11 +85,13 @@ int sctp_chunk_iif(const struct sctp_chunk *chunk)
* Note 2: The ECN capable field is reserved for future use of * Note 2: The ECN capable field is reserved for future use of
* Explicit Congestion Notification. * Explicit Congestion Notification.
*/ */
static const sctp_ecn_capable_param_t ecap_param = { static const struct sctp_paramhdr ecap_param = {
{
SCTP_PARAM_ECN_CAPABLE, SCTP_PARAM_ECN_CAPABLE,
__constant_htons(sizeof(sctp_ecn_capable_param_t)), __constant_htons(sizeof(struct sctp_paramhdr)),
} };
static const struct sctp_paramhdr prsctp_param = {
SCTP_PARAM_FWD_TSN_SUPPORT,
__constant_htons(sizeof(struct sctp_paramhdr)),
}; };
/* A helper to initialize to initialize an op error inside a /* A helper to initialize to initialize an op error inside a
...@@ -196,6 +194,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, ...@@ -196,6 +194,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types); chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
chunksize += sizeof(ecap_param); chunksize += sizeof(ecap_param);
if (sctp_prsctp_enable)
chunksize += sizeof(prsctp_param);
chunksize += vparam_len; chunksize += vparam_len;
/* RFC 2960 3.3.2 Initiation (INIT) (1) /* RFC 2960 3.3.2 Initiation (INIT) (1)
...@@ -232,6 +232,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, ...@@ -232,6 +232,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
sctp_addto_chunk(retval, num_types * sizeof(__u16), &types); sctp_addto_chunk(retval, num_types * sizeof(__u16), &types);
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
if (sctp_prsctp_enable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
nodata: nodata:
if (addrs.v) if (addrs.v)
kfree(addrs.v); kfree(addrs.v);
...@@ -278,6 +280,10 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, ...@@ -278,6 +280,10 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
if (asoc->peer.ecn_capable) if (asoc->peer.ecn_capable)
chunksize += sizeof(ecap_param); chunksize += sizeof(ecap_param);
/* Tell peer that we'll do PR-SCTP only if peer advertised. */
if (asoc->peer.prsctp_capable)
chunksize += sizeof(prsctp_param);
/* Now allocate and fill out the chunk. */ /* Now allocate and fill out the chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize); retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
if (!retval) if (!retval)
...@@ -293,6 +299,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, ...@@ -293,6 +299,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
sctp_addto_chunk(retval, cookie_len, cookie); sctp_addto_chunk(retval, cookie_len, cookie);
if (asoc->peer.ecn_capable) if (asoc->peer.ecn_capable)
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
if (asoc->peer.prsctp_capable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
/* We need to remove the const qualifier at this point. */ /* We need to remove the const qualifier at this point. */
retval->asoc = (struct sctp_association *) asoc; retval->asoc = (struct sctp_association *) asoc;
...@@ -1286,6 +1294,9 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, ...@@ -1286,6 +1294,9 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
/* Save the raw address list length in the cookie. */ /* Save the raw address list length in the cookie. */
cookie->c.raw_addr_list_len = addrs_len; cookie->c.raw_addr_list_len = addrs_len;
/* Remember PR-SCTP capability. */
cookie->c.prsctp_capable = asoc->peer.prsctp_capable;
/* Set an expiration time for the cookie. */ /* Set an expiration time for the cookie. */
do_gettimeofday(&cookie->c.expiration); do_gettimeofday(&cookie->c.expiration);
TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration); TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration);
...@@ -1442,6 +1453,8 @@ struct sctp_association *sctp_unpack_cookie( ...@@ -1442,6 +1453,8 @@ struct sctp_association *sctp_unpack_cookie(
retval->next_tsn = retval->c.initial_tsn; retval->next_tsn = retval->c.initial_tsn;
retval->ctsn_ack_point = retval->next_tsn - 1; retval->ctsn_ack_point = retval->next_tsn - 1;
retval->addip_serial = retval->c.initial_tsn; retval->addip_serial = retval->c.initial_tsn;
retval->adv_peer_ack_point = retval->ctsn_ack_point;
retval->peer.prsctp_capable = retval->c.prsctp_capable;
/* The INIT stuff will be done by the side effects. */ /* The INIT stuff will be done by the side effects. */
return retval; return retval;
...@@ -1653,6 +1666,10 @@ static int sctp_verify_param(const struct sctp_association *asoc, ...@@ -1653,6 +1666,10 @@ static int sctp_verify_param(const struct sctp_association *asoc,
case SCTP_PARAM_HOST_NAME_ADDRESS: case SCTP_PARAM_HOST_NAME_ADDRESS:
/* Tell the peer, we won't support this param. */ /* Tell the peer, we won't support this param. */
return sctp_process_hn_param(asoc, param, chunk, err_chunk); return sctp_process_hn_param(asoc, param, chunk, err_chunk);
case SCTP_PARAM_FWD_TSN_SUPPORT:
if (sctp_prsctp_enable)
break;
/* Fall Through */
default: default:
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n", SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
ntohs(param.p->type), cid); ntohs(param.p->type), cid);
...@@ -1968,6 +1985,12 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, ...@@ -1968,6 +1985,12 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param,
asoc->peer.ecn_capable = 1; asoc->peer.ecn_capable = 1;
break; break;
case SCTP_PARAM_FWD_TSN_SUPPORT:
if (sctp_prsctp_enable) {
asoc->peer.prsctp_capable = 1;
break;
}
/* Fall Through */
default: default:
/* Any unrecognized parameters should have been caught /* Any unrecognized parameters should have been caught
* and handled by sctp_verify_param() which should be * and handled by sctp_verify_param() which should be
...@@ -2622,3 +2645,38 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, ...@@ -2622,3 +2645,38 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
return retval; return retval;
} }
/* Make a FWD TSN chunk. */
struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
__u32 new_cum_tsn, size_t nstreams,
struct sctp_fwdtsn_skip *skiplist)
{
struct sctp_chunk *retval = NULL;
struct sctp_fwdtsn_chunk *ftsn_chunk;
struct sctp_fwdtsn_hdr ftsn_hdr;
struct sctp_fwdtsn_skip skip;
size_t hint;
int i;
hint = (nstreams + 1) * sizeof(__u32);
/* Maybe set the T-bit if we have no association. */
retval = sctp_make_chunk(asoc, SCTP_CID_FWD_TSN, 0, hint);
if (!retval)
return NULL;
ftsn_chunk = (struct sctp_fwdtsn_chunk *)retval->subh.fwdtsn_hdr;
ftsn_hdr.new_cum_tsn = htonl(new_cum_tsn);
retval->subh.fwdtsn_hdr =
sctp_addto_chunk(retval, sizeof(ftsn_hdr), &ftsn_hdr);
for (i = 0; i < nstreams; i++) {
skip.stream = skiplist[i].stream;
skip.ssn = skiplist[i].ssn;
sctp_addto_chunk(retval, sizeof(skip), &skip);
}
return retval;
}
...@@ -579,7 +579,7 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, ...@@ -579,7 +579,7 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
/* Helper function to process the process SACK command. */ /* Helper function to process the process SACK command. */
static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
struct sctp_association *asoc, struct sctp_association *asoc,
sctp_sackhdr_t *sackh) struct sctp_sackhdr *sackh)
{ {
int err; int err;
...@@ -729,6 +729,19 @@ static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds, ...@@ -729,6 +729,19 @@ static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds,
} }
} }
/* Process variable FWDTSN chunk information. */
static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq,
struct sctp_chunk *chunk)
{
struct sctp_fwdtsn_skip *skip;
/* Walk through all the skipped SSNs */
sctp_walk_fwdtsn(skip, chunk) {
sctp_ulpq_skip(ulpq, ntohs(skip->stream), ntohs(skip->ssn));
}
return;
}
/* These three macros allow us to pull the debugging code out of the /* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real * main flow of sctp_do_sm() to keep attention focused on the real
* functionality there. * functionality there.
...@@ -903,7 +916,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -903,7 +916,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
struct timer_list *timer; struct timer_list *timer;
unsigned long timeout; unsigned long timeout;
struct sctp_transport *t; struct sctp_transport *t;
sctp_sackhdr_t sackh; struct sctp_sackhdr sackh;
int local_cork = 0; int local_cork = 0;
if (SCTP_EVENT_T_TIMEOUT != event_type) if (SCTP_EVENT_T_TIMEOUT != event_type)
...@@ -962,6 +975,18 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -962,6 +975,18 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32); sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32);
break; break;
case SCTP_CMD_REPORT_FWDTSN:
/* Move the Cumulattive TSN Ack ahead. */
sctp_tsnmap_skip(&asoc->peer.tsn_map, cmd->obj.u32);
/* Abort any in progress partial delivery. */
sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
break;
case SCTP_CMD_PROCESS_FWDTSN:
sctp_cmd_process_fwdtsn(&asoc->ulpq, cmd->obj.ptr);
break;
case SCTP_CMD_GEN_SACK: case SCTP_CMD_GEN_SACK:
/* Generate a Selective ACK. /* Generate a Selective ACK.
* The argument tells us whether to just count * The argument tells us whether to just count
......
...@@ -3204,6 +3204,143 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, ...@@ -3204,6 +3204,143 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
return SCTP_DISPOSITION_DISCARD; return SCTP_DISPOSITION_DISCARD;
} }
/*
* PR-SCTP Section 3.6 Receiver Side Implementation of PR-SCTP
*
* When a FORWARD TSN chunk arrives, the data receiver MUST first update
* its cumulative TSN point to the value carried in the FORWARD TSN
* chunk, and then MUST further advance its cumulative TSN point locally
* if possible.
* After the above processing, the data receiver MUST stop reporting any
* missing TSNs earlier than or equal to the new cumulative TSN point.
*
* Verification Tag: 8.5 Verification Tag [Normal verification]
*
* The return value is the disposition of the chunk.
*/
sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *commands)
{
struct sctp_chunk *chunk = arg;
struct sctp_fwdtsn_hdr *fwdtsn_hdr;
__u16 len;
__u32 tsn;
/* RFC 2960 8.5 Verification Tag
*
* When receiving an SCTP packet, the endpoint MUST ensure
* that the value in the Verification Tag field of the
* received SCTP packet matches its own Tag.
*/
if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
SCTP_NULL());
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
}
fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data;
chunk->subh.fwdtsn_hdr = fwdtsn_hdr;
len = ntohs(chunk->chunk_hdr->length);
len -= sizeof(struct sctp_chunkhdr);
skb_pull(chunk->skb, len);
tsn = ntohl(fwdtsn_hdr->new_cum_tsn);
SCTP_DEBUG_PRINTK("%s: TSN 0x%x.\n", __FUNCTION__, tsn);
/* The TSN is too high--silently discard the chunk and count on it
* getting retransmitted later.
*/
if (sctp_tsnmap_check(&asoc->peer.tsn_map, tsn) < 0)
goto discard_noforce;
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn));
if (len > sizeof(struct sctp_fwdtsn_hdr))
sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_FWDTSN,
SCTP_CHUNK(chunk));
/* Count this as receiving DATA. */
if (asoc->autoclose) {
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
}
/* FIXME: For now send a SACK, but DATA processing may
* send another.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
/* Start the SACK timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
return SCTP_DISPOSITION_CONSUME;
discard_noforce:
return SCTP_DISPOSITION_DISCARD;
}
sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *commands)
{
struct sctp_chunk *chunk = arg;
struct sctp_fwdtsn_hdr *fwdtsn_hdr;
__u16 len;
__u32 tsn;
/* RFC 2960 8.5 Verification Tag
*
* When receiving an SCTP packet, the endpoint MUST ensure
* that the value in the Verification Tag field of the
* received SCTP packet matches its own Tag.
*/
if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
SCTP_NULL());
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
}
fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data;
chunk->subh.fwdtsn_hdr = fwdtsn_hdr;
len = ntohs(chunk->chunk_hdr->length);
len -= sizeof(struct sctp_chunkhdr);
skb_pull(chunk->skb, len);
tsn = ntohl(fwdtsn_hdr->new_cum_tsn);
SCTP_DEBUG_PRINTK("%s: TSN 0x%x.\n", __FUNCTION__, tsn);
/* The TSN is too high--silently discard the chunk and count on it
* getting retransmitted later.
*/
if (sctp_tsnmap_check(&asoc->peer.tsn_map, tsn) < 0)
goto gen_shutdown;
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn));
if (len > sizeof(struct sctp_fwdtsn_hdr))
sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_FWDTSN,
SCTP_CHUNK(chunk));
/* Go a head and force a SACK, since we are shutting down. */
gen_shutdown:
/* Implementor's Guide.
*
* While in SHUTDOWN-SENT state, the SHUTDOWN sender MUST immediately
* respond to each received packet containing one or more DATA chunk(s)
* with a SACK, a SHUTDOWN chunk, and restart the T2-shutdown timer
*/
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SHUTDOWN, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
return SCTP_DISPOSITION_CONSUME;
}
/* /*
* Process an unknown chunk. * Process an unknown chunk.
* *
...@@ -4671,28 +4808,20 @@ struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, ...@@ -4671,28 +4808,20 @@ struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc,
/* Make a transport for the bucket, Eliza... */ /* Make a transport for the bucket, Eliza... */
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC); transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
if (!transport) if (!transport)
goto nomem; goto nomem;
/* Allocate a new packet for sending the response. */
packet = t_new(struct sctp_packet, GFP_ATOMIC);
if (!packet)
goto nomem_packet;
/* Cache a route for the transport with the chunk's destination as /* Cache a route for the transport with the chunk's destination as
* the source address. * the source address.
*/ */
sctp_transport_route(transport, (union sctp_addr *)&chunk->dest, sctp_transport_route(transport, (union sctp_addr *)&chunk->dest,
sctp_sk(sctp_get_ctl_sock())); sctp_sk(sctp_get_ctl_sock()));
packet = sctp_packet_init(packet, transport, sport, dport); packet = sctp_packet_init(&transport->packet, transport, sport, dport);
packet = sctp_packet_config(packet, vtag, 0, NULL); packet = sctp_packet_config(packet, vtag, 0);
return packet; return packet;
nomem_packet:
sctp_transport_free(transport);
nomem: nomem:
return NULL; return NULL;
} }
...@@ -4701,7 +4830,6 @@ struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, ...@@ -4701,7 +4830,6 @@ struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc,
void sctp_ootb_pkt_free(struct sctp_packet *packet) void sctp_ootb_pkt_free(struct sctp_packet *packet)
{ {
sctp_transport_free(packet->transport); sctp_transport_free(packet->transport);
sctp_packet_free(packet);
} }
/* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */ /* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003 * (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
* Hui Huang <hui.huang@nokia.com> * Hui Huang <hui.huang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -486,6 +487,34 @@ const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][ ...@@ -486,6 +487,34 @@ const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][
TYPE_SCTP_ASCONF_ACK, TYPE_SCTP_ASCONF_ACK,
}; /*state_fn_t addip_chunk_event_table[][] */ }; /*state_fn_t addip_chunk_event_table[][] */
#define TYPE_SCTP_FWD_TSN { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_ootb, .name = "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_eat_fwd_tsn, .name = "sctp_sf_eat_fwd_tsn"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_eat_fwd_tsn, .name = "sctp_sf_eat_fwd_tsn"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_eat_fwd_tsn_fast, .name = "sctp_sf_eat_fwd_tsn_fast"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_FWD_TSN */
/* The primary index for this table is the chunk type.
* The secondary index for this table is the state.
*/
const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_FWD_TSN,
}; /*state_fn_t prsctp_chunk_event_table[][] */
static const sctp_sm_table_entry_t static const sctp_sm_table_entry_t
chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
/* SCTP_STATE_EMPTY */ /* SCTP_STATE_EMPTY */
...@@ -924,6 +953,11 @@ const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, ...@@ -924,6 +953,11 @@ const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) if (cid >= 0 && cid <= SCTP_CID_BASE_MAX)
return &chunk_event_table[cid][state]; return &chunk_event_table[cid][state];
if (sctp_prsctp_enable) {
if (cid == SCTP_CID_FWD_TSN)
return &prsctp_chunk_event_table[0][state];
}
if (sctp_addip_enable) { if (sctp_addip_enable) {
if (cid == SCTP_CID_ASCONF) if (cid == SCTP_CID_ASCONF)
return &addip_chunk_event_table[0][state]; return &addip_chunk_event_table[0][state];
......
...@@ -1948,6 +1948,9 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char *optval, int optlen) ...@@ -1948,6 +1948,9 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char *optval, int optlen)
*/ */
static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen) static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen)
{ {
struct sctp_association *asoc;
struct list_head *pos;
struct sctp_opt *sp = sctp_sk(sk);
int val; int val;
if (optlen < sizeof(int)) if (optlen < sizeof(int))
...@@ -1956,7 +1959,15 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen) ...@@ -1956,7 +1959,15 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen)
return -EFAULT; return -EFAULT;
if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)) if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN))
return -EINVAL; return -EINVAL;
sctp_sk(sk)->user_frag = val; sp->user_frag = val;
if (val) {
/* Update the frag_point of the existing associations. */
list_for_each(pos, &(sp->ep->asocs)) {
asoc = list_entry(pos, struct sctp_association, asocs);
asoc->frag_point = sctp_frag_point(sp, asoc->pmtu);
}
}
return 0; return 0;
} }
...@@ -2531,10 +2542,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -2531,10 +2542,6 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map); status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
status.sstat_instrms = asoc->c.sinit_max_instreams; status.sstat_instrms = asoc->c.sinit_max_instreams;
status.sstat_outstrms = asoc->c.sinit_num_ostreams; status.sstat_outstrms = asoc->c.sinit_num_ostreams;
/* Just in time frag_point update. */
if (sctp_sk(sk)->user_frag)
asoc->frag_point
= min_t(int, asoc->frag_point, sctp_sk(sk)->user_frag);
status.sstat_fragmentation_point = asoc->frag_point; status.sstat_fragmentation_point = asoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc); status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address, memcpy(&status.sstat_primary.spinfo_address,
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 2002 International Business Machines Corp. * (C) Copyright IBM Corp. 2002, 2004
* Copyright (c) 2002 Intel Corp. * Copyright (c) 2002 Intel Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.com>
* Ryan Layer <rmlayer@us.ibm.com> * Ryan Layer <rmlayer@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -170,6 +171,14 @@ static ctl_table sctp_table[] = { ...@@ -170,6 +171,14 @@ static ctl_table sctp_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec .proc_handler = &proc_dointvec
}, },
{
.ctl_name = NET_SCTP_PRSCTP_ENABLE,
.procname = "prsctp_enable",
.data = &sctp_prsctp_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{ .ctl_name = 0 } { .ctl_name = 0 }
}; };
......
...@@ -118,7 +118,6 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, ...@@ -118,7 +118,6 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
INIT_LIST_HEAD(&peer->transmitted); INIT_LIST_HEAD(&peer->transmitted);
INIT_LIST_HEAD(&peer->send_ready); INIT_LIST_HEAD(&peer->send_ready);
INIT_LIST_HEAD(&peer->transports); INIT_LIST_HEAD(&peer->transports);
sctp_packet_init(&peer->packet, peer, 0, 0);
/* Set up the retransmission timer. */ /* Set up the retransmission timer. */
init_timer(&peer->T3_rtx_timer); init_timer(&peer->T3_rtx_timer);
...@@ -169,6 +168,8 @@ void sctp_transport_destroy(struct sctp_transport *transport) ...@@ -169,6 +168,8 @@ void sctp_transport_destroy(struct sctp_transport *transport)
if (transport->asoc) if (transport->asoc)
sctp_association_put(transport->asoc); sctp_association_put(transport->asoc);
sctp_packet_free(&transport->packet);
dst_release(transport->dst); dst_release(transport->dst);
kfree(transport); kfree(transport);
SCTP_DBG_OBJCNT_DEC(transport); SCTP_DBG_OBJCNT_DEC(transport);
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* Karl Knutson <karl@athena.chicago.il.us> * Karl Knutson <karl@athena.chicago.il.us>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -253,6 +254,40 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map, ...@@ -253,6 +254,40 @@ int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
return ended; return ended;
} }
/* Mark this and any lower TSN as seen. */
void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn)
{
__s32 gap;
/* Vacuously mark any TSN which precedes the map base or
* exceeds the end of the map.
*/
if (TSN_lt(tsn, map->base_tsn))
return;
if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
return;
/* Bump the max. */
if (TSN_lt(map->max_tsn_seen, tsn))
map->max_tsn_seen = tsn;
/* Assert: TSN is in range. */
gap = tsn - map->base_tsn + 1;
/* Mark the TSNs as received. */
if (gap <= map->len)
memset(map->tsn_map, 0x01, gap);
else {
memset(map->tsn_map, 0x01, map->len);
memset(map->overflow_map, 0x01, (gap - map->len));
}
/* Go fixup any internal TSN mapping variables including
* cumulative_tsn_ack_point.
*/
sctp_tsnmap_update(map);
}
/******************************************************************** /********************************************************************
* 2nd Level Abstractions * 2nd Level Abstractions
********************************************************************/ ********************************************************************/
......
...@@ -839,6 +839,9 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event, ...@@ -839,6 +839,9 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
sctp_ulpevent_set_owner(event, asoc); sctp_ulpevent_set_owner(event, asoc);
sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb)); sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
if (!skb->data_len)
return;
/* Note: Not clearing the entire event struct as this is just a /* Note: Not clearing the entire event struct as this is just a
* fragment of the real event. However, we still need to do rwnd * fragment of the real event. However, we still need to do rwnd
* accounting. * accounting.
...@@ -867,6 +870,9 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) ...@@ -867,6 +870,9 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
skb = sctp_event2skb(event); skb = sctp_event2skb(event);
sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb)); sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb));
if (!skb->data_len)
goto done;
/* Don't forget the fragments. */ /* Don't forget the fragments. */
for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) { for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
/* NOTE: skb_shinfos are recursive. Although IP returns /* NOTE: skb_shinfos are recursive. Although IP returns
...@@ -875,6 +881,8 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) ...@@ -875,6 +881,8 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
*/ */
sctp_ulpevent_release_data(sctp_skb2event(frag)); sctp_ulpevent_release_data(sctp_skb2event(frag));
} }
done:
sctp_ulpevent_release_owner(event); sctp_ulpevent_release_owner(event);
} }
......
...@@ -680,6 +680,71 @@ static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq, ...@@ -680,6 +680,71 @@ static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
return event; return event;
} }
/* Helper function to gather skbs that have possibly become
* ordered by forward tsn skipping their dependencies.
*/
static inline void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq)
{
struct sk_buff *pos, *tmp;
struct sctp_ulpevent *cevent;
struct sctp_ulpevent *event = NULL;
struct sctp_stream *in;
struct sk_buff_head temp;
__u16 csid, cssn;
in = &ulpq->asoc->ssnmap->in;
/* We are holding the chunks by stream, by SSN. */
sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
cevent = (struct sctp_ulpevent *) pos->cb;
csid = cevent->stream;
cssn = cevent->ssn;
if (cssn != sctp_ssn_peek(in, csid))
break;
/* Found it, so mark in the ssnmap. */
sctp_ssn_next(in, csid);
__skb_unlink(pos, pos->list);
if (!event) {
/* Create a temporary list to collect chunks on. */
event = sctp_skb2event(pos);
skb_queue_head_init(&temp);
__skb_queue_tail(&temp, sctp_event2skb(event));
} else {
/* Attach all gathered skbs to the event. */
__skb_queue_tail(sctp_event2skb(event)->list, pos);
}
}
/* Send event to the ULP. */
if (event)
sctp_ulpq_tail_event(ulpq, event);
}
/* Skip over an SSN. */
void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
{
struct sctp_stream *in;
/* Note: The stream ID must be verified before this routine. */
in = &ulpq->asoc->ssnmap->in;
/* Is this an old SSN? If so ignore. */
if (SSN_lt(ssn, sctp_ssn_peek(in, sid)))
return;
/* Mark that we are no longer expecting this SSN or lower. */
sctp_ssn_skip(in, sid, ssn);
/* Go find any other chunks that were waiting for
* ordering and deliver them if needed.
*/
sctp_ulpq_reap_ordered(ulpq);
return;
}
/* Renege 'needed' bytes from the ordering queue. */ /* Renege 'needed' bytes from the ordering queue. */
static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed) static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment