Commit a0065b2f authored by Sridhar Samudrala's avatar Sridhar Samudrala

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

into us.ibm.com:/home/sridhar/BK/lksctp-2.5.60
parents f2478c00 9d6d6cb3
/* SCTP kernel reference Implementation Copyright (C) 1999-2001 /* SCTP kernel reference Implementation Copyright (C) 1999-2001
* Cisco, Motorola, and IBM * Cisco, Motorola, and IBM
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
* These are the definitions needed for the command object. * These are the definitions needed for the command object.
* *
* The SCTP reference implementation is free software; * The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by * the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option) * the Free Software Foundation; either version 2, or (at your option)
* any later version. * any later version.
* *
* the SCTP reference implementation is distributed in the hope that it * the SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied * will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************ * ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to * along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330, * the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
* *
* Please send any bug reports or fixes you make to one of the * Please send any bug reports or fixes you make to one of the
* following email addresses: * following email addresses:
* *
* 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>
* *
* 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.
*/ */
...@@ -69,11 +69,11 @@ typedef enum { ...@@ -69,11 +69,11 @@ typedef enum {
SCTP_CMD_INIT_FAILED, /* High level, do init failure work. */ SCTP_CMD_INIT_FAILED, /* High level, do init failure work. */
SCTP_CMD_REPORT_DUP, /* Report a duplicate TSN. */ SCTP_CMD_REPORT_DUP, /* Report a duplicate TSN. */
SCTP_CMD_REPORT_BIGGAP, /* Narc on a TSN (it was too high). */ SCTP_CMD_REPORT_BIGGAP, /* Narc on a TSN (it was too high). */
SCTP_CMD_SET_BIND_ADDR, /* Set the association bind_addr. */
SCTP_CMD_STRIKE, /* Mark a strike against a transport. */ SCTP_CMD_STRIKE, /* Mark a strike against a transport. */
SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */ SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */ SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
SCTP_CMD_HB_TIMERS_UPDATE, /* Update the heartbeat timers. */ SCTP_CMD_HB_TIMER_UPDATE, /* Update a heartbeat timers. */
SCTP_CMD_HB_TIMERS_STOP, /* Stop the heartbeat timers. */
SCTP_CMD_TRANSPORT_RESET, /* Reset the status of a transport. */ SCTP_CMD_TRANSPORT_RESET, /* Reset the status of a transport. */
SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */ SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */
SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */ SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */
...@@ -112,7 +112,7 @@ typedef union { ...@@ -112,7 +112,7 @@ typedef union {
void *ptr; void *ptr;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_bind_addr_t *bp; sctp_bind_addr_t *bp;
sctp_init_chunk_t *init; sctp_init_chunk_t *init;
sctp_ulpevent_t *ulpevent; sctp_ulpevent_t *ulpevent;
...@@ -160,7 +160,7 @@ SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) ...@@ -160,7 +160,7 @@ SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to)
SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr) SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr)
SCTP_ARG_CONSTRUCTOR(CHUNK, sctp_chunk_t *, chunk) SCTP_ARG_CONSTRUCTOR(CHUNK, sctp_chunk_t *, chunk)
SCTP_ARG_CONSTRUCTOR(ASOC, sctp_association_t *, asoc) SCTP_ARG_CONSTRUCTOR(ASOC, sctp_association_t *, asoc)
SCTP_ARG_CONSTRUCTOR(TRANSPORT, sctp_transport_t *, transport) SCTP_ARG_CONSTRUCTOR(TRANSPORT, struct sctp_transport *, transport)
SCTP_ARG_CONSTRUCTOR(BA, sctp_bind_addr_t *, bp) SCTP_ARG_CONSTRUCTOR(BA, sctp_bind_addr_t *, bp)
SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init) SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init)
SCTP_ARG_CONSTRUCTOR(ULPEVENT, sctp_ulpevent_t *, ulpevent) SCTP_ARG_CONSTRUCTOR(ULPEVENT, sctp_ulpevent_t *, ulpevent)
......
...@@ -3,33 +3,33 @@ ...@@ -3,33 +3,33 @@
* 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-2002 International Business Machines Corp. * Copyright (c) 2001-2002 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
* This file is part of the implementation of the add-IP extension, * This file is part of the implementation of the add-IP extension,
* based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001, * based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
* for the SCTP kernel reference Implementation. * for the SCTP kernel reference Implementation.
* *
* The SCTP reference implementation is free software; * The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by * the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option) * the Free Software Foundation; either version 2, or (at your option)
* any later version. * any later version.
* *
* the SCTP reference implementation is distributed in the hope that it * the SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* ************************ * ************************
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to * along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330, * the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
* *
* Please send any bug reports or fixes you make to one of the following email * Please send any bug reports or fixes you make to one of the following email
* addresses: * addresses:
* *
* 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>
* Randall Stewart <randall@stewart.chicago.il.us> * Randall Stewart <randall@stewart.chicago.il.us>
...@@ -38,14 +38,14 @@ ...@@ -38,14 +38,14 @@
* Xingang Guo <xingang.guo@intel.com> * Xingang Guo <xingang.guo@intel.com>
* Sridhar Samudrala <samudrala@us.ibm.com> * Sridhar Samudrala <samudrala@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@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.
* *
* There are still LOTS of bugs in this code... I always run on the motto * There are still LOTS of bugs in this code... I always run on the motto
* "it is a wonder any code ever works :)" * "it is a wonder any code ever works :)"
* *
* *
*/ */
#ifndef __sctp_constants_h__ #ifndef __sctp_constants_h__
...@@ -56,17 +56,10 @@ ...@@ -56,17 +56,10 @@
#include <linux/ipv6.h> /* For ipv6hdr. */ #include <linux/ipv6.h> /* For ipv6hdr. */
#include <net/sctp/user.h> #include <net/sctp/user.h>
/* What a hack! Jiminy Cricket! */ /* Value used for stream negotiation. */
enum { SCTP_MAX_STREAM = 10 }; enum { SCTP_MAX_STREAM = 0xffff };
enum { SCTP_DEFAULT_OUTSTREAMS = 10 };
/* Define the amount of space to reserve for SCTP, IP, LL. enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
* 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))
/* Define the amount of space to reserve for SCTP, IP, LL. /* Define the amount of space to reserve for SCTP, IP, LL.
* There is a little bit of waste that we are always allocating * There is a little bit of waste that we are always allocating
...@@ -105,57 +98,37 @@ typedef enum { ...@@ -105,57 +98,37 @@ typedef enum {
*/ */
typedef enum { typedef enum {
SCTP_EVENT_TIMEOUT_NONE = 0, SCTP_EVENT_TIMEOUT_NONE = 0,
SCTP_EVENT_TIMEOUT_T1_COOKIE, SCTP_EVENT_TIMEOUT_T1_COOKIE,
SCTP_EVENT_TIMEOUT_T1_INIT, SCTP_EVENT_TIMEOUT_T1_INIT,
SCTP_EVENT_TIMEOUT_T2_SHUTDOWN, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN,
SCTP_EVENT_TIMEOUT_T3_RTX, SCTP_EVENT_TIMEOUT_T3_RTX,
SCTP_EVENT_TIMEOUT_T4_RTO,
SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD, SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
SCTP_EVENT_TIMEOUT_HEARTBEAT, SCTP_EVENT_TIMEOUT_HEARTBEAT,
SCTP_EVENT_TIMEOUT_SACK, SCTP_EVENT_TIMEOUT_SACK,
SCTP_EVENT_TIMEOUT_AUTOCLOSE, SCTP_EVENT_TIMEOUT_AUTOCLOSE,
SCTP_EVENT_TIMEOUT_PMTU_RAISE,
} sctp_event_timeout_t; } sctp_event_timeout_t;
#define SCTP_EVENT_TIMEOUT_MAX SCTP_EVENT_TIMEOUT_PMTU_RAISE #define SCTP_EVENT_TIMEOUT_MAX SCTP_EVENT_TIMEOUT_AUTOCLOSE
#define SCTP_NUM_TIMEOUT_TYPES (SCTP_EVENT_TIMEOUT_MAX + 1) #define SCTP_NUM_TIMEOUT_TYPES (SCTP_EVENT_TIMEOUT_MAX + 1)
typedef enum { typedef enum {
SCTP_EVENT_NO_PENDING_TSN = 0, SCTP_EVENT_NO_PENDING_TSN = 0,
SCTP_EVENT_ICMP_UNREACHFRAG,
} sctp_event_other_t; } sctp_event_other_t;
#define SCTP_EVENT_OTHER_MAX SCTP_EVENT_ICMP_UNREACHFRAG #define SCTP_EVENT_OTHER_MAX SCTP_EVENT_NO_PENDING_TSN
#define SCTP_NUM_OTHER_TYPES (SCTP_EVENT_OTHER_MAX + 1) #define SCTP_NUM_OTHER_TYPES (SCTP_EVENT_OTHER_MAX + 1)
/* These are primitive requests from the ULP. */ /* These are primitive requests from the ULP. */
typedef enum { typedef enum {
SCTP_PRIMITIVE_ASSOCIATE = 0,
SCTP_PRIMITIVE_INITIALIZE = 0,
SCTP_PRIMITIVE_ASSOCIATE,
SCTP_PRIMITIVE_SHUTDOWN, SCTP_PRIMITIVE_SHUTDOWN,
SCTP_PRIMITIVE_ABORT, SCTP_PRIMITIVE_ABORT,
SCTP_PRIMITIVE_SEND, SCTP_PRIMITIVE_SEND,
SCTP_PRIMITIVE_SETPRIMARY,
SCTP_PRIMITIVE_RECEIVE,
SCTP_PRIMITIVE_STATUS,
SCTP_PRIMITIVE_CHANGEHEARTBEAT,
SCTP_PRIMITIVE_REQUESTHEARTBEAT, SCTP_PRIMITIVE_REQUESTHEARTBEAT,
SCTP_PRIMITIVE_GETSRTTREPORT,
SCTP_PRIMITIVE_SETFAILURETHRESHOLD,
SCTP_PRIMITIVE_SETPROTOPARAMETERS,
SCTP_PRIMITIVE_RECEIVE_UNSENT,
SCTP_PRIMITIVE_RECEIVE_UNACKED,
SCTP_PRIMITIVE_DESTROY,
} sctp_event_primitive_t; } sctp_event_primitive_t;
#define SCTP_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_DESTROY #define SCTP_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_REQUESTHEARTBEAT
#define SCTP_NUM_PRIMITIVE_TYPES (SCTP_EVENT_PRIMITIVE_MAX + 1) #define SCTP_NUM_PRIMITIVE_TYPES (SCTP_EVENT_PRIMITIVE_MAX + 1)
/* We define here a utility type for manipulating subtypes. /* We define here a utility type for manipulating subtypes.
...@@ -268,8 +241,13 @@ extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[]; ...@@ -268,8 +241,13 @@ extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[];
#define SCTP_ADDR_REACHABLE 2 #define SCTP_ADDR_REACHABLE 2
#define SCTP_ADDR_NOT_REACHABLE 1 #define SCTP_ADDR_NOT_REACHABLE 1
/* Maximum chunk length considering padding requirements. */
enum { SCTP_MAX_CHUNK_LEN = ((1<<16) - sizeof(__u32)) };
/* Encourage Cookie-Echo bundling by pre-fragmenting chunks a little
* harder (until reaching ESTABLISHED state).
*/
enum { SCTP_ARBITRARY_COOKIE_ECHO_LEN = 200 };
/* Guess at how big to make the TSN mapping array. /* Guess at how big to make the TSN mapping array.
* We guarantee that we can handle at least this big a gap between the * We guarantee that we can handle at least this big a gap between the
...@@ -289,7 +267,8 @@ extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[]; ...@@ -289,7 +267,8 @@ extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[];
* is enough room for 131 duplicate reports. Round down to the * is enough room for 131 duplicate reports. Round down to the
* nearest power of 2. * nearest power of 2.
*/ */
#define SCTP_MAX_DUP_TSNS 128 enum { SCTP_MIN_PMTU = 576 };
enum { SCTP_MAX_DUP_TSNS = 128 };
typedef enum { typedef enum {
SCTP_COUNTER_INIT_ERROR, SCTP_COUNTER_INIT_ERROR,
...@@ -298,7 +277,6 @@ typedef enum { ...@@ -298,7 +277,6 @@ typedef enum {
/* How many counters does an association need? */ /* How many counters does an association need? */
#define SCTP_NUMBER_COUNTERS 5 #define SCTP_NUMBER_COUNTERS 5
/* Here we define the default timers. */ /* Here we define the default timers. */
/* cookie timer def = ? seconds */ /* cookie timer def = ? seconds */
...@@ -317,10 +295,6 @@ typedef enum { ...@@ -317,10 +295,6 @@ typedef enum {
#define SCTP_DEFAULT_TIMEOUT_SACK ((200 * HZ) / 1000) #define SCTP_DEFAULT_TIMEOUT_SACK ((200 * HZ) / 1000)
#define SCTP_DEFAULT_TIMEOUT_SACK_MAX ((500 * HZ) / 1000) /* 500 ms */ #define SCTP_DEFAULT_TIMEOUT_SACK_MAX ((500 * HZ) / 1000) /* 500 ms */
/* How long do we wait before attempting to raise the PMTU? */
#define SCTP_DEFAULT_TIMEOUT_PMTU_RAISE (10 * 60 * HZ) /* 10 Minutes */
#define SCTP_DEFAULT_TIMEOUT_PMTU_RAISE_MIN (10 * 60 * HZ) /* 10 Minutes */
/* RTO.Initial - 3 seconds /* RTO.Initial - 3 seconds
* RTO.Min - 1 second * RTO.Min - 1 second
* RTO.Max - 60 seconds * RTO.Max - 60 seconds
...@@ -439,6 +413,13 @@ typedef enum { ...@@ -439,6 +413,13 @@ typedef enum {
#define SCTP_ADDR6_PEERSUPP 0x00000004 /* IPv6 address is supported by #define SCTP_ADDR6_PEERSUPP 0x00000004 /* IPv6 address is supported by
peer */ peer */
/* Reasons to retransmit. */
typedef enum {
SCTP_RETRANSMIT_T3_RTX,
SCTP_RETRANSMIT_FAST_RTX,
SCTP_RETRANSMIT_PMTU_DISCOVERY,
} sctp_retransmit_reason_t;
/* Reasons to lower cwnd. */ /* Reasons to lower cwnd. */
typedef enum { typedef enum {
SCTP_LOWER_CWND_T3_RTX, SCTP_LOWER_CWND_T3_RTX,
...@@ -448,4 +429,3 @@ typedef enum { ...@@ -448,4 +429,3 @@ typedef enum {
} sctp_lower_cwnd_t; } sctp_lower_cwnd_t;
#endif /* __sctp_constants_h__ */ #endif /* __sctp_constants_h__ */
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines, Corp. * Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001-2003 Intel Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -36,7 +36,9 @@ ...@@ -36,7 +36,9 @@
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* 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>
* Ardelle Fan <ardelle.fan@intel.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.
...@@ -147,7 +149,9 @@ extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg); ...@@ -147,7 +149,9 @@ extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg);
/* /*
* sctp_crc32c.c * sctp_crc32c.c
*/ */
extern __u32 count_crc(__u8 *ptr, __u16 count); extern __u32 sctp_start_cksum(__u8 *ptr, __u16 count);
extern __u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 cksum);
extern __u32 sctp_end_cksum(__u32 cksum);
/* /*
* sctp_input.c * sctp_input.c
...@@ -162,6 +166,9 @@ extern void sctp_hash_endpoint(sctp_endpoint_t *); ...@@ -162,6 +166,9 @@ extern void sctp_hash_endpoint(sctp_endpoint_t *);
extern void __sctp_hash_endpoint(sctp_endpoint_t *); extern void __sctp_hash_endpoint(sctp_endpoint_t *);
extern void sctp_unhash_endpoint(sctp_endpoint_t *); extern void sctp_unhash_endpoint(sctp_endpoint_t *);
extern void __sctp_unhash_endpoint(sctp_endpoint_t *); extern void __sctp_unhash_endpoint(sctp_endpoint_t *);
extern sctp_association_t *__sctp_lookup_association(const union sctp_addr *,
const union sctp_addr *,
struct sctp_transport **);
/* /*
* sctp_hashdriver.c * sctp_hashdriver.c
...@@ -266,6 +273,7 @@ extern atomic_t sctp_dbg_objcnt_transport; ...@@ -266,6 +273,7 @@ extern atomic_t sctp_dbg_objcnt_transport;
extern atomic_t sctp_dbg_objcnt_chunk; extern atomic_t sctp_dbg_objcnt_chunk;
extern atomic_t sctp_dbg_objcnt_bind_addr; extern atomic_t sctp_dbg_objcnt_bind_addr;
extern atomic_t sctp_dbg_objcnt_addr; extern atomic_t sctp_dbg_objcnt_addr;
extern atomic_t sctp_dbg_objcnt_ssnmap;
/* Macros to atomically increment/decrement objcnt counters. */ /* Macros to atomically increment/decrement objcnt counters. */
#define SCTP_DBG_OBJCNT_INC(name) \ #define SCTP_DBG_OBJCNT_INC(name) \
...@@ -418,6 +426,23 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen) ...@@ -418,6 +426,23 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
return retval; return retval;
} }
/* Generate a random jitter in the range of -50% ~ +50% of input RTO. */
static inline __s32 sctp_jitter(__u32 rto)
{
static __u32 sctp_rand;
__s32 ret;
sctp_rand += jiffies;
sctp_rand ^= (sctp_rand << 12);
sctp_rand ^= (sctp_rand >> 20);
/* Choose random number from 0 to rto, then move to -50% ~ +50%
* of rto.
*/
ret = sctp_rand % rto - (rto >> 1);
return ret;
}
/* Walk through a list of TLV parameters. Don't trust the /* Walk through a list of TLV parameters. Don't trust the
* individual parameter lengths and instead depend on * individual parameter lengths and instead depend on
* the chunk length to indicate when to stop. Make sure * the chunk length to indicate when to stop. Make sure
......
...@@ -256,7 +256,7 @@ sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *, ...@@ -256,7 +256,7 @@ sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *,
const sctp_chunk_t *, const sctp_chunk_t *,
const struct msghdr *); const struct msghdr *);
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *, sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *,
const sctp_transport_t *, const struct sctp_transport *,
const void *payload, const void *payload,
const size_t paylen); const size_t paylen);
sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *, sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *,
...@@ -269,6 +269,11 @@ sctp_chunk_t *sctp_make_op_error(const sctp_association_t *, ...@@ -269,6 +269,11 @@ sctp_chunk_t *sctp_make_op_error(const sctp_association_t *,
const void *payload, const void *payload,
size_t paylen); size_t paylen);
void sctp_chunk_assign_tsn(sctp_chunk_t *); void sctp_chunk_assign_tsn(sctp_chunk_t *);
void sctp_chunk_assign_ssn(sctp_chunk_t *);
int sctp_datachunks_from_user(sctp_association_t *,
const struct sctp_sndrcvinfo *,
struct msghdr *, int len,
struct sk_buff_head *);
/* Prototypes for statetable processing. */ /* Prototypes for statetable processing. */
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Dajiang Zhang <dajiang.zhang@nokia.com> * Dajiang Zhang <dajiang.zhang@nokia.com>
* Ardelle Fan <ardelle.fan@intel.com> * Ardelle Fan <ardelle.fan@intel.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.
...@@ -104,27 +104,26 @@ union sctp_addr { ...@@ -104,27 +104,26 @@ union sctp_addr {
/* Forward declarations for data structures. */ /* Forward declarations for data structures. */
struct SCTP_protocol; struct sctp_protocol;
struct SCTP_endpoint; struct SCTP_endpoint;
struct SCTP_association; struct SCTP_association;
struct SCTP_transport; struct sctp_transport;
struct SCTP_packet; struct SCTP_packet;
struct SCTP_chunk; struct SCTP_chunk;
struct SCTP_inqueue; struct SCTP_inqueue;
struct SCTP_outqueue; struct sctp_outq;
struct SCTP_bind_addr; struct SCTP_bind_addr;
struct sctp_ulpq;
struct sctp_opt; struct sctp_opt;
struct sctp_endpoint_common; struct sctp_endpoint_common;
struct sctp_ssnmap;
typedef struct sctp_protocol sctp_protocol_t;
typedef struct SCTP_protocol sctp_protocol_t;
typedef struct SCTP_endpoint sctp_endpoint_t; typedef struct SCTP_endpoint sctp_endpoint_t;
typedef struct SCTP_association sctp_association_t; typedef struct SCTP_association sctp_association_t;
typedef struct SCTP_transport sctp_transport_t;
typedef struct SCTP_packet sctp_packet_t; typedef struct SCTP_packet sctp_packet_t;
typedef struct SCTP_chunk sctp_chunk_t; typedef struct SCTP_chunk sctp_chunk_t;
typedef struct SCTP_inqueue sctp_inqueue_t; typedef struct SCTP_inqueue sctp_inqueue_t;
typedef struct SCTP_outqueue sctp_outqueue_t;
typedef struct SCTP_bind_addr sctp_bind_addr_t; typedef struct SCTP_bind_addr sctp_bind_addr_t;
typedef struct sctp_opt sctp_opt_t; typedef struct sctp_opt sctp_opt_t;
typedef struct sctp_endpoint_common sctp_endpoint_common_t; typedef struct sctp_endpoint_common sctp_endpoint_common_t;
...@@ -133,7 +132,6 @@ typedef struct sctp_endpoint_common sctp_endpoint_common_t; ...@@ -133,7 +132,6 @@ typedef struct sctp_endpoint_common sctp_endpoint_common_t;
#include <net/sctp/ulpevent.h> #include <net/sctp/ulpevent.h>
#include <net/sctp/ulpqueue.h> #include <net/sctp/ulpqueue.h>
/* Structures useful for managing bind/connect. */ /* Structures useful for managing bind/connect. */
typedef struct sctp_bind_bucket { typedef struct sctp_bind_bucket {
...@@ -157,7 +155,7 @@ typedef struct sctp_hashbucket { ...@@ -157,7 +155,7 @@ typedef struct sctp_hashbucket {
/* The SCTP protocol structure. */ /* The SCTP protocol structure. */
struct SCTP_protocol { struct sctp_protocol {
/* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values /* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
* *
* The following protocol parameters are RECOMMENDED: * The following protocol parameters are RECOMMENDED:
...@@ -183,8 +181,8 @@ struct SCTP_protocol { ...@@ -183,8 +181,8 @@ struct SCTP_protocol {
/* Valid.Cookie.Life - 60 seconds */ /* Valid.Cookie.Life - 60 seconds */
int valid_cookie_life; int valid_cookie_life;
/* Whether Cookie Preservative is enabled(1) or not(0) */ /* Whether Cookie Preservative is enabled(1) or not(0) */
int cookie_preserve_enable; int cookie_preserve_enable;
/* Association.Max.Retrans - 10 attempts /* Association.Max.Retrans - 10 attempts
...@@ -239,7 +237,9 @@ struct SCTP_protocol { ...@@ -239,7 +237,9 @@ struct SCTP_protocol {
* (i.e. things that depend on the address family.) * (i.e. things that depend on the address family.)
*/ */
struct sctp_af { struct sctp_af {
int (*queue_xmit) (struct sk_buff *skb); int (*sctp_xmit) (struct sk_buff *skb,
struct sctp_transport *,
int ipfragok);
int (*setsockopt) (struct sock *sk, int (*setsockopt) (struct sock *sk,
int level, int level,
int optname, int optname,
...@@ -250,12 +250,18 @@ struct sctp_af { ...@@ -250,12 +250,18 @@ struct sctp_af {
int optname, int optname,
char *optval, char *optval,
int *optlen); int *optlen);
struct dst_entry *(*get_dst) (union sctp_addr *daddr, struct dst_entry *(*get_dst) (sctp_association_t *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr); union sctp_addr *saddr);
void (*get_saddr) (sctp_association_t *asoc,
struct dst_entry *dst,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*copy_addrlist) (struct list_head *, void (*copy_addrlist) (struct list_head *,
struct net_device *); struct net_device *);
void (*dst_saddr) (union sctp_addr *saddr, void (*dst_saddr) (union sctp_addr *saddr,
struct dst_entry *dst); struct dst_entry *dst,
unsigned short port);
int (*cmp_addr) (const union sctp_addr *addr1, int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2); const union sctp_addr *addr2);
void (*addr_copy) (union sctp_addr *dst, void (*addr_copy) (union sctp_addr *dst,
...@@ -282,7 +288,7 @@ struct sctp_af *sctp_get_af_specific(sa_family_t); ...@@ -282,7 +288,7 @@ struct sctp_af *sctp_get_af_specific(sa_family_t);
int sctp_register_af(struct sctp_af *); int sctp_register_af(struct sctp_af *);
/* Protocol family functions. */ /* Protocol family functions. */
typedef struct sctp_pf { struct sctp_pf {
void (*event_msgname)(sctp_ulpevent_t *, char *, int *); void (*event_msgname)(sctp_ulpevent_t *, char *, int *);
void (*skb_msgname) (struct sk_buff *, char *, int *); void (*skb_msgname) (struct sk_buff *, char *, int *);
int (*af_supported) (sa_family_t); int (*af_supported) (sa_family_t);
...@@ -291,7 +297,7 @@ typedef struct sctp_pf { ...@@ -291,7 +297,7 @@ typedef struct sctp_pf {
struct sctp_opt *); struct sctp_opt *);
int (*bind_verify) (struct sctp_opt *, union sctp_addr *); int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
struct sctp_af *af; struct sctp_af *af;
} sctp_pf_t; };
/* SCTP Socket type: UDP or TCP style. */ /* SCTP Socket type: UDP or TCP style. */
typedef enum { typedef enum {
...@@ -318,7 +324,7 @@ struct sctp_opt { ...@@ -318,7 +324,7 @@ struct sctp_opt {
__u32 autoclose; __u32 autoclose;
__u8 nodelay; __u8 nodelay;
__u8 disable_fragments; __u8 disable_fragments;
sctp_pf_t *pf; struct sctp_pf *pf;
}; };
...@@ -360,7 +366,8 @@ typedef struct sctp_cookie { ...@@ -360,7 +366,8 @@ typedef struct sctp_cookie {
struct timeval expiration; struct timeval expiration;
/* Number of inbound/outbound streams which are set /* Number of inbound/outbound streams which are set
* and negotiated during the INIT process. */ * and negotiated during the INIT process.
*/
__u16 sinit_num_ostreams; __u16 sinit_num_ostreams;
__u16 sinit_max_instreams; __u16 sinit_max_instreams;
...@@ -426,6 +433,49 @@ typedef struct sctp_sender_hb_info { ...@@ -426,6 +433,49 @@ typedef struct sctp_sender_hb_info {
unsigned long sent_at; unsigned long sent_at;
} sctp_sender_hb_info_t __attribute__((packed)); } sctp_sender_hb_info_t __attribute__((packed));
/*
* RFC 2960 1.3.2 Sequenced Delivery within Streams
*
* The term "stream" is used in SCTP to refer to a sequence of user
* messages that are to be delivered to the upper-layer protocol in
* order with respect to other messages within the same stream. This is
* in contrast to its usage in TCP, where it refers to a sequence of
* bytes (in this document a byte is assumed to be eight bits).
* ...
*
* This is the structure we use to track both our outbound and inbound
* SSN, or Stream Sequence Numbers.
*/
struct sctp_stream {
__u16 *ssn;
unsigned int len;
};
struct sctp_ssnmap {
struct sctp_stream in;
struct sctp_stream out;
int malloced;
};
struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *, __u16, __u16);
struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int priority);
void sctp_ssnmap_free(struct sctp_ssnmap *map);
void sctp_ssnmap_clear(struct sctp_ssnmap *map);
/* What is the current SSN number for this stream? */
static inline __u16 sctp_ssn_peek(struct sctp_stream *stream, __u16 id)
{
return stream->ssn[id];
}
/* Return the next SSN number for this stream. */
static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
{
return stream->ssn[id]++;
}
/* RFC2960 1.4 Key Terms /* RFC2960 1.4 Key Terms
* *
* o Chunk: A unit of information within an SCTP packet, consisting of * o Chunk: A unit of information within an SCTP packet, consisting of
...@@ -499,6 +549,7 @@ struct SCTP_chunk { ...@@ -499,6 +549,7 @@ struct SCTP_chunk {
__u8 rtt_in_progress; /* Is this chunk used for RTT calculation? */ __u8 rtt_in_progress; /* Is this chunk used for RTT calculation? */
__u8 num_times_sent; /* How man times did we send this? */ __u8 num_times_sent; /* How man times did we send this? */
__u8 has_tsn; /* Does this chunk have a TSN yet? */ __u8 has_tsn; /* Does this chunk have a TSN yet? */
__u8 has_ssn; /* Does this chunk have a SSN yet? */
__u8 singleton; /* Was this the only chunk in the packet? */ __u8 singleton; /* Was this the only chunk in the packet? */
__u8 end_of_packet; /* Was this the last chunk in the packet? */ __u8 end_of_packet; /* Was this the last chunk in the packet? */
__u8 ecn_ce_done; /* Have we processed the ECN CE bit? */ __u8 ecn_ce_done; /* Have we processed the ECN CE bit? */
...@@ -516,15 +567,13 @@ struct SCTP_chunk { ...@@ -516,15 +567,13 @@ struct SCTP_chunk {
* For an outbound chunk, it tells us where we'd like it to * For an outbound chunk, it tells us where we'd like it to
* go. It is NULL if we have no preference. * go. It is NULL if we have no preference.
*/ */
sctp_transport_t *transport; struct sctp_transport *transport;
}; };
sctp_chunk_t *sctp_make_chunk(const sctp_association_t *, __u8 type, sctp_chunk_t *sctp_make_chunk(const sctp_association_t *, __u8 type,
__u8 flags, int size); __u8 flags, int size);
void sctp_free_chunk(sctp_chunk_t *); void sctp_free_chunk(sctp_chunk_t *);
sctp_chunk_t *sctp_copy_chunk(sctp_chunk_t *, int flags);
void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data); void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data);
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data);
sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *, sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *,
struct sock *); struct sock *);
void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *); void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *);
...@@ -560,7 +609,7 @@ struct SCTP_packet { ...@@ -560,7 +609,7 @@ struct SCTP_packet {
* The function we finally use to pass down to the next lower * The function we finally use to pass down to the next lower
* layer lives in the transport structure. * layer lives in the transport structure.
*/ */
sctp_transport_t *transport; struct sctp_transport *transport;
/* Allow a callback for getting a high priority chunk /* Allow a callback for getting a high priority chunk
* bundled early into the packet (This is used for ECNE). * bundled early into the packet (This is used for ECNE).
...@@ -575,30 +624,33 @@ struct SCTP_packet { ...@@ -575,30 +624,33 @@ struct SCTP_packet {
/* This packet contains a COOKIE-ECHO chunk. */ /* This packet contains a COOKIE-ECHO chunk. */
int has_cookie_echo; int has_cookie_echo;
/* SCTP cannot fragment this packet. So let ip fragment it. */
int ipfragok;
int malloced; int malloced;
}; };
typedef int (sctp_outqueue_thandler_t)(sctp_outqueue_t *, void *); typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *);
typedef int (sctp_outqueue_ehandler_t)(sctp_outqueue_t *); typedef int (sctp_outq_ehandler_t)(struct sctp_outq *);
typedef sctp_packet_t *(sctp_outqueue_ohandler_init_t) typedef sctp_packet_t *(sctp_outq_ohandler_init_t)
(sctp_packet_t *, (sctp_packet_t *,
sctp_transport_t *, struct sctp_transport *,
__u16 sport, __u16 sport,
__u16 dport); __u16 dport);
typedef sctp_packet_t *(sctp_outqueue_ohandler_config_t) typedef sctp_packet_t *(sctp_outq_ohandler_config_t)
(sctp_packet_t *, (sctp_packet_t *,
__u32 vtag, __u32 vtag,
int ecn_capable, int ecn_capable,
sctp_packet_phandler_t *get_prepend_chunk); sctp_packet_phandler_t *get_prepend_chunk);
typedef sctp_xmit_t (sctp_outqueue_ohandler_t)(sctp_packet_t *, typedef sctp_xmit_t (sctp_outq_ohandler_t)(sctp_packet_t *,
sctp_chunk_t *); sctp_chunk_t *);
typedef int (sctp_outqueue_ohandler_force_t)(sctp_packet_t *); typedef int (sctp_outq_ohandler_force_t)(sctp_packet_t *);
sctp_outqueue_ohandler_init_t sctp_packet_init; sctp_outq_ohandler_init_t sctp_packet_init;
sctp_outqueue_ohandler_config_t sctp_packet_config; sctp_outq_ohandler_config_t sctp_packet_config;
sctp_outqueue_ohandler_t sctp_packet_append_chunk; sctp_outq_ohandler_t sctp_packet_append_chunk;
sctp_outqueue_ohandler_t sctp_packet_transmit_chunk; sctp_outq_ohandler_t sctp_packet_transmit_chunk;
sctp_outqueue_ohandler_force_t sctp_packet_transmit; sctp_outq_ohandler_force_t sctp_packet_transmit;
void sctp_packet_free(sctp_packet_t *); void sctp_packet_free(sctp_packet_t *);
...@@ -622,7 +674,7 @@ void sctp_packet_free(sctp_packet_t *); ...@@ -622,7 +674,7 @@ void sctp_packet_free(sctp_packet_t *);
* period. * period.
* *
*/ */
struct SCTP_transport { struct sctp_transport {
/* A list of transports. */ /* A list of transports. */
struct list_head transports; struct list_head transports;
...@@ -692,6 +744,8 @@ struct SCTP_transport { ...@@ -692,6 +744,8 @@ struct SCTP_transport {
/* Destination */ /* Destination */
struct dst_entry *dst; struct dst_entry *dst;
/* Source address. */
union sctp_addr saddr;
/* When was the last time(in jiffies) that a data packet was sent on /* When was the last time(in jiffies) that a data packet was sent on
* this transport? This is used to adjust the cwnd when the transport * this transport? This is used to adjust the cwnd when the transport
...@@ -771,24 +825,26 @@ struct SCTP_transport { ...@@ -771,24 +825,26 @@ struct SCTP_transport {
int malloced; /* Is this structure kfree()able? */ int malloced; /* Is this structure kfree()able? */
}; };
extern sctp_transport_t *sctp_transport_new(const union sctp_addr *, int); struct sctp_transport *sctp_transport_new(const union sctp_addr *, int);
extern sctp_transport_t *sctp_transport_init(sctp_transport_t *, struct sctp_transport *sctp_transport_init(struct sctp_transport *,
const union sctp_addr *, int); const union sctp_addr *, int);
extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *); void sctp_transport_set_owner(struct sctp_transport *, sctp_association_t *);
extern void sctp_transport_route(sctp_transport_t *, union sctp_addr *, void sctp_transport_route(struct sctp_transport *, union sctp_addr *,
struct sctp_opt *); struct sctp_opt *);
extern void sctp_transport_free(sctp_transport_t *); void sctp_transport_pmtu(struct sctp_transport *);
extern void sctp_transport_destroy(sctp_transport_t *); void sctp_transport_free(struct sctp_transport *);
extern void sctp_transport_reset_timers(sctp_transport_t *); void sctp_transport_destroy(struct sctp_transport *);
extern void sctp_transport_hold(sctp_transport_t *); void sctp_transport_reset_timers(struct sctp_transport *);
extern void sctp_transport_put(sctp_transport_t *); void sctp_transport_hold(struct sctp_transport *);
extern void sctp_transport_update_rto(sctp_transport_t *, __u32); void sctp_transport_put(struct sctp_transport *);
extern void sctp_transport_raise_cwnd(sctp_transport_t *, __u32, __u32); void sctp_transport_update_rto(struct sctp_transport *, __u32);
extern void sctp_transport_lower_cwnd(sctp_transport_t *, sctp_lower_cwnd_t); void sctp_transport_raise_cwnd(struct sctp_transport *, __u32, __u32);
void sctp_transport_lower_cwnd(struct sctp_transport *, sctp_lower_cwnd_t);
unsigned long sctp_transport_timeout(struct sctp_transport *);
/* This is the structure we use to queue packets as they come into /* This is the structure we use to queue packets as they come into
* SCTP. We write packets to it and read chunks from it. It handles * SCTP. We write packets to it and read chunks from it.
* fragment reassembly and chunk unbundling.
*/ */
struct SCTP_inqueue { struct SCTP_inqueue {
/* This is actually a queue of sctp_chunk_t each /* This is actually a queue of sctp_chunk_t each
...@@ -835,7 +891,7 @@ void sctp_inqueue_set_th_handler(sctp_inqueue_t *, ...@@ -835,7 +891,7 @@ void sctp_inqueue_set_th_handler(sctp_inqueue_t *,
* *
* When free()'d, it empties itself out via output_handler(). * When free()'d, it empties itself out via output_handler().
*/ */
struct SCTP_outqueue { struct sctp_outq {
sctp_association_t *asoc; sctp_association_t *asoc;
/* BUG: This really should be an array of streams. /* BUG: This really should be an array of streams.
...@@ -861,11 +917,11 @@ struct SCTP_outqueue { ...@@ -861,11 +917,11 @@ struct SCTP_outqueue {
* layer. This is always SCTP_packet, but we separate the two * layer. This is always SCTP_packet, but we separate the two
* structures to make testing simpler. * structures to make testing simpler.
*/ */
sctp_outqueue_ohandler_init_t *init_output; sctp_outq_ohandler_init_t *init_output;
sctp_outqueue_ohandler_config_t *config_output; sctp_outq_ohandler_config_t *config_output;
sctp_outqueue_ohandler_t *append_output; sctp_outq_ohandler_t *append_output;
sctp_outqueue_ohandler_t *build_output; sctp_outq_ohandler_t *build_output;
sctp_outqueue_ohandler_force_t *force_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;
...@@ -877,24 +933,24 @@ struct SCTP_outqueue { ...@@ -877,24 +933,24 @@ struct SCTP_outqueue {
int malloced; int malloced;
}; };
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *); struct sctp_outq *sctp_outq_new(sctp_association_t *);
void sctp_outqueue_init(sctp_association_t *, sctp_outqueue_t *); void sctp_outq_init(sctp_association_t *, struct sctp_outq *);
void sctp_outqueue_teardown(sctp_outqueue_t *); void sctp_outq_teardown(struct sctp_outq *);
void sctp_outqueue_free(sctp_outqueue_t*); void sctp_outq_free(struct sctp_outq*);
void sctp_force_outqueue(sctp_outqueue_t *); int sctp_outq_tail(struct sctp_outq *, sctp_chunk_t *chunk);
int sctp_push_outqueue(sctp_outqueue_t *, sctp_chunk_t *chunk); int sctp_outq_flush(struct sctp_outq *, int);
int sctp_flush_outqueue(sctp_outqueue_t *, int); int sctp_outq_sack(struct sctp_outq *, sctp_sackhdr_t *);
int sctp_sack_outqueue(sctp_outqueue_t *, sctp_sackhdr_t *); int sctp_outq_is_empty(const struct sctp_outq *);
int sctp_outqueue_is_empty(const sctp_outqueue_t *); int sctp_outq_set_output_handlers(struct sctp_outq *,
int sctp_outqueue_set_output_handlers(sctp_outqueue_t *, sctp_outq_ohandler_init_t init,
sctp_outqueue_ohandler_init_t init, sctp_outq_ohandler_config_t config,
sctp_outqueue_ohandler_config_t config, sctp_outq_ohandler_t append,
sctp_outqueue_ohandler_t append, sctp_outq_ohandler_t build,
sctp_outqueue_ohandler_t build, sctp_outq_ohandler_force_t force);
sctp_outqueue_ohandler_force_t force); void sctp_outq_restart(struct sctp_outq *);
void sctp_outqueue_restart(sctp_outqueue_t *); void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
void sctp_retransmit(sctp_outqueue_t *, sctp_transport_t *, __u8); sctp_retransmit_reason_t);
void sctp_retransmit_mark(sctp_outqueue_t *, sctp_transport_t *, __u8); void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8);
/* These bind address data fields common between endpoints and associations */ /* These bind address data fields common between endpoints and associations */
...@@ -1027,7 +1083,7 @@ struct SCTP_endpoint { ...@@ -1027,7 +1083,7 @@ struct SCTP_endpoint {
/* These are the system-wide defaults and other stuff which is /* These are the system-wide defaults and other stuff which is
* endpoint-independent. * endpoint-independent.
*/ */
sctp_protocol_t *proto; struct sctp_protocol *proto;
/* Associations: A list of current associations and mappings /* Associations: A list of current associations and mappings
* to the data consumers for each association. This * to the data consumers for each association. This
...@@ -1066,10 +1122,7 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base) ...@@ -1066,10 +1122,7 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base)
{ {
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
/* We are not really a list, but the list_entry() macro is ep = container_of(base, sctp_endpoint_t, base);
* really quite generic to find the address of an outter struct.
*/
ep = list_entry(base, sctp_endpoint_t, base);
return ep; return ep;
} }
...@@ -1083,7 +1136,7 @@ void sctp_endpoint_hold(sctp_endpoint_t *); ...@@ -1083,7 +1136,7 @@ void sctp_endpoint_hold(sctp_endpoint_t *);
void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc); void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc);
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
const union sctp_addr *paddr, const union sctp_addr *paddr,
sctp_transport_t **); struct sctp_transport **);
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *); int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const union sctp_addr *); const union sctp_addr *);
...@@ -1184,7 +1237,7 @@ struct SCTP_association { ...@@ -1184,7 +1237,7 @@ struct SCTP_association {
* designate the connection we are currently using to * designate the connection we are currently using to
* transmit new data and most control chunks. * transmit new data and most control chunks.
*/ */
sctp_transport_t *primary_path; struct sctp_transport *primary_path;
/* Cache the primary path address here, when we /* Cache the primary path address here, when we
* need a an address for msg_name. * need a an address for msg_name.
...@@ -1195,7 +1248,7 @@ struct SCTP_association { ...@@ -1195,7 +1248,7 @@ struct SCTP_association {
* The path that we are currently using to * The path that we are currently using to
* transmit new data and most control chunks. * transmit new data and most control chunks.
*/ */
sctp_transport_t *active_path; struct sctp_transport *active_path;
/* retran_path /* retran_path
* *
...@@ -1207,13 +1260,13 @@ struct SCTP_association { ...@@ -1207,13 +1260,13 @@ struct SCTP_association {
* different from the last destination address to * different from the last destination address to
* which the DATA chunk was sent. * which the DATA chunk was sent.
*/ */
sctp_transport_t *retran_path; struct sctp_transport *retran_path;
/* Pointer to last transport I have sent on. */ /* Pointer to last transport I have sent on. */
sctp_transport_t *last_sent_to; struct sctp_transport *last_sent_to;
/* This is the last transport I have recieved DATA on. */ /* This is the last transport I have recieved DATA on. */
sctp_transport_t *last_data_from; struct sctp_transport *last_data_from;
/* /*
* Mapping An array of bits or bytes indicating which out of * Mapping An array of bits or bytes indicating which out of
...@@ -1325,7 +1378,7 @@ struct SCTP_association { ...@@ -1325,7 +1378,7 @@ struct SCTP_association {
struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES]; struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES];
/* Transport to which SHUTDOWN chunk was last sent. */ /* Transport to which SHUTDOWN chunk was last sent. */
sctp_transport_t *shutdown_last_sent_to; struct sctp_transport *shutdown_last_sent_to;
/* Next TSN : The next TSN number to be assigned to a new /* Next TSN : The next TSN number to be assigned to a new
* : DATA chunk. This is sent in the INIT or INIT * : DATA chunk. This is sent in the INIT or INIT
...@@ -1408,18 +1461,15 @@ struct SCTP_association { ...@@ -1408,18 +1461,15 @@ struct SCTP_association {
} defaults; } defaults;
/* This tracks outbound ssn for a given stream. */ /* This tracks outbound ssn for a given stream. */
__u16 ssn[SCTP_MAX_STREAM]; struct sctp_ssnmap *ssnmap;
/* All outbound chunks go through this structure. */ /* All outbound chunks go through this structure. */
sctp_outqueue_t outqueue; struct sctp_outq outqueue;
/* A smart pipe that will handle reordering and fragmentation, /* A smart pipe that will handle reordering and fragmentation,
* as well as handle passing events up to the ULP. * as well as handle passing events up to the ULP.
* In the future, we should make this at least dynamic, if
* not also some sparse structure.
*/ */
sctp_ulpqueue_t ulpq; struct sctp_ulpq ulpq;
__u8 _ssnmap[sctp_ulpqueue_storage_size(SCTP_MAX_STREAM)];
/* Need to send an ECNE Chunk? */ /* Need to send an ECNE Chunk? */
int need_ecne; int need_ecne;
...@@ -1505,7 +1555,7 @@ struct SCTP_association { ...@@ -1505,7 +1555,7 @@ struct SCTP_association {
* *
* *
* [I really think this is EXACTLY the sort of intelligence * [I really think this is EXACTLY the sort of intelligence
* which already resides in SCTP_outqueue. Please move this * which already resides in sctp_outq. Please move this
* queue and its supporting logic down there. --piggy] * queue and its supporting logic down there. --piggy]
*/ */
struct sk_buff_head addip_chunks; struct sk_buff_head addip_chunks;
...@@ -1546,10 +1596,7 @@ static inline sctp_association_t *sctp_assoc(sctp_endpoint_common_t *base) ...@@ -1546,10 +1596,7 @@ static inline sctp_association_t *sctp_assoc(sctp_endpoint_common_t *base)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
/* We are not really a list, but the list_entry() macro is asoc = container_of(base, sctp_association_t, base);
* really quite generic find the address of an outter struct.
*/
asoc = list_entry(base, sctp_association_t, base);
return asoc; return asoc;
} }
...@@ -1567,18 +1614,19 @@ void sctp_association_free(sctp_association_t *); ...@@ -1567,18 +1614,19 @@ void sctp_association_free(sctp_association_t *);
void sctp_association_put(sctp_association_t *); void sctp_association_put(sctp_association_t *);
void sctp_association_hold(sctp_association_t *); void sctp_association_hold(sctp_association_t *);
sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *); struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t *);
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *, struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *,
const union sctp_addr *); const union sctp_addr *);
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *, struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *,
const union sctp_addr *address, const union sctp_addr *address,
const int priority); const int priority);
void sctp_assoc_control_transport(sctp_association_t *, sctp_transport_t *, void sctp_assoc_control_transport(sctp_association_t *,
struct sctp_transport *,
sctp_transport_cmd_t, sctp_sn_error_t); sctp_transport_cmd_t, sctp_sn_error_t);
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *, __u32); struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *, __u32);
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *, struct sctp_transport *sctp_assoc_is_match(sctp_association_t *,
const union sctp_addr *, const union sctp_addr *,
const union sctp_addr *); const union sctp_addr *);
void sctp_assoc_migrate(sctp_association_t *, struct sock *); void sctp_assoc_migrate(sctp_association_t *, struct sock *);
void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src); void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src);
...@@ -1586,6 +1634,14 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *); ...@@ -1586,6 +1634,14 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *);
__u32 __sctp_association_get_tsn_block(sctp_association_t *, int); __u32 __sctp_association_get_tsn_block(sctp_association_t *, int);
__u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid); __u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid);
void sctp_assoc_sync_pmtu(sctp_association_t *);
void sctp_assoc_rwnd_increase(sctp_association_t *, int);
void sctp_assoc_rwnd_decrease(sctp_association_t *, int);
int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *, int);
int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *,
sctp_cookie_t *, int);
int sctp_cmp_addr_exact(const union sctp_addr *ss1, int sctp_cmp_addr_exact(const union sctp_addr *ss1,
const union sctp_addr *ss2); const union sctp_addr *ss2);
sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc); sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc);
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp. * 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
* *
* These are the definitions needed for the sctp_ulpqueue type. The * These are the definitions needed for the sctp_ulpq type. The
* sctp_ulpqueue is the interface between the Upper Layer Protocol, or ULP, * sctp_ulpq is the interface between the Upper Layer Protocol, or ULP,
* and the core SCTP state machine. This is the component which handles * and the core SCTP state machine. This is the component which handles
* reassembly and ordering. * reassembly and ordering.
* *
* The SCTP reference implementation is free software; * The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by * the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option) * the Free Software Foundation; either version 2, or (at your option)
* any later version. * any later version.
* *
* the SCTP reference implementation is distributed in the hope that it * the SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied * will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************ * ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to * along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330, * the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
* *
* Please send any bug reports or fixes you make to one of the * Please send any bug reports or fixes you make to the
* following email addresses: * email addresses:
* * lksctp developers <lksctp-developers@lists.sourceforge.net>
* Jon Grimm <jgrimm@us.ibm.com> *
* La Monte H.P. Yarroll <piggy@acm.org> * Or submit a bug report through the following website:
* * http://www.sf.net/projects/lksctp
*
* Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org>
*
* 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.
*/ */
...@@ -42,46 +47,26 @@ ...@@ -42,46 +47,26 @@
#define __sctp_ulpqueue_h__ #define __sctp_ulpqueue_h__
/* A structure to carry information to the ULP (e.g. Sockets API) */ /* A structure to carry information to the ULP (e.g. Sockets API) */
typedef struct sctp_ulpqueue { struct sctp_ulpq {
int malloced; int malloced;
spinlock_t lock;
sctp_association_t *asoc; sctp_association_t *asoc;
struct sk_buff_head reasm; struct sk_buff_head reasm;
struct sk_buff_head lobby; struct sk_buff_head lobby;
__u16 ssn[0]; };
} sctp_ulpqueue_t;
/* This macro assists in creation of external storage for variable length
* internal buffers.
*/
#define sctp_ulpqueue_storage_size(inbound) (sizeof(__u16) * (inbound))
sctp_ulpqueue_t *sctp_ulpqueue_new(sctp_association_t *asoc,
__u16 inbound,
int priority);
sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq,
sctp_association_t *asoc,
__u16 inbound);
void sctp_ulpqueue_free(sctp_ulpqueue_t *);
/* Prototypes. */
struct sctp_ulpq *sctp_ulpq_new(sctp_association_t *asoc, int priority);
struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *, sctp_association_t *);
void sctp_ulpq_free(struct sctp_ulpq *);
/* Add a new DATA chunk for processing. */ /* Add a new DATA chunk for processing. */
int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *, int sctp_ulpq_tail_data(struct sctp_ulpq *, sctp_chunk_t *chunk, int priority);
sctp_chunk_t *chunk,
int priority);
/* Add a new event for propogation to the ULP. */ /* Add a new event for propogation to the ULP. */
int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *, int sctp_ulpq_tail_event(struct sctp_ulpq *, struct sctp_ulpevent *ev);
sctp_ulpevent_t *event);
/* Is the ulpqueue empty. */ /* Is the ulpqueue empty. */
int sctp_ulpqueue_is_empty(sctp_ulpqueue_t *); int sctp_ulpqueue_is_empty(struct sctp_ulpq *);
int sctp_ulpqueue_is_data_empty(sctp_ulpqueue_t *);
#endif /* __sctp_ulpqueue_h__ */ #endif /* __sctp_ulpqueue_h__ */
...@@ -90,4 +75,4 @@ int sctp_ulpqueue_is_data_empty(sctp_ulpqueue_t *); ...@@ -90,4 +75,4 @@ int sctp_ulpqueue_is_data_empty(sctp_ulpqueue_t *);
...@@ -100,6 +100,14 @@ enum sctp_optname { ...@@ -100,6 +100,14 @@ enum sctp_optname {
#define SCTP_SOCKOPT_BINDX_REM SCTP_SOCKOPT_BINDX_REM #define SCTP_SOCKOPT_BINDX_REM SCTP_SOCKOPT_BINDX_REM
SCTP_SOCKOPT_PEELOFF, /* peel off association. */ SCTP_SOCKOPT_PEELOFF, /* peel off association. */
#define SCTP_SOCKOPT_PEELOFF SCTP_SOCKOPT_PEELOFF #define SCTP_SOCKOPT_PEELOFF SCTP_SOCKOPT_PEELOFF
SCTP_GET_PEER_ADDRS_NUM, /* Get number of peer addresss. */
#define SCTP_GET_PEER_ADDRS_NUM SCTP_GET_PEER_ADDRS_NUM
SCTP_GET_PEER_ADDRS, /* Get all peer addresss. */
#define SCTP_GET_PEER_ADDRS SCTP_GET_PEER_ADDRS
SCTP_GET_LOCAL_ADDRS_NUM, /* Get number of local addresss. */
#define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM
SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
}; };
...@@ -576,6 +584,15 @@ struct sctp_setstrm_timeout { ...@@ -576,6 +584,15 @@ struct sctp_setstrm_timeout {
__u16 ssto_streamid_end; __u16 ssto_streamid_end;
}; };
/*
* 8.3 8.5 get all peer/local addresses on a socket
* This parameter struct is for getsockopt
*/
struct sctp_getaddrs {
sctp_assoc_t assoc_id;
int addr_num;
struct sockaddr_storage *addrs;
};
/* These are bit fields for msghdr->msg_flags. See section 5.1. */ /* These are bit fields for msghdr->msg_flags. See section 5.1. */
/* On user space Linux, these live in <bits/socket.h> as an enum. */ /* On user space Linux, these live in <bits/socket.h> as an enum. */
......
...@@ -6,7 +6,7 @@ menu "SCTP Configuration (EXPERIMENTAL)" ...@@ -6,7 +6,7 @@ menu "SCTP Configuration (EXPERIMENTAL)"
depends on INET && EXPERIMENTAL depends on INET && EXPERIMENTAL
config IPV6_SCTP__ config IPV6_SCTP__
bool tristate
default y if IPV6=n default y if IPV6=n
default IPV6 if IPV6 default IPV6 if IPV6
......
...@@ -10,7 +10,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ ...@@ -10,7 +10,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
inqueue.o outqueue.o ulpqueue.o command.o \ inqueue.o outqueue.o ulpqueue.o command.o \
tsnmap.o bind_addr.o socket.o primitive.o \ tsnmap.o bind_addr.o socket.o primitive.o \
output.o input.o hashdriver.o sla1.o \ output.o input.o hashdriver.o sla1.o \
debug.o debug.o ssnmap.o
ifeq ($(CONFIG_SCTP_ADLER32), y) ifeq ($(CONFIG_SCTP_ADLER32), y)
sctp-y += adler32.o sctp-y += adler32.o
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 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
* *
...@@ -36,6 +37,7 @@ ...@@ -36,6 +37,7 @@
* Randall Stewart <rstewar1@email.mot.com> * Randall Stewart <rstewar1@email.mot.com>
* Ken Morneau <kmorneau@cisco.com> * Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@email.mot.com> * Qiaobing Xie <qxie1@email.mot.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.
...@@ -122,7 +124,7 @@ unsigned long update_adler32(unsigned long adler, ...@@ -122,7 +124,7 @@ unsigned long update_adler32(unsigned long adler,
return (s2 << 16) + s1; return (s2 << 16) + s1;
} }
__u32 count_crc(__u8 *ptr, __u16 count) __u32 sctp_start_cksum(__u8 *ptr, __u16 count)
{ {
/* /*
* Update a running Adler-32 checksum with the bytes * Update a running Adler-32 checksum with the bytes
...@@ -146,3 +148,15 @@ __u32 count_crc(__u8 *ptr, __u16 count) ...@@ -146,3 +148,15 @@ __u32 count_crc(__u8 *ptr, __u16 count)
return adler; return adler;
} }
__u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 adler)
{
adler = update_adler32(adler, ptr, count);
return adler;
}
__u32 sctp_end_cksum(__u32 adler)
{
return adler;
}
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines Corp. * Copyright (c) 2001-2003 International Business Machines Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
* *
...@@ -166,15 +166,10 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -166,15 +166,10 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc->max_init_attempts = sp->initmsg.sinit_max_attempts; asoc->max_init_attempts = sp->initmsg.sinit_max_attempts;
asoc->max_init_timeo = sp->initmsg.sinit_max_init_timeo * HZ; asoc->max_init_timeo = sp->initmsg.sinit_max_init_timeo * HZ;
/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number /* Allocate storage for the ssnmap after the inbound and outbound
* * streams have been negotiated during Init.
* The stream sequence number in all the streams shall start
* from 0 when the association is established. Also, when the
* stream sequence number reaches the value 65535 the next
* stream sequence number shall be set to 0.
*/ */
for (i = 0; i < SCTP_MAX_STREAM; i++) asoc->ssnmap = NULL;
asoc->ssn[i] = 0;
/* Set the local window size for receive. /* Set the local window size for receive.
* This is also the rcvbuf space per association. * This is also the rcvbuf space per association.
...@@ -252,15 +247,15 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -252,15 +247,15 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc); asoc);
/* Create an output queue. */ /* Create an output queue. */
sctp_outqueue_init(asoc, &asoc->outqueue); sctp_outq_init(asoc, &asoc->outqueue);
sctp_outqueue_set_output_handlers(&asoc->outqueue, sctp_outq_set_output_handlers(&asoc->outqueue,
sctp_packet_init, sctp_packet_init,
sctp_packet_config, sctp_packet_config,
sctp_packet_append_chunk, sctp_packet_append_chunk,
sctp_packet_transmit_chunk, sctp_packet_transmit_chunk,
sctp_packet_transmit); sctp_packet_transmit);
if (NULL == sctp_ulpqueue_init(&asoc->ulpq, asoc, SCTP_MAX_STREAM)) if (NULL == sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init; goto fail_init;
/* Set up the tsn tracking. */ /* Set up the tsn tracking. */
...@@ -296,7 +291,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -296,7 +291,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
*/ */
void sctp_association_free(sctp_association_t *asoc) void sctp_association_free(sctp_association_t *asoc)
{ {
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
struct list_head *pos, *temp; struct list_head *pos, *temp;
int i; int i;
...@@ -310,14 +305,17 @@ void sctp_association_free(sctp_association_t *asoc) ...@@ -310,14 +305,17 @@ void sctp_association_free(sctp_association_t *asoc)
asoc->base.dead = 1; asoc->base.dead = 1;
/* Dispose of any data lying around in the outqueue. */ /* Dispose of any data lying around in the outqueue. */
sctp_outqueue_free(&asoc->outqueue); sctp_outq_free(&asoc->outqueue);
/* Dispose of any pending messages for the upper layer. */ /* Dispose of any pending messages for the upper layer. */
sctp_ulpqueue_free(&asoc->ulpq); sctp_ulpq_free(&asoc->ulpq);
/* Dispose of any pending chunks on the inqueue. */ /* Dispose of any pending chunks on the inqueue. */
sctp_inqueue_free(&asoc->base.inqueue); sctp_inqueue_free(&asoc->base.inqueue);
/* Free ssnmap storage. */
sctp_ssnmap_free(asoc->ssnmap);
/* Clean up the bound address list. */ /* Clean up the bound address list. */
sctp_bind_addr_free(&asoc->base.bind_addr); sctp_bind_addr_free(&asoc->base.bind_addr);
...@@ -339,7 +337,7 @@ void sctp_association_free(sctp_association_t *asoc) ...@@ -339,7 +337,7 @@ void sctp_association_free(sctp_association_t *asoc)
/* Release the transport structures. */ /* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport, transports);
list_del(pos); list_del(pos);
sctp_transport_free(transport); sctp_transport_free(transport);
} }
...@@ -365,11 +363,11 @@ static void sctp_association_destroy(sctp_association_t *asoc) ...@@ -365,11 +363,11 @@ static void sctp_association_destroy(sctp_association_t *asoc)
/* Add a transport address to an association. */ /* Add a transport address to an association. */
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc,
const union sctp_addr *addr, const union sctp_addr *addr,
int priority) int priority)
{ {
sctp_transport_t *peer; struct sctp_transport *peer;
sctp_opt_t *sp; sctp_opt_t *sp;
unsigned short port; unsigned short port;
...@@ -392,8 +390,8 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -392,8 +390,8 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
sctp_transport_set_owner(peer, asoc); sctp_transport_set_owner(peer, asoc);
/* Cache a route for the transport. */ /* Initialize the pmtu of the transport. */
sctp_transport_route(peer, NULL, sctp_sk(asoc->base.sk)); sctp_transport_pmtu(peer);
/* If this is the first transport addr on this association, /* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU. * initialize the association PMTU to the peer's PMTU.
...@@ -478,16 +476,16 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -478,16 +476,16 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
} }
/* Lookup a transport by address. */ /* Lookup a transport by address. */
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc, struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
const union sctp_addr *address) const union sctp_addr *address)
{ {
sctp_transport_t *t; struct sctp_transport *t;
struct list_head *pos; struct list_head *pos;
/* Cycle through all transports searching for a peer address. */ /* Cycle through all transports searching for a peer address. */
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports); t = list_entry(pos, struct sctp_transport, transports);
if (sctp_cmp_addr_exact(address, &t->ipaddr)) if (sctp_cmp_addr_exact(address, &t->ipaddr))
return t; return t;
} }
...@@ -500,13 +498,13 @@ sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc, ...@@ -500,13 +498,13 @@ sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
* Select and update the new active and retran paths. * Select and update the new active and retran paths.
*/ */
void sctp_assoc_control_transport(sctp_association_t *asoc, void sctp_assoc_control_transport(sctp_association_t *asoc,
sctp_transport_t *transport, struct sctp_transport *transport,
sctp_transport_cmd_t command, sctp_transport_cmd_t command,
sctp_sn_error_t error) sctp_sn_error_t error)
{ {
sctp_transport_t *t = NULL; struct sctp_transport *t = NULL;
sctp_transport_t *first; struct sctp_transport *first;
sctp_transport_t *second; struct sctp_transport *second;
sctp_ulpevent_t *event; sctp_ulpevent_t *event;
struct list_head *pos; struct list_head *pos;
int spc_state = 0; int spc_state = 0;
...@@ -524,7 +522,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc, ...@@ -524,7 +522,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
break; break;
default: default:
BUG(); return;
}; };
/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
...@@ -534,7 +532,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc, ...@@ -534,7 +532,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
(struct sockaddr_storage *) &transport->ipaddr, (struct sockaddr_storage *) &transport->ipaddr,
0, spc_state, error, GFP_ATOMIC); 0, spc_state, error, GFP_ATOMIC);
if (event) if (event)
sctp_ulpqueue_tail_event(&asoc->ulpq, event); sctp_ulpq_tail_event(&asoc->ulpq, event);
/* Select new active and retran paths. */ /* Select new active and retran paths. */
...@@ -547,7 +545,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc, ...@@ -547,7 +545,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
first = NULL; second = NULL; first = NULL; second = NULL;
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports); t = list_entry(pos, struct sctp_transport, transports);
if (!t->active) if (!t->active)
continue; continue;
...@@ -631,11 +629,6 @@ __u32 __sctp_association_get_tsn_block(sctp_association_t *asoc, int num) ...@@ -631,11 +629,6 @@ __u32 __sctp_association_get_tsn_block(sctp_association_t *asoc, int num)
return retval; return retval;
} }
/* Fetch the next Stream Sequence Number for stream number 'sid'. */
__u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid)
{
return asoc->ssn[sid]++;
}
/* Compare two addresses to see if they match. Wildcard addresses /* Compare two addresses to see if they match. Wildcard addresses
* only match themselves. * only match themselves.
...@@ -695,12 +688,12 @@ sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc) ...@@ -695,12 +688,12 @@ sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc)
/* /*
* Find which transport this TSN was sent on. * Find which transport this TSN was sent on.
*/ */
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn) struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
{ {
sctp_transport_t *active; struct sctp_transport *active;
sctp_transport_t *match; struct sctp_transport *match;
struct list_head *entry, *pos; struct list_head *entry, *pos;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
__u32 key = htonl(tsn); __u32 key = htonl(tsn);
...@@ -734,7 +727,7 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn) ...@@ -734,7 +727,7 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
/* If not found, go search all the other transports. */ /* If not found, go search all the other transports. */
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport, transports);
if (transport == active) if (transport == active)
break; break;
...@@ -752,11 +745,11 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn) ...@@ -752,11 +745,11 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
} }
/* Is this the association we are looking for? */ /* Is this the association we are looking for? */
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc, struct sctp_transport *sctp_assoc_is_match(sctp_association_t *asoc,
const union sctp_addr *laddr, const union sctp_addr *laddr,
const union sctp_addr *paddr) const union sctp_addr *paddr)
{ {
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_read_lock(&asoc->base.addr_lock); sctp_read_lock(&asoc->base.addr_lock);
...@@ -852,8 +845,6 @@ void sctp_assoc_migrate(sctp_association_t *assoc, struct sock *newsk) ...@@ -852,8 +845,6 @@ void sctp_assoc_migrate(sctp_association_t *assoc, struct sock *newsk)
/* Update an association (possibly from unexpected COOKIE-ECHO processing). */ /* Update an association (possibly from unexpected COOKIE-ECHO processing). */
void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new) void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new)
{ {
int i;
/* Copy in new parameters of peer. */ /* Copy in new parameters of peer. */
asoc->c = new->c; asoc->c = new->c;
asoc->peer.rwnd = new->peer.rwnd; asoc->peer.rwnd = new->peer.rwnd;
...@@ -872,23 +863,28 @@ void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new) ...@@ -872,23 +863,28 @@ void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new)
/* If the case is A (association restart), use /* If the case is A (association restart), use
* initial_tsn as next_tsn. If the case is B, use * initial_tsn as next_tsn. If the case is B, use
* current next_tsn in case there is data sent to peer * current next_tsn in case data sent to peer
* has been discarded and needs retransmission. * has been discarded and needs retransmission.
*/ */
if (SCTP_STATE_ESTABLISHED == asoc->state) { if (SCTP_STATE_ESTABLISHED == asoc->state) {
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;
/* Reinitialize SSN for both local streams /* Reinitialize SSN for both local streams
* and peer's streams. * and peer's streams.
*/ */
for (i = 0; i < SCTP_MAX_STREAM; i++) { sctp_ssnmap_clear(asoc->ssnmap);
asoc->ssn[i] = 0;
asoc->ulpq.ssn[i] = 0;
}
} else { } else {
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
if (!asoc->ssnmap) {
/* Move the ssnmap. */
asoc->ssnmap = new->ssnmap;
new->ssnmap = NULL;
}
} }
} }
/* Choose the transport for sending a shutdown packet. /* Choose the transport for sending a shutdown packet.
...@@ -896,9 +892,9 @@ void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new) ...@@ -896,9 +892,9 @@ void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new)
* through the inactive transports as this is the next best thing * through the inactive transports as this is the next best thing
* we can try. * we can try.
*/ */
sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc) struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
{ {
sctp_transport_t *t, *next; struct sctp_transport *t, *next;
struct list_head *head = &asoc->peer.transport_addr_list; struct list_head *head = &asoc->peer.transport_addr_list;
struct list_head *pos; struct list_head *pos;
...@@ -921,7 +917,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc) ...@@ -921,7 +917,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
else else
pos = pos->next; pos = pos->next;
t = list_entry(pos, sctp_transport_t, transports); t = list_entry(pos, struct sctp_transport, transports);
/* Try to find an active transport. */ /* Try to find an active transport. */
...@@ -947,3 +943,136 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc) ...@@ -947,3 +943,136 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
return t; return t;
} }
/* Update the association's pmtu and frag_point by going through all the
* transports. This routine is called when a transport's PMTU has changed.
*/
void sctp_assoc_sync_pmtu(sctp_association_t *asoc)
{
struct sctp_transport *t;
struct list_head *pos;
__u32 pmtu = 0;
if (!asoc)
return;
/* Get the lowest pmtu of all the transports. */
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports);
if (!pmtu || (t->pmtu < pmtu))
pmtu = t->pmtu;
}
if (pmtu) {
asoc->pmtu = pmtu;
asoc->frag_point = pmtu - (SCTP_IP_OVERHEAD +
sizeof(sctp_data_chunk_t));
}
SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
__FUNCTION__, asoc, asoc->pmtu, asoc->frag_point);
}
/* Increase asoc's rwnd by len and send any window update SACK if needed. */
void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len)
{
sctp_chunk_t *sack;
struct timer_list *timer;
if (asoc->rwnd_over) {
if (asoc->rwnd_over >= len) {
asoc->rwnd_over -= len;
} else {
asoc->rwnd += (len - asoc->rwnd_over);
asoc->rwnd_over = 0;
}
} else {
asoc->rwnd += len;
}
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) - %u\n",
__FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over,
asoc->a_rwnd);
/* Send a window update SACK if the rwnd has increased by at least the
* minimum of the association's PMTU and half of the receive buffer.
* The algorithm used is similar to the one described in
* Section 4.2.3.3 of RFC 1122.
*/
if ((asoc->state == SCTP_STATE_ESTABLISHED) &&
(asoc->rwnd > asoc->a_rwnd) &&
((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) {
SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "
"rwnd: %u a_rwnd: %u\n",
__FUNCTION__, asoc, asoc->rwnd, asoc->a_rwnd);
sack = sctp_make_sack(asoc);
if (!sack)
return;
/* Update the last advertised rwnd value. */
asoc->a_rwnd = asoc->rwnd;
asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0;
sctp_outq_tail(&asoc->outqueue, sack);
/* Stop the SACK timer. */
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
if (timer_pending(timer) && del_timer(timer))
sctp_association_put(asoc);
}
}
/* Decrease asoc's rwnd by len. */
void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len)
{
SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
if (asoc->rwnd >= len) {
asoc->rwnd -= len;
} else {
asoc->rwnd_over = len - asoc->rwnd;
asoc->rwnd = 0;
}
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n",
__FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over);
}
/* Build the bind address list for the association based on info from the
* local endpoint and the remote peer.
*/
int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *asoc, int priority)
{
sctp_scope_t scope;
int flags;
/* Use scoping rules to determine the subset of addresses from
* the endpoint.
*/
scope = sctp_scope(&asoc->peer.active_path->ipaddr);
flags = (PF_INET6 == asoc->base.sk->family) ? SCTP_ADDR6_ALLOWED : 0;
if (asoc->peer.ipv4_address)
flags |= SCTP_ADDR4_PEERSUPP;
if (asoc->peer.ipv6_address)
flags |= SCTP_ADDR6_PEERSUPP;
return sctp_bind_addr_copy(&asoc->base.bind_addr,
&asoc->ep->base.bind_addr,
scope, priority, flags);
}
/* Build the association's bind address list from the cookie. */
int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *asoc,
sctp_cookie_t *cookie, int priority)
{
int var_size2 = ntohs(cookie->peer_init->chunk_hdr.length);
int var_size3 = cookie->raw_addr_list_len;
__u8 *raw_addr_list = (__u8 *)cookie + sizeof(sctp_cookie_t) +
var_size2;
return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw_addr_list,
var_size3, asoc->ep->base.bind_addr.port,
priority);
}
...@@ -47,8 +47,8 @@ sctp_cmd_seq_t *sctp_new_cmd_seq(int priority) ...@@ -47,8 +47,8 @@ sctp_cmd_seq_t *sctp_new_cmd_seq(int priority)
{ {
sctp_cmd_seq_t *retval = t_new(sctp_cmd_seq_t, priority); sctp_cmd_seq_t *retval = t_new(sctp_cmd_seq_t, priority);
/* XXX Check for NULL? -DaveM */ if (retval)
sctp_init_cmd_seq(retval); sctp_init_cmd_seq(retval);
return retval; return retval;
} }
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp. * Copyright (c) 2001-2003 International Business Machines, Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
* Written or modified by: * Written or modified by:
* Dinakaran Joseph * Dinakaran Joseph
* 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.
...@@ -135,11 +136,10 @@ __u32 crc_c[256] = { ...@@ -135,11 +136,10 @@ __u32 crc_c[256] = {
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
}; };
__u32 count_crc(__u8 *buffer, __u16 length) __u32 sctp_start_cksum(__u8 *buffer, __u16 length)
{ {
__u32 crc32 = ~(__u32) 0; __u32 crc32 = ~(__u32) 0;
__u32 i, result; __u32 i;
__u8 byte0, byte1, byte2, byte3;
/* Optimize this routine to be SCTP specific, knowing how /* Optimize this routine to be SCTP specific, knowing how
* to skip the checksum field of the SCTP header. * to skip the checksum field of the SCTP header.
...@@ -157,6 +157,24 @@ __u32 count_crc(__u8 *buffer, __u16 length) ...@@ -157,6 +157,24 @@ __u32 count_crc(__u8 *buffer, __u16 length)
for (i = sizeof(struct sctphdr); i < length ; i++) for (i = sizeof(struct sctphdr); i < length ; i++)
CRC32C(crc32, buffer[i]); CRC32C(crc32, buffer[i]);
return crc32;
}
__u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
{
__u32 i;
for (i = 0; i < length ; i++)
CRC32C(crc32, buffer[i]);
return crc32;
}
__u32 sctp_end_cksum(__u32 crc32)
{
__u32 result;
__u8 byte0, byte1, byte2, byte3;
result = ~crc32; result = ~crc32;
/* result now holds the negated polynomial remainder; /* result now holds the negated polynomial remainder;
...@@ -183,5 +201,3 @@ __u32 count_crc(__u8 *buffer, __u16 length) ...@@ -183,5 +201,3 @@ __u32 count_crc(__u8 *buffer, __u16 length)
byte3); byte3);
return crc32; return crc32;
} }
...@@ -148,22 +148,11 @@ const char *sctp_status_tbl[] = { ...@@ -148,22 +148,11 @@ const char *sctp_status_tbl[] = {
/* Printable forms of primitives */ /* Printable forms of primitives */
static const char *sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = { static const char *sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = {
"PRIMITIVE_INITIALIZE",
"PRIMITIVE_ASSOCIATE", "PRIMITIVE_ASSOCIATE",
"PRIMITIVE_SHUTDOWN", "PRIMITIVE_SHUTDOWN",
"PRIMITIVE_ABORT", "PRIMITIVE_ABORT",
"PRIMITIVE_SEND", "PRIMITIVE_SEND",
"PRIMITIVE_SETPRIMARY",
"PRIMITIVE_RECEIVE",
"PRIMITIVE_STATUS",
"PRIMITIVE_CHANGEHEARTBEAT",
"PRIMITIVE_REQUESTHEARTBEAT", "PRIMITIVE_REQUESTHEARTBEAT",
"PRIMITIVE_GETSRTTREPORT",
"PRIMITIVE_SETFAILURETHRESHOLD",
"PRIMITIVE_SETPROTOPARAMETERS",
"PRIMITIVE_RECEIVE_UNSENT",
"PRIMITIVE_RECEIVE_UNACKED",
"PRIMITIVE_DESTROY"
}; };
/* Lookup primitive debug name. */ /* Lookup primitive debug name. */
...@@ -178,7 +167,6 @@ const char *sctp_pname(const sctp_subtype_t id) ...@@ -178,7 +167,6 @@ const char *sctp_pname(const sctp_subtype_t id)
static const char *sctp_other_tbl[] = { static const char *sctp_other_tbl[] = {
"NO_PENDING_TSN", "NO_PENDING_TSN",
"ICMP_UNREACHFRAG"
}; };
/* Lookup "other" debug name. */ /* Lookup "other" debug name. */
...@@ -197,12 +185,10 @@ static const char *sctp_timer_tbl[] = { ...@@ -197,12 +185,10 @@ static const char *sctp_timer_tbl[] = {
"TIMEOUT_T1_INIT", "TIMEOUT_T1_INIT",
"TIMEOUT_T2_SHUTDOWN", "TIMEOUT_T2_SHUTDOWN",
"TIMEOUT_T3_RTX", "TIMEOUT_T3_RTX",
"TIMEOUT_T4_RTO",
"TIMEOUT_T5_SHUTDOWN_GUARD", "TIMEOUT_T5_SHUTDOWN_GUARD",
"TIMEOUT_HEARTBEAT", "TIMEOUT_HEARTBEAT",
"TIMEOUT_SACK", "TIMEOUT_SACK",
"TIMEOUT_AUTOCLOSE", "TIMEOUT_AUTOCLOSE",
"TIMEOUT_PMTU_RAISE",
}; };
/* Lookup timer debug name. */ /* Lookup timer debug name. */
......
...@@ -137,7 +137,6 @@ sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto, ...@@ -137,7 +137,6 @@ sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto,
ep->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = ep->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] =
sp->rtoinfo.srto_initial; sp->rtoinfo.srto_initial;
ep->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0; ep->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0;
ep->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = 0;
/* sctpimpguide-05 Section 2.12.2 /* sctpimpguide-05 Section 2.12.2
* If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the * If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the
...@@ -152,8 +151,6 @@ sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto, ...@@ -152,8 +151,6 @@ sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto,
SCTP_DEFAULT_TIMEOUT_SACK; SCTP_DEFAULT_TIMEOUT_SACK;
ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] =
sp->autoclose * HZ; sp->autoclose * HZ;
ep->timeouts[SCTP_EVENT_TIMEOUT_PMTU_RAISE] =
SCTP_DEFAULT_TIMEOUT_PMTU_RAISE;
/* Set up the default send/receive buffer space. */ /* Set up the default send/receive buffer space. */
...@@ -264,7 +261,7 @@ sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep, ...@@ -264,7 +261,7 @@ sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep,
sctp_association_t *__sctp_endpoint_lookup_assoc( sctp_association_t *__sctp_endpoint_lookup_assoc(
const sctp_endpoint_t *endpoint, const sctp_endpoint_t *endpoint,
const union sctp_addr *paddr, const union sctp_addr *paddr,
sctp_transport_t **transport) struct sctp_transport **transport)
{ {
int rport; int rport;
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -289,9 +286,10 @@ sctp_association_t *__sctp_endpoint_lookup_assoc( ...@@ -289,9 +286,10 @@ sctp_association_t *__sctp_endpoint_lookup_assoc(
} }
/* Lookup association on an endpoint based on a peer address. BH-safe. */ /* Lookup association on an endpoint based on a peer address. BH-safe. */
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, sctp_association_t *sctp_endpoint_lookup_assoc(
const union sctp_addr *paddr, const sctp_endpoint_t *ep,
sctp_transport_t **transport) const union sctp_addr *paddr,
struct sctp_transport **transport)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -333,7 +331,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep) ...@@ -333,7 +331,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
struct sock *sk; struct sock *sk;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
sctp_inqueue_t *inqueue; sctp_inqueue_t *inqueue;
sctp_subtype_t subtype; sctp_subtype_t subtype;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp. * 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
...@@ -53,6 +53,9 @@ ...@@ -53,6 +53,9 @@
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/time.h> /* For struct timeval */ #include <linux/time.h> /* For struct timeval */
#include <net/ip.h>
#include <net/icmp.h>
#include <net/snmp.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
...@@ -63,7 +66,7 @@ static int sctp_rcv_ootb(struct sk_buff *); ...@@ -63,7 +66,7 @@ static int sctp_rcv_ootb(struct sk_buff *);
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const union sctp_addr *laddr, const union sctp_addr *laddr,
const union sctp_addr *paddr, const union sctp_addr *paddr,
sctp_transport_t **transportp); struct sctp_transport **transportp);
sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr); sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
...@@ -72,10 +75,19 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb) ...@@ -72,10 +75,19 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb)
{ {
struct sctphdr *sh; struct sctphdr *sh;
__u32 cmp, val; __u32 cmp, val;
struct sk_buff *list = skb_shinfo(skb)->frag_list;
sh = (struct sctphdr *) skb->h.raw; sh = (struct sctphdr *) skb->h.raw;
cmp = ntohl(sh->checksum); cmp = ntohl(sh->checksum);
val = count_crc((__u8 *)sh, skb->len);
val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
for (; list; list = list->next)
val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
val);
val = sctp_end_cksum(val);
if (val != cmp) { if (val != cmp) {
/* CRC failure, dump it. */ /* CRC failure, dump it. */
return -1; return -1;
...@@ -92,7 +104,7 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -92,7 +104,7 @@ int sctp_rcv(struct sk_buff *skb)
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_endpoint_t *ep = NULL; sctp_endpoint_t *ep = NULL;
sctp_endpoint_common_t *rcvr; sctp_endpoint_common_t *rcvr;
sctp_transport_t *transport = NULL; struct sctp_transport *transport = NULL;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
struct sctphdr *sh; struct sctphdr *sh;
union sctp_addr src; union sctp_addr src;
...@@ -248,6 +260,27 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -248,6 +260,27 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
return 0; return 0;
} }
/* Handle icmp frag needed error. */
static inline void sctp_icmp_frag_needed(struct sock *sk,
sctp_association_t *asoc,
struct sctp_transport *transport,
__u32 pmtu)
{
if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
printk(KERN_WARNING "%s: Reported pmtu %d too low, "
"using default minimum of %d\n", __FUNCTION__, pmtu,
SCTP_DEFAULT_MINSEGMENT);
pmtu = SCTP_DEFAULT_MINSEGMENT;
}
if (!sock_owned_by_user(sk) && transport && (transport->pmtu != pmtu)) {
transport->pmtu = pmtu;
sctp_assoc_sync_pmtu(asoc);
sctp_retransmit(&asoc->outqueue, transport,
SCTP_RETRANSMIT_PMTU_DISCOVERY );
}
}
/* /*
* This routine is called by the ICMP module when it gets some * This routine is called by the ICMP module when it gets some
* sort of error condition. If err < 0 then the socket should * sort of error condition. If err < 0 then the socket should
...@@ -263,9 +296,109 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -263,9 +296,109 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
* is probably better. * is probably better.
* *
*/ */
void sctp_v4_err(struct sk_buff *skb, u32 info) void sctp_v4_err(struct sk_buff *skb, __u32 info)
{ {
/* This should probably involve a call to SCTPhandleICMP(). */ struct iphdr *iph = (struct iphdr *)skb->data;
struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2));
int type = skb->h.icmph->type;
int code = skb->h.icmph->code;
union sctp_addr saddr, daddr;
struct inet_opt *inet;
struct sock *sk = NULL;
sctp_endpoint_t *ep = NULL;
sctp_association_t *asoc = NULL;
struct sctp_transport *transport;
int err;
if (skb->len < ((iph->ihl << 2) + 8)) {
ICMP_INC_STATS_BH(IcmpInErrors);
return;
}
saddr.v4.sin_family = AF_INET;
saddr.v4.sin_port = ntohs(sh->source);
memcpy(&saddr.v4.sin_addr.s_addr, &iph->saddr, sizeof(struct in_addr));
daddr.v4.sin_family = AF_INET;
daddr.v4.sin_port = ntohs(sh->dest);
memcpy(&daddr.v4.sin_addr.s_addr, &iph->daddr, sizeof(struct in_addr));
/* Look for an association that matches the incoming ICMP error
* packet.
*/
asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
if (!asoc) {
/* If there is no matching association, see if it matches any
* endpoint. This may happen for an ICMP error generated in
* response to an INIT_ACK.
*/
ep = __sctp_rcv_lookup_endpoint(&daddr);
if (!ep) {
ICMP_INC_STATS_BH(IcmpInErrors);
return;
}
}
if (asoc) {
if (ntohl(sh->vtag) != asoc->c.peer_vtag) {
ICMP_INC_STATS_BH(IcmpInErrors);
goto out;
}
sk = asoc->base.sk;
} else
sk = ep->base.sk;
sctp_bh_lock_sock(sk);
/* If too many ICMPs get dropped on busy
* servers this needs to be solved differently.
*/
if (sock_owned_by_user(sk))
NET_INC_STATS_BH(LockDroppedIcmps);
switch (type) {
case ICMP_PARAMETERPROB:
err = EPROTO;
break;
case ICMP_DEST_UNREACH:
if (code > NR_ICMP_UNREACH)
goto out_unlock;
/* PMTU discovery (RFC1191) */
if (ICMP_FRAG_NEEDED == code) {
sctp_icmp_frag_needed(sk, asoc, transport, info);
goto out_unlock;
}
err = icmp_err_convert[code].errno;
break;
case ICMP_TIME_EXCEEDED:
/* Ignore any time exceeded errors due to fragment reassembly
* timeouts.
*/
if (ICMP_EXC_FRAGTIME == code)
goto out_unlock;
err = EHOSTUNREACH;
break;
default:
goto out_unlock;
}
inet = inet_sk(sk);
if (!sock_owned_by_user(sk) && inet->recverr) {
sk->err = err;
sk->error_report(sk);
} else { /* Only an error on timeout */
sk->err_soft = err;
}
out_unlock:
sctp_bh_unlock_sock(sk);
out:
sock_put(sk);
if (asoc)
sctp_association_put(asoc);
if (ep)
sctp_endpoint_put(ep);
} }
/* /*
...@@ -303,17 +436,17 @@ int sctp_rcv_ootb(struct sk_buff *skb) ...@@ -303,17 +436,17 @@ int sctp_rcv_ootb(struct sk_buff *skb)
* chunk, the receiver should silently discard the packet * chunk, the receiver should silently discard the packet
* and take no further action. * and take no further action.
*/ */
if (ch->type == SCTP_CID_SHUTDOWN_COMPLETE) if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type)
goto discard; goto discard;
/* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR
* or a COOKIE ACK the SCTP Packet should be silently * or a COOKIE ACK the SCTP Packet should be silently
* discarded. * discarded.
*/ */
if (ch->type == SCTP_CID_COOKIE_ACK) if (SCTP_CID_COOKIE_ACK == ch->type)
goto discard; goto discard;
if (ch->type == SCTP_CID_ERROR) { if (SCTP_CID_ERROR == ch->type) {
err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t)); err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t));
if (SCTP_ERROR_STALE_COOKIE == err->cause) if (SCTP_ERROR_STALE_COOKIE == err->cause)
goto discard; goto discard;
...@@ -485,12 +618,12 @@ void __sctp_unhash_established(sctp_association_t *asoc) ...@@ -485,12 +618,12 @@ void __sctp_unhash_established(sctp_association_t *asoc)
/* Look up an association. */ /* Look up an association. */
sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr, sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr,
const union sctp_addr *paddr, const union sctp_addr *paddr,
sctp_transport_t **transportp) struct sctp_transport **transportp)
{ {
sctp_hashbucket_t *head; sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb; sctp_endpoint_common_t *epb;
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_transport_t *transport; struct sctp_transport *transport;
int hash; int hash;
/* Optimize here for direct hit, only listening connections can /* Optimize here for direct hit, only listening connections can
...@@ -521,7 +654,7 @@ sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr, ...@@ -521,7 +654,7 @@ sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr,
/* Look up an association. BH-safe. */ /* Look up an association. BH-safe. */
sctp_association_t *sctp_lookup_association(const union sctp_addr *laddr, sctp_association_t *sctp_lookup_association(const union sctp_addr *laddr,
const union sctp_addr *paddr, const union sctp_addr *paddr,
sctp_transport_t **transportp) struct sctp_transport **transportp)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -537,7 +670,7 @@ int sctp_has_association(const union sctp_addr *laddr, ...@@ -537,7 +670,7 @@ int sctp_has_association(const union sctp_addr *laddr,
const union sctp_addr *paddr) const union sctp_addr *paddr)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_transport_t *transport; struct sctp_transport *transport;
if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) { if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
sock_put(asoc->base.sk); sock_put(asoc->base.sk);
...@@ -567,7 +700,7 @@ int sctp_has_association(const union sctp_addr *laddr, ...@@ -567,7 +700,7 @@ int sctp_has_association(const union sctp_addr *laddr,
* *
*/ */
static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb, static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb,
const union sctp_addr *laddr, sctp_transport_t **transportp) const union sctp_addr *laddr, struct sctp_transport **transportp)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
union sctp_addr addr; union sctp_addr addr;
...@@ -627,7 +760,7 @@ static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb, ...@@ -627,7 +760,7 @@ static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb,
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const union sctp_addr *paddr, const union sctp_addr *paddr,
const union sctp_addr *laddr, const union sctp_addr *laddr,
sctp_transport_t **transportp) struct sctp_transport **transportp)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
......
...@@ -98,43 +98,22 @@ static inline void sctp_v6_err(struct sk_buff *skb, ...@@ -98,43 +98,22 @@ static inline void sctp_v6_err(struct sk_buff *skb,
} }
/* Based on tcp_v6_xmit() in tcp_ipv6.c. */ /* Based on tcp_v6_xmit() in tcp_ipv6.c. */
static inline int sctp_v6_xmit(struct sk_buff *skb) static inline int sctp_v6_xmit(struct sk_buff *skb,
struct sctp_transport *transport, int ipfragok)
{ {
struct sock *sk = skb->sk; struct sock *sk = skb->sk;
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct flowi fl; struct flowi fl;
struct dst_entry *dst = skb->dst; struct dst_entry *dst = skb->dst;
struct rt6_info *rt6 = (struct rt6_info *)dst; struct rt6_info *rt6 = (struct rt6_info *)dst;
struct in6_addr saddr;
int err;
fl.proto = sk->protocol; fl.proto = sk->protocol;
fl.fl6_dst = &rt6->rt6i_dst.addr;
/* FIXME: Currently, ip6_route_output() doesn't fill in the source /* Fill in the dest address from the route entry passed with the skb
* address in the returned route entry. So we call ipv6_get_saddr() * and the source address from the transport.
* to get an appropriate source address. It is possible that this address
* may not be part of the bind address list of the association.
* Once ip6_route_ouput() is fixed so that it returns a route entry
* with an appropriate source address, the following if condition can
* be removed. With ip6_route_output() returning a source address filled
* route entry, sctp_transport_route() can do real source address
* selection for v6.
*/ */
if (ipv6_addr_any(&rt6->rt6i_src.addr)) { fl.fl6_dst = &rt6->rt6i_dst.addr;
err = ipv6_get_saddr(dst, fl.fl6_dst, &saddr); fl.fl6_src = &transport->saddr.v6.sin6_addr;
if (err) {
printk(KERN_ERR "%s: No saddr available for "
"DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, NIP6(fl.fl6_src));
return err;
}
fl.fl6_src = &saddr;
} else {
fl.fl6_src = &rt6->rt6i_src.addr;
}
fl.fl6_flowlabel = np->flow_label; fl.fl6_flowlabel = np->flow_label;
IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
...@@ -147,20 +126,26 @@ static inline int sctp_v6_xmit(struct sk_buff *skb) ...@@ -147,20 +126,26 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
fl.nl_u.ip6_u.daddr = rt0->addr; fl.nl_u.ip6_u.daddr = rt0->addr;
} }
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
"src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
"dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, skb, skb->len, NIP6(fl.fl6_src),
NIP6(fl.fl6_dst));
return ip6_xmit(sk, skb, &fl, np->opt); return ip6_xmit(sk, skb, &fl, np->opt);
} }
/* Returns the dst cache entry for the given source and destination ip /* Returns the dst cache entry for the given source and destination ip
* addresses. * addresses.
*/ */
struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr, struct dst_entry *sctp_v6_get_dst(sctp_association_t *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr) union sctp_addr *saddr)
{ {
struct dst_entry *dst; struct dst_entry *dst;
struct flowi fl = { struct flowi fl = {
.nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } }; .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } };
SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
__FUNCTION__, NIP6(fl.fl6_dst)); __FUNCTION__, NIP6(fl.fl6_dst));
...@@ -181,12 +166,96 @@ struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr, ...@@ -181,12 +166,96 @@ struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr,
NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr)); NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr));
} else { } else {
SCTP_DEBUG_PRINTK("NO ROUTE\n"); SCTP_DEBUG_PRINTK("NO ROUTE\n");
return NULL;
} }
return dst; return dst;
} }
/* Returns the number of consecutive initial bits that match in the 2 ipv6
* addresses.
*/
static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
union sctp_addr *s2)
{
struct in6_addr *a1 = &s1->v6.sin6_addr;
struct in6_addr *a2 = &s2->v6.sin6_addr;
int i, j;
for (i = 0; i < 4 ; i++) {
__u32 a1xora2;
a1xora2 = a1->s6_addr32[i] ^ a2->s6_addr32[i];
if ((j = fls(ntohl(a1xora2))))
return (i * 32 + 32 - j);
}
return (i*32);
}
/* Fills in the source address(saddr) based on the destination address(daddr)
* and asoc's bind address list.
*/
void sctp_v6_get_saddr(sctp_association_t *asoc, struct dst_entry *dst,
union sctp_addr *daddr, union sctp_addr *saddr)
{
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;
struct list_head *pos;
sctp_scope_t scope;
union sctp_addr *baddr = NULL;
__u8 matchlen = 0;
__u8 bmatchlen;
SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p "
"daddr:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
__FUNCTION__, asoc, dst, NIP6(&daddr->v6.sin6_addr));
if (!asoc) {
ipv6_get_saddr(dst, &daddr->v6.sin6_addr, &saddr->v6.sin6_addr);
SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: "
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(&saddr->v6.sin6_addr));
return;
}
scope = sctp_scope(daddr);
bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
/* Go through the bind address list and find the best source address
* that matches the scope of the destination address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
if ((laddr->a.sa.sa_family == AF_INET6) &&
(scope <= sctp_scope(&laddr->a))) {
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
if (!baddr || (matchlen < bmatchlen)) {
baddr = &laddr->a;
matchlen = bmatchlen;
}
}
}
if (baddr) {
memcpy(saddr, baddr, sizeof(union sctp_addr));
SCTP_DEBUG_PRINTK("saddr: "
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(&saddr->v6.sin6_addr));
} else {
printk(KERN_ERR "%s: asoc:%p Could not find a valid source "
"address for the "
"dest:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, asoc, NIP6(&daddr->v6.sin6_addr));
}
sctp_read_unlock(addr_lock);
}
/* Make a copy of all potential local addresses. */ /* Make a copy of all potential local addresses. */
static void sctp_v6_copy_addrlist(struct list_head *addrlist, static void sctp_v6_copy_addrlist(struct list_head *addrlist,
struct net_device *dev) struct net_device *dev)
...@@ -257,10 +326,12 @@ static void sctp_v6_to_sk(union sctp_addr *addr, struct sock *sk) ...@@ -257,10 +326,12 @@ static void sctp_v6_to_sk(union sctp_addr *addr, struct sock *sk)
} }
/* Initialize a sctp_addr from a dst_entry. */ /* Initialize a sctp_addr from a dst_entry. */
static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst) static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
unsigned short port)
{ {
struct rt6_info *rt = (struct rt6_info *)dst; struct rt6_info *rt = (struct rt6_info *)dst;
addr->sa.sa_family = AF_INET6; addr->sa.sa_family = AF_INET6;
addr->v6.sin6_port = port;
ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr);
} }
...@@ -527,10 +598,11 @@ static struct inet6_protocol sctpv6_protocol = { ...@@ -527,10 +598,11 @@ static struct inet6_protocol sctpv6_protocol = {
}; };
static struct sctp_af sctp_ipv6_specific = { static struct sctp_af sctp_ipv6_specific = {
.queue_xmit = sctp_v6_xmit, .sctp_xmit = sctp_v6_xmit,
.setsockopt = ipv6_setsockopt, .setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt, .getsockopt = ipv6_getsockopt,
.get_dst = sctp_v6_get_dst, .get_dst = sctp_v6_get_dst,
.get_saddr = sctp_v6_get_saddr,
.copy_addrlist = sctp_v6_copy_addrlist, .copy_addrlist = sctp_v6_copy_addrlist,
.from_skb = sctp_v6_from_skb, .from_skb = sctp_v6_from_skb,
.from_sk = sctp_v6_from_sk, .from_sk = sctp_v6_from_sk,
...@@ -572,7 +644,7 @@ int sctp_v6_init(void) ...@@ -572,7 +644,7 @@ int sctp_v6_init(void)
/* Register the SCTP specfic AF_INET6 functions. */ /* Register the SCTP specfic AF_INET6 functions. */
sctp_register_af(&sctp_ipv6_specific); sctp_register_af(&sctp_ipv6_specific);
/* Register notifier for inet6 address additions/deletions. */ /* Register notifier for inet6 address additions/deletions. */
register_inet6addr_notifier(&sctp_inetaddr_notifier); register_inet6addr_notifier(&sctp_inetaddr_notifier);
return 0; return 0;
......
...@@ -54,6 +54,7 @@ SCTP_DBG_OBJCNT(assoc); ...@@ -54,6 +54,7 @@ SCTP_DBG_OBJCNT(assoc);
SCTP_DBG_OBJCNT(bind_addr); SCTP_DBG_OBJCNT(bind_addr);
SCTP_DBG_OBJCNT(chunk); SCTP_DBG_OBJCNT(chunk);
SCTP_DBG_OBJCNT(addr); SCTP_DBG_OBJCNT(addr);
SCTP_DBG_OBJCNT(ssnmap);
/* An array to make it easy to pretty print the debug information /* An array to make it easy to pretty print the debug information
* to the proc fs. * to the proc fs.
...@@ -66,6 +67,7 @@ sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = { ...@@ -66,6 +67,7 @@ sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
SCTP_DBG_OBJCNT_ENTRY(chunk), SCTP_DBG_OBJCNT_ENTRY(chunk),
SCTP_DBG_OBJCNT_ENTRY(bind_addr), SCTP_DBG_OBJCNT_ENTRY(bind_addr),
SCTP_DBG_OBJCNT_ENTRY(addr), SCTP_DBG_OBJCNT_ENTRY(addr),
SCTP_DBG_OBJCNT_ENTRY(ssnmap),
}; };
/* Callback from procfs to read out objcount information. /* Callback from procfs to read out objcount information.
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp. * Copyright (c) 2001-2003 International Business Machines, Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -62,7 +62,6 @@ ...@@ -62,7 +62,6 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Forward declarations for private helpers. */ /* Forward declarations for private helpers. */
__u32 count_crc(__u8 *ptr, __u16 count);
static void sctp_packet_reset(sctp_packet_t *packet); static void sctp_packet_reset(sctp_packet_t *packet);
static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
sctp_chunk_t *chunk); sctp_chunk_t *chunk);
...@@ -81,6 +80,7 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, ...@@ -81,6 +80,7 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet,
packet->ecn_capable = ecn_capable; packet->ecn_capable = ecn_capable;
packet->get_prepend_chunk = prepend_handler; packet->get_prepend_chunk = prepend_handler;
packet->has_cookie_echo = 0; packet->has_cookie_echo = 0;
packet->ipfragok = 0;
/* We might need to call the prepend_handler right away. */ /* We might need to call the prepend_handler right away. */
if (packet_empty) if (packet_empty)
...@@ -90,7 +90,7 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet, ...@@ -90,7 +90,7 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet,
/* Initialize the packet structure. */ /* Initialize the packet structure. */
sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, sctp_packet_t *sctp_packet_init(sctp_packet_t *packet,
sctp_transport_t *transport, struct sctp_transport *transport,
__u16 sport, __u16 sport,
__u16 dport) __u16 dport)
{ {
...@@ -102,6 +102,7 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *packet, ...@@ -102,6 +102,7 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *packet,
packet->ecn_capable = 0; packet->ecn_capable = 0;
packet->get_prepend_chunk = NULL; packet->get_prepend_chunk = NULL;
packet->has_cookie_echo = 0; packet->has_cookie_echo = 0;
packet->ipfragok = 0;
packet->malloced = 0; packet->malloced = 0;
sctp_packet_reset(packet); sctp_packet_reset(packet);
return packet; return packet;
...@@ -193,6 +194,7 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) ...@@ -193,6 +194,7 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
* transmit and rely on IP * transmit and rely on IP
* fragmentation. * fragmentation.
*/ */
packet->ipfragok = 1;
goto append; goto append;
} }
} else { /* !packet_empty */ } else { /* !packet_empty */
...@@ -228,13 +230,13 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk) ...@@ -228,13 +230,13 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
} }
/* All packets are sent to the network through this function from /* All packets are sent to the network through this function from
* sctp_push_outqueue(). * sctp_outq_tail().
* *
* The return value is a normal kernel error return value. * The return value is a normal kernel error return value.
*/ */
int sctp_packet_transmit(sctp_packet_t *packet) int sctp_packet_transmit(sctp_packet_t *packet)
{ {
sctp_transport_t *transport = packet->transport; struct sctp_transport *transport = packet->transport;
sctp_association_t *asoc = transport->asoc; sctp_association_t *asoc = transport->asoc;
struct sctphdr *sh; struct sctphdr *sh;
__u32 crc32; __u32 crc32;
...@@ -358,22 +360,14 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -358,22 +360,14 @@ int sctp_packet_transmit(sctp_packet_t *packet)
* Note: Adler-32 is no longer applicable, as has been replaced * Note: Adler-32 is no longer applicable, as has been replaced
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>. * by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/ */
crc32 = count_crc((__u8 *)sh, nskb->len); crc32 = sctp_start_cksum((__u8 *)sh, nskb->len);
crc32 = sctp_end_cksum(crc32);
/* 3) Put the resultant value into the checksum field in the /* 3) Put the resultant value into the checksum field in the
* common header, and leave the rest of the bits unchanged. * common header, and leave the rest of the bits unchanged.
*/ */
sh->checksum = htonl(crc32); sh->checksum = htonl(crc32);
/* FIXME: Delete the rest of this switch statement once phase 2
* of address selection (ipv6 support) drops in.
*/
switch (transport->ipaddr.sa.sa_family) {
case AF_INET6:
SCTP_V6(inet6_sk(sk)->daddr = transport->ipaddr.v6.sin6_addr;)
break;
};
/* IP layer ECN support /* IP layer ECN support
* From RFC 2481 * From RFC 2481
* "The ECN-Capable Transport (ECT) bit would be set by the * "The ECN-Capable Transport (ECT) bit would be set by the
...@@ -423,8 +417,10 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -423,8 +417,10 @@ int sctp_packet_transmit(sctp_packet_t *packet)
} }
dst = transport->dst; dst = transport->dst;
if (!dst || dst->obsolete) { /* The 'obsolete' field of dst is set to 2 when a dst is freed. */
if (!dst || (dst->obsolete > 1)) {
sctp_transport_route(transport, NULL, sctp_sk(sk)); sctp_transport_route(transport, NULL, sctp_sk(sk));
sctp_assoc_sync_pmtu(asoc);
} }
nskb->dst = dst_clone(transport->dst); nskb->dst = dst_clone(transport->dst);
...@@ -433,14 +429,22 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -433,14 +429,22 @@ int sctp_packet_transmit(sctp_packet_t *packet)
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n", SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
nskb->len); nskb->len);
(*transport->af_specific->queue_xmit)(nskb); (*transport->af_specific->sctp_xmit)(nskb, transport, packet->ipfragok);
out: out:
packet->size = SCTP_IP_OVERHEAD; packet->size = SCTP_IP_OVERHEAD;
return err; return err;
no_route: no_route:
kfree_skb(nskb); kfree_skb(nskb);
IP_INC_STATS_BH(IpOutNoRoutes); IP_INC_STATS_BH(IpOutNoRoutes);
err = -EHOSTUNREACH;
/* FIXME: Returning the 'err' will effect all the associations
* associated with a socket, although only one of the paths of the
* association is unreachable.
* The real failure of a transport or association can be passed on
* to the user via notifications. So setting this error may not be
* required.
*/
/* err = -EHOSTUNREACH; */
goto out; goto out;
} }
...@@ -473,7 +477,7 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, ...@@ -473,7 +477,7 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
{ {
sctp_xmit_t retval = SCTP_XMIT_OK; sctp_xmit_t retval = SCTP_XMIT_OK;
size_t datasize, rwnd, inflight; size_t datasize, rwnd, inflight;
sctp_transport_t *transport = packet->transport; struct sctp_transport *transport = packet->transport;
__u32 max_burst_bytes; __u32 max_burst_bytes;
/* RFC 2960 6.1 Transmission of DATA Chunks /* RFC 2960 6.1 Transmission of DATA Chunks
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
* 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-2002 International Business Machines Corp. * Copyright (c) 2001-2003 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
* These functions implement the outqueue class. The outqueue handles * These functions implement the sctp_outq class. The outqueue handles
* bundling and queueing of outgoing SCTP chunks. * bundling and queueing of outgoing SCTP chunks.
* *
* The SCTP reference implementation is free software; * The SCTP reference implementation is free software;
...@@ -47,39 +47,39 @@ ...@@ -47,39 +47,39 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/list.h> /* For struct list_head */ #include <linux/list.h> /* For struct list_head */
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <net/sock.h> /* For skb_set_owner_w */ #include <net/sock.h> /* For skb_set_owner_w */
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
/* Declare internal functions here. */ /* Declare internal functions here. */
static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn); static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn);
static void sctp_check_transmitted(sctp_outqueue_t *q, static void sctp_check_transmitted(struct sctp_outq *q,
struct list_head *transmitted_queue, struct list_head *transmitted_queue,
sctp_transport_t *transport, struct sctp_transport *transport,
sctp_sackhdr_t *sack, sctp_sackhdr_t *sack,
__u32 highest_new_tsn); __u32 highest_new_tsn);
/* Generate a new outqueue. */ /* Generate a new outqueue. */
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc) struct sctp_outq *sctp_outq_new(sctp_association_t *asoc)
{ {
sctp_outqueue_t *q; struct sctp_outq *q;
q = t_new(sctp_outqueue_t, GFP_KERNEL); q = t_new(struct sctp_outq, GFP_KERNEL);
if (q) { if (q) {
sctp_outqueue_init(asoc, q); sctp_outq_init(asoc, q);
q->malloced = 1; q->malloced = 1;
} }
return q; return q;
} }
/* Initialize an existing SCTP_outqueue. This does the boring stuff. /* Initialize an existing sctp_outq. This does the boring stuff.
* You still need to define handlers if you really want to DO * You still need to define handlers if you really want to DO
* something with this structure... * something with this structure...
*/ */
void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q) void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q)
{ {
q->asoc = asoc; q->asoc = asoc;
skb_queue_head_init(&q->out); skb_queue_head_init(&q->out);
...@@ -102,15 +102,15 @@ void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q) ...@@ -102,15 +102,15 @@ void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q)
/* Free the outqueue structure and any related pending chunks. /* Free the outqueue structure and any related pending chunks.
* FIXME: Add SEND_FAILED support. * FIXME: Add SEND_FAILED support.
*/ */
void sctp_outqueue_teardown(sctp_outqueue_t *q) void sctp_outq_teardown(struct sctp_outq *q)
{ {
sctp_transport_t *transport; struct sctp_transport *transport;
struct list_head *lchunk, *pos, *temp; struct list_head *lchunk, *pos, *temp;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
/* Throw away unacknowledged chunks. */ /* Throw away unacknowledged chunks. */
list_for_each(pos, &q->asoc->peer.transport_addr_list) { list_for_each(pos, &q->asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport, transports);
while ((lchunk = sctp_list_dequeue(&transport->transmitted))) { while ((lchunk = sctp_list_dequeue(&transport->transmitted))) {
chunk = list_entry(lchunk, sctp_chunk_t, chunk = list_entry(lchunk, sctp_chunk_t,
transmitted_list); transmitted_list);
...@@ -125,35 +125,39 @@ void sctp_outqueue_teardown(sctp_outqueue_t *q) ...@@ -125,35 +125,39 @@ void sctp_outqueue_teardown(sctp_outqueue_t *q)
sctp_free_chunk(chunk); sctp_free_chunk(chunk);
} }
/* Throw away any leftover chunks. */ /* Throw away any chunks in the retransmit queue. */
list_for_each_safe(lchunk, temp, &q->retransmit) {
list_del(lchunk);
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
sctp_free_chunk(chunk);
}
/* Throw away any leftover data chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out))) while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out)))
sctp_free_chunk(chunk); sctp_free_chunk(chunk);
/* Throw away any leftover control chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->control)))
sctp_free_chunk(chunk);
} }
/* Free the outqueue structure and any related pending chunks. */ /* Free the outqueue structure and any related pending chunks. */
void sctp_outqueue_free(sctp_outqueue_t *q) void sctp_outq_free(struct sctp_outq *q)
{ {
/* Throw away leftover chunks. */ /* Throw away leftover chunks. */
sctp_outqueue_teardown(q); sctp_outq_teardown(q);
/* If we were kmalloc()'d, free the memory. */ /* If we were kmalloc()'d, free the memory. */
if (q->malloced) if (q->malloced)
kfree(q); kfree(q);
} }
/* Transmit any pending partial chunks. */ /* Put a new chunk in an sctp_outq. */
void sctp_force_outqueue(sctp_outqueue_t *q) int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk)
{
/* Do we really need this? */
/* BUG */
}
/* Put a new chunk in an SCTP_outqueue. */
int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
{ {
int error = 0; int error = 0;
SCTP_DEBUG_PRINTK("sctp_push_outqueue(%p, %p[%s])\n", SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n",
q, chunk, chunk && chunk->chunk_hdr ? q, chunk, chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk"); : "Illegal Chunk");
...@@ -184,8 +188,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk) ...@@ -184,8 +188,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
default: default:
SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n", SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n",
q, chunk, q, chunk, chunk && chunk->chunk_hdr ?
chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk"); : "Illegal Chunk");
...@@ -193,13 +196,13 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk) ...@@ -193,13 +196,13 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
q->empty = 0; q->empty = 0;
break; break;
}; };
} else { } else
skb_queue_tail(&q->control, (struct sk_buff *) chunk); skb_queue_tail(&q->control, (struct sk_buff *) chunk);
}
if (error < 0) if (error < 0)
return error; return error;
error = sctp_flush_outqueue(q, 0); error = sctp_outq_flush(q, 0);
return error; return error;
} }
...@@ -207,7 +210,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk) ...@@ -207,7 +210,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
/* Insert a chunk into the retransmit queue. Chunks on the retransmit /* Insert a chunk into the retransmit queue. Chunks on the retransmit
* queue are kept in order, based on the TSNs. * queue are kept in order, based on the TSNs.
*/ */
void sctp_retransmit_insert(struct list_head *tlchunk, sctp_outqueue_t *q) void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q)
{ {
struct list_head *rlchunk; struct list_head *rlchunk;
sctp_chunk_t *tchunk, *rchunk; sctp_chunk_t *tchunk, *rchunk;
...@@ -230,15 +233,15 @@ void sctp_retransmit_insert(struct list_head *tlchunk, sctp_outqueue_t *q) ...@@ -230,15 +233,15 @@ void sctp_retransmit_insert(struct list_head *tlchunk, sctp_outqueue_t *q)
list_add_tail(tlchunk, &q->retransmit); list_add_tail(tlchunk, &q->retransmit);
} }
} }
/* Mark all the eligible packets on a transport for retransmission. */ /* Mark all the eligible packets on a transport for retransmission. */
void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport, void sctp_retransmit_mark(struct sctp_outq *q,
struct sctp_transport *transport,
__u8 fast_retransmit) __u8 fast_retransmit)
{ {
struct list_head *lchunk, *ltemp; struct list_head *lchunk, *ltemp;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
/* Walk through the specified transmitted queue. */ /* Walk through the specified transmitted queue. */
list_for_each_safe(lchunk, ltemp, &transport->transmitted) { list_for_each_safe(lchunk, ltemp, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
...@@ -246,8 +249,9 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport, ...@@ -246,8 +249,9 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
/* 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
* retransmission due to a timeout, only the chunks that are * retransmission due to a timeout or pmtu discovery, only the
* not yet acked should be added to the retransmit queue. * chunks that are not yet acked should be added to the
* retransmit queue.
*/ */
if ((fast_retransmit && chunk->fast_retransmit) || if ((fast_retransmit && chunk->fast_retransmit) ||
(!fast_retransmit && !chunk->tsn_gap_acked)) { (!fast_retransmit && !chunk->tsn_gap_acked)) {
...@@ -302,20 +306,27 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport, ...@@ -302,20 +306,27 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
/* Mark all the eligible packets on a transport for retransmission and force /* Mark all the eligible packets on a transport for retransmission and force
* one packet out. * one packet out.
*/ */
void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport, void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
__u8 fast_retransmit) sctp_retransmit_reason_t reason)
{ {
int error = 0; int error = 0;
__u8 fast_retransmit = 0;
if (fast_retransmit) { switch(reason) {
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX); case SCTP_RETRANSMIT_T3_RTX:
} else {
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX); sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX);
break;
case SCTP_RETRANSMIT_FAST_RTX:
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
fast_retransmit = 1;
break;
default:
break;
} }
sctp_retransmit_mark(q, transport, fast_retransmit); sctp_retransmit_mark(q, transport, fast_retransmit);
error = sctp_flush_outqueue(q, /* rtx_timeout */ 1); error = sctp_outq_flush(q, /* rtx_timeout */ 1);
if (error) if (error)
q->asoc->base.sk->err = -error; q->asoc->base.sk->err = -error;
...@@ -323,18 +334,18 @@ void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport, ...@@ -323,18 +334,18 @@ void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport,
/* /*
* Transmit DATA chunks on the retransmit queue. Upon return from * Transmit DATA chunks on the retransmit queue. Upon return from
* sctp_flush_retran_queue() the packet 'pkt' may contain chunks which * sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which
* need to be transmitted by the caller. * need to be transmitted by the caller.
* We assume that pkt->transport has already been set. * We assume that pkt->transport has already been set.
* *
* The return value is a normal kernel error return value. * The return value is a normal kernel error return value.
*/ */
static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt, static int sctp_outq_flush_rtx(struct sctp_outq *q, sctp_packet_t *pkt,
int rtx_timeout, int *start_timer) int rtx_timeout, int *start_timer)
{ {
struct list_head *lqueue; struct list_head *lqueue;
struct list_head *lchunk; struct list_head *lchunk;
sctp_transport_t *transport = pkt->transport; struct sctp_transport *transport = pkt->transport;
sctp_xmit_t status; sctp_xmit_t status;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -374,6 +385,18 @@ static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt, ...@@ -374,6 +385,18 @@ static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt,
continue; continue;
} }
#endif #endif
/* Make sure that Gap Acked TSNs are not retransmitted. A
* simple approach is just to move such TSNs out of the
* way and into a 'transmitted' queue and skip to the
* next chunk.
*/
if (chunk->tsn_gap_acked) {
list_add_tail(lchunk, &transport->transmitted);
lchunk = sctp_list_dequeue(lqueue);
continue;
}
/* Attempt to append this chunk to the packet. */ /* Attempt to append this chunk to the packet. */
status = (*q->append_output)(pkt, chunk); status = (*q->append_output)(pkt, chunk);
...@@ -427,10 +450,10 @@ static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt, ...@@ -427,10 +450,10 @@ static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt,
* queue. 'pos' points to the next chunk in the output queue after the * queue. 'pos' points to the next chunk in the output queue after the
* chunk that is currently in the process of fragmentation. * chunk that is currently in the process of fragmentation.
*/ */
void sctp_xmit_frag(sctp_outqueue_t *q, struct sk_buff *pos, void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos,
sctp_packet_t *packet, sctp_chunk_t *frag, __u32 tsn) sctp_packet_t *packet, sctp_chunk_t *frag, __u32 tsn)
{ {
sctp_transport_t *transport = packet->transport; struct sctp_transport *transport = packet->transport;
struct sk_buff_head *queue = &q->out; struct sk_buff_head *queue = &q->out;
sctp_xmit_t status; sctp_xmit_t status;
int error; int error;
...@@ -503,7 +526,7 @@ void sctp_xmit_frag(sctp_outqueue_t *q, struct sk_buff *pos, ...@@ -503,7 +526,7 @@ void sctp_xmit_frag(sctp_outqueue_t *q, struct sk_buff *pos,
* The argument 'frag' point to the first fragment and it holds the list * The argument 'frag' point to the first fragment and it holds the list
* of all the other fragments in the 'frag_list' field. * of all the other fragments in the 'frag_list' field.
*/ */
void sctp_xmit_fragmented_chunks(sctp_outqueue_t *q, sctp_packet_t *packet, void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet,
sctp_chunk_t *frag) sctp_chunk_t *frag)
{ {
sctp_association_t *asoc = frag->asoc; sctp_association_t *asoc = frag->asoc;
...@@ -548,6 +571,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -548,6 +571,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
sctp_chunk_t *first_frag, *frag; sctp_chunk_t *first_frag, *frag;
struct list_head *frag_list; struct list_head *frag_list;
int nfrags; int nfrags;
__u8 old_flags, flags;
/* nfrags = no. of max size fragments + any smaller last fragment. */ /* nfrags = no. of max size fragments + any smaller last fragment. */
nfrags = ((chunk_data_len / max_frag_data_len) + nfrags = ((chunk_data_len / max_frag_data_len) +
...@@ -556,13 +580,20 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -556,13 +580,20 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
/* Start of the data in the chunk. */ /* Start of the data in the chunk. */
data_ptr += sizeof(sctp_datahdr_t); data_ptr += sizeof(sctp_datahdr_t);
/* Are we fragmenting an already fragmented large message? */
old_flags = chunk->chunk_hdr->flags;
if (old_flags & SCTP_DATA_FIRST_FRAG)
flags = SCTP_DATA_FIRST_FRAG;
else
flags = SCTP_DATA_MIDDLE_FRAG;
/* Make the first fragment. */ /* Make the first fragment. */
first_frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len, first_frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
data_ptr, SCTP_DATA_FIRST_FRAG, ssn); data_ptr, flags, ssn);
if (!first_frag) if (!first_frag)
goto err; goto err;
first_frag->has_ssn = 1;
/* All the fragments are added to the frag_list of the first chunk. */ /* All the fragments are added to the frag_list of the first chunk. */
frag_list = &first_frag->frag_list; frag_list = &first_frag->frag_list;
...@@ -576,7 +607,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -576,7 +607,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
ssn); ssn);
if (!frag) if (!frag)
goto err; goto err;
frag->has_ssn = 1;
/* Add the middle fragment to the first fragment's /* Add the middle fragment to the first fragment's
* frag_list. * frag_list.
*/ */
...@@ -586,11 +617,17 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -586,11 +617,17 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
data_ptr += max_frag_data_len; data_ptr += max_frag_data_len;
} }
if (old_flags & SCTP_DATA_LAST_FRAG)
flags = SCTP_DATA_LAST_FRAG;
else
flags = SCTP_DATA_MIDDLE_FRAG;
/* Make the last fragment. */ /* Make the last fragment. */
frag = sctp_make_datafrag(asoc, sinfo, chunk_data_len, data_ptr, frag = sctp_make_datafrag(asoc, sinfo, chunk_data_len, data_ptr,
SCTP_DATA_LAST_FRAG, ssn); flags, ssn);
if (!frag) if (!frag)
goto err; goto err;
frag->has_ssn = 1;
/* Add the last fragment to the first fragment's frag_list. */ /* Add the last fragment to the first fragment's frag_list. */
list_add_tail(&frag->frag_list, frag_list); list_add_tail(&frag->frag_list, frag_list);
...@@ -620,7 +657,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -620,7 +657,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
} }
/* /*
* sctp_flush_outqueue - Try to flush an outqueue. * sctp_outq_flush - Try to flush an outqueue.
* *
* Description: Send everything in q which we legally can, subject to * Description: Send everything in q which we legally can, subject to
* congestion limitations. * congestion limitations.
...@@ -629,7 +666,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, ...@@ -629,7 +666,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
* locking concerns must be made. Today we use the sock lock to protect * locking concerns must be made. Today we use the sock lock to protect
* this function. * this function.
*/ */
int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
{ {
sctp_packet_t *packet; sctp_packet_t *packet;
sctp_packet_t singleton; sctp_packet_t singleton;
...@@ -642,13 +679,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -642,13 +679,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
sctp_packet_phandler_t *s_ecne_handler = NULL; sctp_packet_phandler_t *s_ecne_handler = NULL;
sctp_packet_phandler_t *ecne_handler = NULL; sctp_packet_phandler_t *ecne_handler = NULL;
struct sk_buff_head *queue; struct sk_buff_head *queue;
sctp_transport_t *transport = NULL; struct sctp_transport *transport = NULL;
sctp_transport_t *new_transport; struct sctp_transport *new_transport;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
sctp_xmit_t status; sctp_xmit_t status;
int error = 0; int error = 0;
int start_timer = 0; int start_timer = 0;
sctp_ulpevent_t *event;
/* These transports have chunks to send. */ /* These transports have chunks to send. */
struct list_head transport_list; struct list_head transport_list;
...@@ -783,10 +819,8 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -783,10 +819,8 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
(*q->config_output)(packet, vtag, (*q->config_output)(packet, vtag,
ecn_capable, ecne_handler); ecn_capable, ecne_handler);
retran: retran:
error = sctp_flush_retran_queue(q, error = sctp_outq_flush_rtx(q, packet,
packet, rtx_timeout, &start_timer);
rtx_timeout,
&start_timer);
if (start_timer) if (start_timer)
sctp_transport_reset_timers(transport); sctp_transport_reset_timers(transport);
...@@ -813,15 +847,14 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -813,15 +847,14 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/ */
if (chunk->sinfo.sinfo_stream >= if (chunk->sinfo.sinfo_stream >=
asoc->c.sinit_num_ostreams) { asoc->c.sinit_num_ostreams) {
struct sctp_ulpevent *ev;
/* Generate a SEND FAILED event. */ /* Generate a SEND FAILED event. */
event = sctp_ulpevent_make_send_failed(asoc, ev = sctp_ulpevent_make_send_failed(asoc,
chunk, SCTP_DATA_UNSENT, chunk, SCTP_DATA_UNSENT,
SCTP_ERROR_INV_STRM, SCTP_ERROR_INV_STRM, GFP_ATOMIC);
GFP_ATOMIC); if (ev)
if (event) { sctp_ulpq_tail_event(&asoc->ulpq, ev);
sctp_ulpqueue_tail_event(&asoc->ulpq,
event);
}
/* Free the chunk. This chunk is not on any /* Free the chunk. This chunk is not on any
* list yet, just free it. * list yet, just free it.
...@@ -830,6 +863,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -830,6 +863,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
continue; continue;
} }
/* Now do delayed assignment of SSN. This will
* probably change again when we start supporting
* large (> approximately 2^16) size messages.
*/
sctp_chunk_assign_ssn(chunk);
/* If there is a specified transport, use it. /* If there is a specified transport, use it.
* Otherwise, we want to use the active path. * Otherwise, we want to use the active path.
*/ */
...@@ -878,7 +917,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -878,7 +917,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
/* We could not append this chunk, so put /* We could not append this chunk, so put
* the chunk back on the output queue. * the chunk back on the output queue.
*/ */
SCTP_DEBUG_PRINTK("sctp_flush_outqueue: could " SCTP_DEBUG_PRINTK("sctp_outq_flush: could "
"not transmit TSN: 0x%x, status: %d\n", "not transmit TSN: 0x%x, status: %d\n",
ntohl(chunk->subh.data_hdr->tsn), ntohl(chunk->subh.data_hdr->tsn),
status); status);
...@@ -952,8 +991,9 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -952,8 +991,9 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
* --xguo * --xguo
*/ */
while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) { while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) {
sctp_transport_t *t = list_entry(ltransport, struct sctp_transport *t = list_entry(ltransport,
sctp_transport_t, send_ready); struct sctp_transport,
send_ready);
if (t != transport) if (t != transport)
transport = t; transport = t;
...@@ -966,12 +1006,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -966,12 +1006,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
} }
/* Set the various output handling callbacks. */ /* Set the various output handling callbacks. */
int sctp_outqueue_set_output_handlers(sctp_outqueue_t *q, int sctp_outq_set_output_handlers(struct sctp_outq *q,
sctp_outqueue_ohandler_init_t init, sctp_outq_ohandler_init_t init,
sctp_outqueue_ohandler_config_t config, sctp_outq_ohandler_config_t config,
sctp_outqueue_ohandler_t append, sctp_outq_ohandler_t append,
sctp_outqueue_ohandler_t build, sctp_outq_ohandler_t build,
sctp_outqueue_ohandler_force_t force) sctp_outq_ohandler_force_t force)
{ {
q->init_output = init; q->init_output = init;
q->config_output = config; q->config_output = config;
...@@ -1005,7 +1045,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack, ...@@ -1005,7 +1045,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
sctp_association_t *asoc) sctp_association_t *asoc)
{ {
struct list_head *ltransport, *lchunk; struct list_head *ltransport, *lchunk;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
__u32 highest_new_tsn, tsn; __u32 highest_new_tsn, tsn;
struct list_head *transport_list = &asoc->peer.transport_addr_list; struct list_head *transport_list = &asoc->peer.transport_addr_list;
...@@ -1013,7 +1053,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack, ...@@ -1013,7 +1053,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
highest_new_tsn = ntohl(sack->cum_tsn_ack); highest_new_tsn = ntohl(sack->cum_tsn_ack);
list_for_each(ltransport, transport_list) { list_for_each(ltransport, transport_list) {
transport = list_entry(ltransport, sctp_transport_t, transport = list_entry(ltransport, struct sctp_transport,
transports); transports);
list_for_each(lchunk, &transport->transmitted) { list_for_each(lchunk, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t, chunk = list_entry(lchunk, sctp_chunk_t,
...@@ -1028,17 +1068,17 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack, ...@@ -1028,17 +1068,17 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
} }
return highest_new_tsn; return highest_new_tsn;
} }
/* This is where we REALLY process a SACK. /* This is where we REALLY process a SACK.
* *
* Process the sack against the outqueue. Mostly, this just frees * Process the SACK against the outqueue. Mostly, this just frees
* things off the transmitted queue. * things off the transmitted queue.
*/ */
int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
{ {
sctp_association_t *asoc = q->asoc; sctp_association_t *asoc = q->asoc;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_chunk_t *tchunk; sctp_chunk_t *tchunk;
struct list_head *lchunk, *transport_list, *pos; struct list_head *lchunk, *transport_list, *pos;
sctp_sack_variable_t *frags = sack->variable; sctp_sack_variable_t *frags = sack->variable;
...@@ -1053,7 +1093,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1053,7 +1093,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
sack_ctsn = ntohl(sack->cum_tsn_ack); sack_ctsn = ntohl(sack->cum_tsn_ack);
/* Get the highest TSN in the sack. */ /* Get the highest TSN in the sack. */
highest_tsn = sack_ctsn + highest_tsn = sack_ctsn +
ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end); ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end);
if (TSN_lt(asoc->highest_sacked, highest_tsn)) { if (TSN_lt(asoc->highest_sacked, highest_tsn)) {
...@@ -1074,7 +1114,8 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1074,7 +1114,8 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
* This is a MASSIVE candidate for optimization. * This is a MASSIVE candidate for optimization.
*/ */
list_for_each(pos, transport_list) { list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport,
transports);
sctp_check_transmitted(q, &transport->transmitted, sctp_check_transmitted(q, &transport->transmitted,
transport, sack, highest_new_tsn); transport, sack, highest_new_tsn);
} }
...@@ -1127,7 +1168,8 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1127,7 +1168,8 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
goto finish; goto finish;
list_for_each(pos, transport_list) { list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport,
transports);
q->empty = q->empty && list_empty(&transport->transmitted); q->empty = q->empty && list_empty(&transport->transmitted);
if (!q->empty) if (!q->empty)
goto finish; goto finish;
...@@ -1139,7 +1181,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack) ...@@ -1139,7 +1181,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
} }
/* Is the outqueue empty? */ /* Is the outqueue empty? */
int sctp_outqueue_is_empty(const sctp_outqueue_t *q) int sctp_outq_is_empty(const struct sctp_outq *q)
{ {
return q->empty; return q->empty;
} }
...@@ -1161,9 +1203,9 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q) ...@@ -1161,9 +1203,9 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q)
* transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5. * transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5.
* KEPT TSN6-TSN7, etc. * KEPT TSN6-TSN7, etc.
*/ */
static void sctp_check_transmitted(sctp_outqueue_t *q, static void sctp_check_transmitted(struct sctp_outq *q,
struct list_head *transmitted_queue, struct list_head *transmitted_queue,
sctp_transport_t *transport, struct sctp_transport *transport,
sctp_sackhdr_t *sack, sctp_sackhdr_t *sack,
__u32 highest_new_tsn_in_sack) __u32 highest_new_tsn_in_sack)
{ {
...@@ -1494,7 +1536,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1494,7 +1536,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
if (transport) { if (transport) {
if (do_fast_retransmit) if (do_fast_retransmit)
sctp_retransmit(q, transport, do_fast_retransmit); sctp_retransmit(q, transport, SCTP_RETRANSMIT_FAST_RTX);
SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, " SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, "
"ssthresh: %d, flight_size: %d, pba: %d\n", "ssthresh: %d, flight_size: %d, pba: %d\n",
......
...@@ -203,23 +203,3 @@ DECLARE_PRIMITIVE(SEND); ...@@ -203,23 +203,3 @@ DECLARE_PRIMITIVE(SEND);
*/ */
DECLARE_PRIMITIVE(REQUESTHEARTBEAT); DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
/* COMMENT BUG. Find out where this is mentioned in the spec. */
int sctp_other_icmp_unreachfrag(sctp_association_t *asoc, void *arg)
{
int error = 0;
sctp_event_t event_type;
sctp_subtype_t subtype;
sctp_state_t state;
sctp_endpoint_t *ep;
event_type = SCTP_EVENT_T_OTHER;
subtype = SCTP_ST_OTHER(SCTP_EVENT_ICMP_UNREACHFRAG);
state = asoc ? asoc->state : SCTP_STATE_CLOSED;
ep = asoc ? asoc->ep : NULL;
error = sctp_do_sm(event_type, subtype, state, ep,
asoc, arg, GFP_ATOMIC);
return error;
}
...@@ -82,7 +82,7 @@ struct sock *sctp_get_ctl_sock(void) ...@@ -82,7 +82,7 @@ struct sock *sctp_get_ctl_sock(void)
} }
/* Set up the proc fs entry for the SCTP protocol. */ /* Set up the proc fs entry for the SCTP protocol. */
void sctp_proc_init(void) __init void sctp_proc_init(void)
{ {
if (!proc_net_sctp) { if (!proc_net_sctp) {
struct proc_dir_entry *ent; struct proc_dir_entry *ent;
...@@ -223,37 +223,6 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp, ...@@ -223,37 +223,6 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp,
return error; return error;
} }
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
struct dst_entry *sctp_v4_get_dst(union sctp_addr *daddr,
union sctp_addr *saddr)
{
struct rtable *rt;
struct flowi fl;
memset(&fl, 0x0, sizeof(struct flowi));
fl.fl4_dst = daddr->v4.sin_addr.s_addr;
fl.proto = IPPROTO_SCTP;
if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr;
SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
__FUNCTION__, NIPQUAD(fl.fl4_dst),
NIPQUAD(fl.fl4_src));
if (ip_route_output_key(&rt, &fl)) {
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return NULL;
}
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
NIPQUAD(rt->rt_src), NIPQUAD(rt->rt_dst));
return &rt->u.dst;
}
/* Initialize a sctp_addr from in incoming skb. */ /* Initialize a sctp_addr from in incoming skb. */
static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb, static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
int is_saddr) int is_saddr)
...@@ -292,10 +261,12 @@ static void sctp_v4_to_sk(union sctp_addr *addr, struct sock *sk) ...@@ -292,10 +261,12 @@ static void sctp_v4_to_sk(union sctp_addr *addr, struct sock *sk)
/* Initialize a sctp_addr from a dst_entry. */ /* Initialize a sctp_addr from a dst_entry. */
static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst) static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst,
unsigned short port)
{ {
struct rtable *rt = (struct rtable *)dst; struct rtable *rt = (struct rtable *)dst;
saddr->v4.sin_family = AF_INET; saddr->v4.sin_family = AF_INET;
saddr->v4.sin_port = port;
saddr->v4.sin_addr.s_addr = rt->rt_src; saddr->v4.sin_addr.s_addr = rt->rt_src;
} }
...@@ -394,6 +365,108 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) ...@@ -394,6 +365,108 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
return retval; return retval;
} }
/* Returns a valid dst cache entry for the given source and destination ip
* addresses. If an association is passed, trys to get a dst entry with a
* source adddress that matches an address in the bind address list.
*/
struct dst_entry *sctp_v4_get_dst(sctp_association_t *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr)
{
struct rtable *rt;
struct flowi fl;
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;
struct list_head *pos;
struct dst_entry *dst = NULL;
union sctp_addr dst_saddr;
memset(&fl, 0x0, sizeof(struct flowi));
fl.fl4_dst = daddr->v4.sin_addr.s_addr;
fl.proto = IPPROTO_SCTP;
if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr;
SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
__FUNCTION__, NIPQUAD(fl.fl4_dst),
NIPQUAD(fl.fl4_src));
if (!ip_route_output_key(&rt, &fl)) {
dst = &rt->u.dst;
}
/* If there is no association or if a source address is passed, no
* more validation is required.
*/
if (!asoc || saddr)
goto out;
bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
if (dst) {
/* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list,
list);
sctp_v4_dst_saddr(&dst_saddr, dst, bp->port);
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
}
sctp_read_unlock(addr_lock);
/* None of the bound addresses match the source address of the
* dst. So release it.
*/
dst_release(dst);
dst = NULL;
}
/* Walk through the bind address list and try to get a dst that
* matches a bind address as the source address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
if (AF_INET == laddr->a.sa.sa_family) {
fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
dst = sctp_v4_get_dst(asoc, daddr, &laddr->a);
if (!ip_route_output_key(&rt, &fl)) {
dst = &rt->u.dst;
goto out_unlock;
}
}
}
out_unlock:
sctp_read_unlock(addr_lock);
out:
if (dst)
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
NIPQUAD(rt->rt_dst), NIPQUAD(rt->rt_src));
else
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return dst;
}
/* For v4, the source address is cached in the route entry(dst). So no need
* to cache it separately and hence this is an empty routine.
*/
void sctp_v4_get_saddr(sctp_association_t *asoc,
struct dst_entry *dst,
union sctp_addr *daddr,
union sctp_addr *saddr)
{
}
/* Event handler for inet address addition/deletion events. /* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list. * Basically, whenever there is an event, we re-build our local address list.
*/ */
...@@ -545,6 +618,19 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) ...@@ -545,6 +618,19 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
return sctp_v4_available(addr); return sctp_v4_available(addr);
} }
/* Wrapper routine that calls the ip transmit routine. */
static inline int sctp_v4_xmit(struct sk_buff *skb,
struct sctp_transport *transport, int ipfragok)
{
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
"src:%u.%u.%u.%u, dst:%u.%u.%u.%u\n",
__FUNCTION__, skb, skb->len,
NIPQUAD(((struct rtable *)skb->dst)->rt_src),
NIPQUAD(((struct rtable *)skb->dst)->rt_dst));
return ip_queue_xmit(skb, ipfragok);
}
struct sctp_af sctp_ipv4_specific; struct sctp_af sctp_ipv4_specific;
static struct sctp_pf sctp_pf_inet = { static struct sctp_pf sctp_pf_inet = {
...@@ -601,10 +687,11 @@ static struct inet_protocol sctp_protocol = { ...@@ -601,10 +687,11 @@ static struct inet_protocol sctp_protocol = {
/* IPv4 address related functions. */ /* IPv4 address related functions. */
struct sctp_af sctp_ipv4_specific = { struct sctp_af sctp_ipv4_specific = {
.queue_xmit = ip_queue_xmit, .sctp_xmit = sctp_v4_xmit,
.setsockopt = ip_setsockopt, .setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt, .getsockopt = ip_getsockopt,
.get_dst = sctp_v4_get_dst, .get_dst = sctp_v4_get_dst,
.get_saddr = sctp_v4_get_saddr,
.copy_addrlist = sctp_v4_copy_addrlist, .copy_addrlist = sctp_v4_copy_addrlist,
.from_skb = sctp_v4_from_skb, .from_skb = sctp_v4_from_skb,
.from_sk = sctp_v4_from_sk, .from_sk = sctp_v4_from_sk,
...@@ -688,7 +775,7 @@ static void cleanup_sctp_mibs(void) ...@@ -688,7 +775,7 @@ static void cleanup_sctp_mibs(void)
} }
/* Initialize the universe into something sensible. */ /* Initialize the universe into something sensible. */
int sctp_init(void) __init int sctp_init(void)
{ {
int i; int i;
int status = 0; int status = 0;
...@@ -750,13 +837,9 @@ int sctp_init(void) ...@@ -750,13 +837,9 @@ int sctp_init(void)
/* Implementation specific variables. */ /* Implementation specific variables. */
/* Initialize default stream count setup information. /* Initialize default stream count setup information. */
* Note: today the stream accounting data structures are very sctp_proto.max_instreams = SCTP_DEFAULT_INSTREAMS;
* fixed size, so one really does need to make sure that these have sctp_proto.max_outstreams = SCTP_DEFAULT_OUTSTREAMS;
* upper/lower limits when changing.
*/
sctp_proto.max_instreams = SCTP_MAX_STREAM;
sctp_proto.max_outstreams = SCTP_MAX_STREAM;
/* Allocate and initialize the association hash table. */ /* Allocate and initialize the association hash table. */
sctp_proto.assoc_hashsize = 4096; sctp_proto.assoc_hashsize = 4096;
...@@ -829,6 +912,7 @@ int sctp_init(void) ...@@ -829,6 +912,7 @@ int sctp_init(void)
sctp_get_local_addr_list(&sctp_proto); sctp_get_local_addr_list(&sctp_proto);
__unsafe(THIS_MODULE);
return 0; return 0;
err_ctl_sock_init: err_ctl_sock_init:
...@@ -852,7 +936,7 @@ int sctp_init(void) ...@@ -852,7 +936,7 @@ int sctp_init(void)
} }
/* Exit handler for the SCTP protocol. */ /* Exit handler for the SCTP protocol. */
void sctp_exit(void) __exit void sctp_exit(void)
{ {
/* BUG. This should probably do something useful like clean /* BUG. This should probably do something useful like clean
* up all the remaining associations and all that memory. * up all the remaining associations and all that memory.
...@@ -889,4 +973,3 @@ module_exit(sctp_exit); ...@@ -889,4 +973,3 @@ module_exit(sctp_exit);
MODULE_AUTHOR("Linux Kernel SCTP developers <lksctp-developers@lists.sourceforge.net>"); MODULE_AUTHOR("Linux Kernel SCTP developers <lksctp-developers@lists.sourceforge.net>");
MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)"); MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -82,12 +82,12 @@ static const sctp_supported_addrs_param_t sat_param = { ...@@ -82,12 +82,12 @@ static const sctp_supported_addrs_param_t sat_param = {
/* gcc 3.2 doesn't allow initialization of zero-length arrays. So the above /* gcc 3.2 doesn't allow initialization of zero-length arrays. So the above
* structure is split and the address types array is initialized using a * structure is split and the address types array is initialized using a
* fixed length array. * fixed length array.
*/ */
static const __u16 sat_addr_types[2] = { static const __u16 sat_addr_types[2] = {
SCTP_PARAM_IPV4_ADDRESS, SCTP_PARAM_IPV4_ADDRESS,
SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,) SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,)
}; };
/* RFC 2960 3.3.2 Initiation (INIT) (1) /* RFC 2960 3.3.2 Initiation (INIT) (1)
* *
...@@ -116,8 +116,8 @@ void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code, ...@@ -116,8 +116,8 @@ void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code,
err.cause = cause_code; err.cause = cause_code;
len = sizeof(sctp_errhdr_t) + paylen; len = sizeof(sctp_errhdr_t) + paylen;
padlen = len % 4; padlen = len % 4;
len += padlen;
err.length = htons(len); err.length = htons(len);
len += padlen;
sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload); chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload);
} }
...@@ -242,35 +242,10 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, ...@@ -242,35 +242,10 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
sctp_cookie_param_t *cookie; sctp_cookie_param_t *cookie;
int cookie_len; int cookie_len;
size_t chunksize; size_t chunksize;
int error;
sctp_scope_t scope;
sctp_bind_addr_t *bp = NULL;
int flags;
retval = NULL; retval = NULL;
/* Build up the bind address list for the association based on addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, priority);
* info from the local endpoint and the remote peer.
*/
bp = sctp_bind_addr_new(priority);
if (!bp)
goto nomem_bindaddr;
/* Look for supported address types parameter and then build
* our address list based on that.
*/
scope = sctp_scope(&asoc->peer.active_path->ipaddr);
flags = (PF_INET6 == asoc->base.sk->family) ? SCTP_ADDR6_ALLOWED : 0;
if (asoc->peer.ipv4_address)
flags |= SCTP_ADDR4_PEERSUPP;
if (asoc->peer.ipv6_address)
flags |= SCTP_ADDR6_PEERSUPP;
error = sctp_bind_addr_copy(bp, &asoc->ep->base.bind_addr,
scope, priority, flags);
if (error)
goto nomem_copyaddr;
addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, priority);
if (!addrs.v) if (!addrs.v)
goto nomem_rawaddr; goto nomem_rawaddr;
...@@ -333,9 +308,6 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, ...@@ -333,9 +308,6 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
nomem_cookie: nomem_cookie:
kfree(addrs.v); kfree(addrs.v);
nomem_rawaddr: nomem_rawaddr:
nomem_copyaddr:
sctp_bind_addr_free(bp);
nomem_bindaddr:
return retval; return retval;
} }
...@@ -598,23 +570,9 @@ sctp_chunk_t *sctp_make_data_empty(sctp_association_t *asoc, ...@@ -598,23 +570,9 @@ sctp_chunk_t *sctp_make_data_empty(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo, const struct sctp_sndrcvinfo *sinfo,
int data_len) int data_len)
{ {
__u16 ssn;
__u8 flags = SCTP_DATA_NOT_FRAG; __u8 flags = SCTP_DATA_NOT_FRAG;
/* Sockets API Extensions for SCTP 5.2.2 return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0);
* MSG_UNORDERED - This flag requests the un-ordered delivery of the
* message. If this flag is clear, the datagram is considered an
* ordered send and a new ssn is generated. The flags field is set
* in the inner routine - sctp_make_datafrag_empty().
*/
if (sinfo->sinfo_flags & MSG_UNORDERED) {
ssn = 0;
} else {
ssn = __sctp_association_get_next_ssn(asoc,
sinfo->sinfo_stream);
}
return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn);
} }
/* Create a selective ackowledgement (SACK) for the given /* Create a selective ackowledgement (SACK) for the given
...@@ -714,6 +672,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc) ...@@ -714,6 +672,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc)
return retval; return retval;
} }
/* FIXME: Comments. */
sctp_chunk_t *sctp_make_shutdown(const sctp_association_t *asoc) sctp_chunk_t *sctp_make_shutdown(const sctp_association_t *asoc)
{ {
sctp_chunk_t *retval; sctp_chunk_t *retval;
...@@ -897,7 +856,7 @@ sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *asoc, ...@@ -897,7 +856,7 @@ sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *asoc,
/* Make a HEARTBEAT chunk. */ /* Make a HEARTBEAT chunk. */
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc, sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
const sctp_transport_t *transport, const struct sctp_transport *transport,
const void *payload, const size_t paylen) const void *payload, const size_t paylen)
{ {
sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT,
...@@ -909,7 +868,7 @@ sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc, ...@@ -909,7 +868,7 @@ sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
/* Cast away the 'const', as this is just telling the chunk /* Cast away the 'const', as this is just telling the chunk
* what transport it belongs to. * what transport it belongs to.
*/ */
retval->transport = (sctp_transport_t *) transport; retval->transport = (struct sctp_transport *) transport;
retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload); retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
nodata: nodata:
...@@ -1013,6 +972,7 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc, ...@@ -1013,6 +972,7 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc,
retval->asoc = (sctp_association_t *) asoc; retval->asoc = (sctp_association_t *) asoc;
retval->num_times_sent = 0; retval->num_times_sent = 0;
retval->has_tsn = 0; retval->has_tsn = 0;
retval->has_ssn = 0;
retval->rtt_in_progress = 0; retval->rtt_in_progress = 0;
retval->sent_at = jiffies; retval->sent_at = jiffies;
retval->singleton = 1; retval->singleton = 1;
...@@ -1118,53 +1078,6 @@ void sctp_free_chunk(sctp_chunk_t *chunk) ...@@ -1118,53 +1078,6 @@ void sctp_free_chunk(sctp_chunk_t *chunk)
SCTP_DBG_OBJCNT_DEC(chunk); SCTP_DBG_OBJCNT_DEC(chunk);
} }
/* Do a deep copy of a chunk. */
sctp_chunk_t *sctp_copy_chunk(sctp_chunk_t *chunk, const int priority)
{
sctp_chunk_t *retval;
long offset;
retval = t_new(sctp_chunk_t, priority);
if (!retval)
goto nodata;
/* Do the shallow copy. */
*retval = *chunk;
/* Make sure that the copy does NOT think it is on any lists. */
retval->next = NULL;
retval->prev = NULL;
retval->list = NULL;
INIT_LIST_HEAD(&retval->transmitted_list);
INIT_LIST_HEAD(&retval->frag_list);
/* Now we copy the deep structure. */
retval->skb = skb_copy(chunk->skb, priority);
if (!retval->skb) {
kfree(retval);
goto nodata;
}
/* Move the copy headers to point into the new skb. */
offset = ((__u8 *)retval->skb->head)
- ((__u8 *)chunk->skb->head);
if (retval->param_hdr.v)
retval->param_hdr.v += offset;
if (retval->subh.v)
retval->subh.v += offset;
if (retval->chunk_end)
((__u8 *) retval->chunk_end) += offset;
if (retval->chunk_hdr)
((__u8 *) retval->chunk_hdr) += offset;
if (retval->sctp_hdr)
((__u8 *) retval->sctp_hdr) += offset;
SCTP_DBG_OBJCNT_INC(chunk);
return retval;
nodata:
return NULL;
}
/* Append bytes to the end of a chunk. Will panic if chunk is not big /* Append bytes to the end of a chunk. Will panic if chunk is not big
* enough. * enough.
...@@ -1193,7 +1106,8 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data) ...@@ -1193,7 +1106,8 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data)
* chunk is not big enough. * chunk is not big enough.
* Returns a kernel err value. * Returns a kernel err value.
*/ */
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data) static int sctp_user_addto_chunk(sctp_chunk_t *chunk, int off, int len,
struct iovec *data)
{ {
__u8 *target; __u8 *target;
int err = 0; int err = 0;
...@@ -1202,7 +1116,7 @@ int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data) ...@@ -1202,7 +1116,7 @@ int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data)
target = skb_put(chunk->skb, len); target = skb_put(chunk->skb, len);
/* Copy data (whole iovec) into chunk */ /* Copy data (whole iovec) into chunk */
if ((err = memcpy_fromiovec(target, data, len))) if ((err = memcpy_fromiovecend(target, data, off, len)))
goto out; goto out;
/* Adjust the chunk length field. */ /* Adjust the chunk length field. */
...@@ -1214,6 +1128,152 @@ int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data) ...@@ -1214,6 +1128,152 @@ int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data)
return err; return err;
} }
/* A data chunk can have a maximum payload of (2^16 - 20). Break
* down any such message into smaller chunks. Opportunistically, fragment
* the chunks down to the current MTU constraints. We may get refragmented
* later if the PMTU changes, but it is _much better_ to fragment immediately
* with a reasonable guess than always doing our fragmentation on the
* soft-interrupt.
*/
int sctp_datachunks_from_user(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo,
struct msghdr *msg, int msg_len,
struct sk_buff_head *chunks)
{
int max, whole, i, offset, over, err;
int len, first_len;
sctp_chunk_t *chunk;
__u8 frag;
/* What is a reasonable fragmentation point right now? */
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;
first_len = max;
/* Encourage Cookie-ECHO bundling. */
if (asoc->state < SCTP_STATE_ESTABLISHED) {
whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
/* Account for the DATA to be bundled with the COOKIE-ECHO. */
if (whole) {
first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
msg_len -= first_len;
whole = 1;
}
}
/* How many full sized? How many bytes leftover? */
whole += msg_len / max;
over = msg_len % max;
offset = 0;
/* Create chunks for all the full sized DATA chunks. */
for (i=0, len=first_len; i < whole; i++) {
frag = SCTP_DATA_MIDDLE_FRAG;
if (0 == i)
frag |= SCTP_DATA_FIRST_FRAG;
if ((i == (whole - 1)) && !over)
frag |= SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
if (!chunk)
goto nomem;
err = sctp_user_addto_chunk(chunk, offset, len, msg->msg_iov);
if (err < 0)
goto errout;
offset += len;
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
__skb_queue_tail(chunks, (struct sk_buff *)chunk);
/* The first chunk, the first chunk was likely short
* to allow bundling, so reset to full size.
*/
if (0 == i)
len = max;
}
/* .. now the leftover bytes. */
if (over) {
if (!whole)
frag = SCTP_DATA_NOT_FRAG;
else
frag = SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
if (!chunk)
goto nomem;
err = sctp_user_addto_chunk(chunk, offset, over, msg->msg_iov);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
if (err < 0)
goto errout;
__skb_queue_tail(chunks, (struct sk_buff *)chunk);
}
err = 0;
goto out;
nomem:
err = -ENOMEM;
errout:
while ((chunk = (sctp_chunk_t *)__skb_dequeue(chunks)))
sctp_free_chunk(chunk);
out:
return err;
}
/* Helper function to assign a TSN if needed. This assumes that both
* the data_hdr and association have already been assigned.
*/
void sctp_chunk_assign_ssn(sctp_chunk_t *chunk)
{
__u16 ssn;
__u16 sid;
if (chunk->has_ssn)
return;
/* This is the last possible instant to assign a SSN. */
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
ssn = 0;
} else {
sid = htons(chunk->subh.data_hdr->stream);
if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid);
else
ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid);
ssn = htons(ssn);
}
chunk->subh.data_hdr->ssn = ssn;
chunk->has_ssn = 1;
}
/* Helper function to assign a TSN if needed. This assumes that both /* Helper function to assign a TSN if needed. This assumes that both
* the data_hdr and association have already been assigned. * the data_hdr and association have already been assigned.
*/ */
...@@ -1352,11 +1412,10 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1352,11 +1412,10 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
sctp_signed_cookie_t *cookie; sctp_signed_cookie_t *cookie;
sctp_cookie_t *bear_cookie; sctp_cookie_t *bear_cookie;
int headersize, bodysize; int headersize, bodysize;
int fixed_size, var_size1, var_size2, var_size3; int fixed_size;
__u8 digest_buf[SCTP_SIGNATURE_SIZE]; __u8 digest_buf[SCTP_SIGNATURE_SIZE];
int secret; int secret;
sctp_scope_t scope; sctp_scope_t scope;
__u8 *raw_addr_list;
headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE; headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE;
bodysize = ntohs(chunk->chunk_hdr->length) - headersize; bodysize = ntohs(chunk->chunk_hdr->length) - headersize;
...@@ -1377,9 +1436,6 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1377,9 +1436,6 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
/* Process the cookie. */ /* Process the cookie. */
cookie = chunk->subh.cookie_hdr; cookie = chunk->subh.cookie_hdr;
bear_cookie = &cookie->c; bear_cookie = &cookie->c;
var_size1 = ntohs(chunk->chunk_hdr->length) - fixed_size;
var_size2 = ntohs(bear_cookie->peer_init->chunk_hdr.length);
var_size3 = bear_cookie->raw_addr_list_len;
/* Check the signature. */ /* Check the signature. */
secret = ep->current_key; secret = ep->current_key;
...@@ -1403,6 +1459,7 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1403,6 +1459,7 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* for init collision case of lost COOKIE ACK. * for init collision case of lost COOKIE ACK.
*/ */
if (!asoc && tv_lt(bear_cookie->expiration, chunk->skb->stamp)) { if (!asoc && tv_lt(bear_cookie->expiration, chunk->skb->stamp)) {
__u16 len;
/* /*
* Section 3.3.10.3 Stale Cookie Error (3) * Section 3.3.10.3 Stale Cookie Error (3)
* *
...@@ -1411,8 +1468,8 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1411,8 +1468,8 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* Stale Cookie Error: Indicates the receipt of a valid State * Stale Cookie Error: Indicates the receipt of a valid State
* Cookie that has expired. * Cookie that has expired.
*/ */
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len = ntohs(chunk->chunk_hdr->length);
ntohs(chunk->chunk_hdr->length)); *err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
if (*err_chk_p) { if (*err_chk_p) {
suseconds_t usecs = (chunk->skb->stamp.tv_sec - suseconds_t usecs = (chunk->skb->stamp.tv_sec -
bear_cookie->expiration.tv_sec) * 1000000L + bear_cookie->expiration.tv_sec) * 1000000L +
...@@ -1443,12 +1500,8 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1443,12 +1500,8 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
/* Populate the association from the cookie. */ /* Populate the association from the cookie. */
retval->c = *bear_cookie; retval->c = *bear_cookie;
/* Build the bind address list based on the cookie. */ if (sctp_assoc_set_bind_addr_from_cookie(retval, bear_cookie,
raw_addr_list = (__u8 *) bear_cookie + GFP_ATOMIC) < 0) {
sizeof(sctp_cookie_t) + var_size2;
if (sctp_raw_to_bind_addrs(&retval->base.bind_addr, raw_addr_list,
var_size3, retval->base.bind_addr.port,
priority)) {
*error = -SCTP_IERROR_NOMEM; *error = -SCTP_IERROR_NOMEM;
goto fail; goto fail;
} }
...@@ -1477,6 +1530,57 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1477,6 +1530,57 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* 3rd Level Abstractions * 3rd Level Abstractions
********************************************************************/ ********************************************************************/
/*
* Report a missing mandatory parameter.
*/
struct __sctp_missing {
__u32 num_missing;
__u16 type;
} __attribute__((packed));;
static int sctp_process_missing_param(const sctp_association_t *asoc,
sctp_param_t paramtype,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
struct __sctp_missing report;
__u16 len;
len = WORD_ROUND(sizeof(report));
/* Make an ERROR chunk, preparing enough room for
* returning multiple unknown parameters.
*/
if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
if (*err_chk_p) {
report.num_missing = htonl(1);
report.type = paramtype;
sctp_init_cause(*err_chk_p, SCTP_ERROR_INV_PARAM,
&report, sizeof(report));
}
/* Stop processing this chunk. */
return 0;
}
/* Report an Invalid Mandatory Parameter. */
static int sctp_process_inv_mandatory(const sctp_association_t *asoc,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
/* Invalid Mandatory Parameter Error has no payload. */
if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, 0);
if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_INV_PARAM, NULL, 0);
/* Stop processing this chunk. */
return 0;
}
/* Do not attempt to handle the HOST_NAME parm. However, do /* Do not attempt to handle the HOST_NAME parm. However, do
* send back an indicator to the peer. * send back an indicator to the peer.
*/ */
...@@ -1487,9 +1591,7 @@ static int sctp_process_hn_param(const sctp_association_t *asoc, ...@@ -1487,9 +1591,7 @@ static int sctp_process_hn_param(const sctp_association_t *asoc,
{ {
__u16 len = ntohs(param.p->length); __u16 len = ntohs(param.p->length);
/* Make an ERROR chunk, preparing enough room for /* Make an ERROR chunk. */
* returning multiple unknown parameters.
*/
if (!*err_chk_p) if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len); *err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
...@@ -1570,7 +1672,8 @@ static int sctp_process_unk_param(const sctp_association_t *asoc, ...@@ -1570,7 +1672,8 @@ static int sctp_process_unk_param(const sctp_association_t *asoc,
} else { } else {
/* If there is no memory for generating the ERROR /* If there is no memory for generating the ERROR
* report as specified, an ABORT will be triggered * report as specified, an ABORT will be triggered
* to the peer and the association won't be established. * to the peer and the association won't be
* established.
*/ */
retval = 0; retval = 0;
} }
...@@ -1633,12 +1736,33 @@ int sctp_verify_init(const sctp_association_t *asoc, ...@@ -1633,12 +1736,33 @@ int sctp_verify_init(const sctp_association_t *asoc,
sctp_chunk_t **err_chk_p) sctp_chunk_t **err_chk_p)
{ {
union sctp_params param; union sctp_params param;
int has_cookie = 0;
/* Verify stream values are non-zero. */
if ((0 == peer_init->init_hdr.num_outbound_streams) ||
(0 == peer_init->init_hdr.num_inbound_streams)) {
sctp_process_inv_mandatory(asoc, chunk, err_chk_p);
return 0;
}
/* Check for missing mandatory parameters. */
sctp_walk_params(param, peer_init, init_hdr.params) {
/* FIXME - Verify the fixed fields of the INIT chunk. Also, verify if (SCTP_PARAM_STATE_COOKIE == param.p->type)
* the mandatory parameters somewhere here and generate either the has_cookie = 1;
* "Missing mandatory parameter" error or the "Invalid mandatory
* parameter" error. } /* for (loop through all parameters) */
/* The only missing mandatory param possible today is
* the state cookie for an INIT-ACK chunk.
*/ */
if ((SCTP_CID_INIT_ACK == cid) && !has_cookie) {
sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
chunk, err_chk_p);
return 0;
}
/* Find unrecognized parameters. */ /* Find unrecognized parameters. */
...@@ -1654,6 +1778,7 @@ int sctp_verify_init(const sctp_association_t *asoc, ...@@ -1654,6 +1778,7 @@ int sctp_verify_init(const sctp_association_t *asoc,
/* Unpack the parameters in an INIT packet into an association. /* Unpack the parameters in an INIT packet into an association.
* Returns 0 on failure, else success. * Returns 0 on failure, else success.
* FIXME: This is an association method.
*/ */
int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const union sctp_addr *peer_addr, const union sctp_addr *peer_addr,
...@@ -1661,7 +1786,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1661,7 +1786,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
int priority) int priority)
{ {
union sctp_params param; union sctp_params param;
sctp_transport_t *transport; struct sctp_transport *transport;
struct list_head *pos, *temp; struct list_head *pos, *temp;
char *cookie; char *cookie;
...@@ -1710,6 +1835,12 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1710,6 +1835,12 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
ntohs(peer_init->init_hdr.num_inbound_streams); ntohs(peer_init->init_hdr.num_inbound_streams);
} }
if (asoc->c.sinit_max_instreams >
ntohs(peer_init->init_hdr.num_outbound_streams)) {
asoc->c.sinit_max_instreams =
ntohs(peer_init->init_hdr.num_outbound_streams);
}
/* Copy Initiation tag from INIT to VT_peer in cookie. */ /* Copy Initiation tag from INIT to VT_peer in cookie. */
asoc->c.peer_vtag = asoc->peer.i.init_tag; asoc->c.peer_vtag = asoc->peer.i.init_tag;
...@@ -1730,7 +1861,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1730,7 +1861,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* advertised window). * advertised window).
*/ */
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport, transports);
transport->ssthresh = asoc->peer.i.a_rwnd; transport->ssthresh = asoc->peer.i.a_rwnd;
} }
...@@ -1738,6 +1869,21 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1738,6 +1869,21 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
asoc->peer.i.initial_tsn); asoc->peer.i.initial_tsn);
/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
*
* The stream sequence number in all the streams shall start
* from 0 when the association is established. Also, when the
* stream sequence number reaches the value 65535 the next
* stream sequence number shall be set to 0.
*/
/* Allocate storage for the negotiated streams. */
asoc->ssnmap = sctp_ssnmap_new(asoc->peer.i.num_outbound_streams,
asoc->c.sinit_num_ostreams,
priority);
if (!asoc->ssnmap)
goto nomem_ssnmap;
/* ADDIP Section 4.1 ASCONF Chunk Procedures /* ADDIP Section 4.1 ASCONF Chunk Procedures
* *
* When an endpoint has an ASCONF signaled change to be sent to the * When an endpoint has an ASCONF signaled change to be sent to the
...@@ -1751,10 +1897,11 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1751,10 +1897,11 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1; asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
return 1; return 1;
nomem_ssnmap:
clean_up: clean_up:
/* Release the transport structures. */ /* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports); transport = list_entry(pos, struct sctp_transport, transports);
list_del(pos); list_del(pos);
sctp_transport_free(transport); sctp_transport_free(transport);
} }
......
...@@ -56,33 +56,29 @@ ...@@ -56,33 +56,29 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Do forward declarations of static functions. */ /* Do forward declarations of static functions. */
static void sctp_do_ecn_ce_work(sctp_association_t *asoc, static void sctp_do_ecn_ce_work(sctp_association_t *,__u32 lowest_tsn);
__u32 lowest_tsn);
static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
__u32 lowest_tsn, __u32 lowest_tsn,
sctp_chunk_t *); sctp_chunk_t *);
static void sctp_do_ecn_cwr_work(sctp_association_t *asoc, static void sctp_do_ecn_cwr_work(sctp_association_t *,__u32 lowest_tsn);
__u32 lowest_tsn); static void sctp_do_8_2_transport_strike(sctp_association_t *,
struct sctp_transport *);
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *);
sctp_transport_t *transport); static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *,
static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *asoc); sctp_event_t, sctp_subtype_t,
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_event_t event_type, sctp_subtype_t stype,
sctp_chunk_t *chunk); sctp_chunk_t *chunk);
static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc, static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
int priority); int priority);
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *); static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *);
static void sctp_cmd_hb_timers_update(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *, sctp_association_t *);
sctp_transport_t *); static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *, sctp_association_t *,
static void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *, sctp_association_t *, struct sctp_transport *);
sctp_bind_addr_t *);
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *); struct sctp_transport *);
static void sctp_cmd_transport_on(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_transport_on(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *, sctp_chunk_t *); struct sctp_transport *, sctp_chunk_t *);
static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *, static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *,
sctp_sackhdr_t *); sctp_sackhdr_t *);
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *,
...@@ -264,7 +260,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -264,7 +260,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
struct list_head *pos; struct list_head *pos;
struct timer_list *timer; struct timer_list *timer;
unsigned long timeout; unsigned long timeout;
sctp_transport_t *t; struct sctp_transport *t;
sctp_sackhdr_t sackh; sctp_sackhdr_t sackh;
if(SCTP_EVENT_T_TIMEOUT != event_type) if(SCTP_EVENT_T_TIMEOUT != event_type)
...@@ -296,7 +292,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -296,7 +292,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_PURGE_OUTQUEUE: case SCTP_CMD_PURGE_OUTQUEUE:
sctp_outqueue_teardown(&asoc->outqueue); sctp_outq_teardown(&asoc->outqueue);
break; break;
case SCTP_CMD_DELETE_TCB: case SCTP_CMD_DELETE_TCB:
...@@ -395,9 +391,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -395,9 +391,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
command->obj.ptr, command->obj.ptr,
"ulpq:", "ulpq:",
&asoc->ulpq); &asoc->ulpq);
sctp_ulpqueue_tail_data(&asoc->ulpq, sctp_ulpq_tail_data(&asoc->ulpq,
command->obj.ptr, command->obj.ptr,
GFP_ATOMIC); GFP_ATOMIC);
break; break;
case SCTP_CMD_EVENT_ULP: case SCTP_CMD_EVENT_ULP:
...@@ -407,14 +403,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -407,14 +403,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
command->obj.ptr, command->obj.ptr,
"ulpq:", "ulpq:",
&asoc->ulpq); &asoc->ulpq);
sctp_ulpqueue_tail_event(&asoc->ulpq, sctp_ulpq_tail_event(&asoc->ulpq,
command->obj.ptr); command->obj.ptr);
break; break;
case SCTP_CMD_REPLY: case SCTP_CMD_REPLY:
/* Send a chunk to our peer. */ /* Send a chunk to our peer. */
error = sctp_push_outqueue(&asoc->outqueue, error = sctp_outq_tail(&asoc->outqueue,
command->obj.ptr); command->obj.ptr);
break; break;
case SCTP_CMD_SEND_PKT: case SCTP_CMD_SEND_PKT:
...@@ -427,12 +423,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -427,12 +423,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_RETRAN: case SCTP_CMD_RETRAN:
/* Mark a transport for retransmission. */ /* Mark a transport for retransmission. */
sctp_retransmit(&asoc->outqueue, sctp_retransmit(&asoc->outqueue,
command->obj.transport, 0); command->obj.transport,
SCTP_RETRANSMIT_T3_RTX);
break; break;
case SCTP_CMD_TRANSMIT: case SCTP_CMD_TRANSMIT:
/* Kick start transmission. */ /* Kick start transmission. */
error = sctp_flush_outqueue(&asoc->outqueue, 0); error = sctp_outq_flush(&asoc->outqueue, 0);
break; break;
case SCTP_CMD_ECN_CE: case SCTP_CMD_ECN_CE:
...@@ -502,7 +499,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -502,7 +499,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
* COOKIE-ECHO we need to resend. * COOKIE-ECHO we need to resend.
*/ */
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, t = list_entry(pos, struct sctp_transport,
transports); transports);
sctp_retransmit_mark(&asoc->outqueue, t, 0); sctp_retransmit_mark(&asoc->outqueue, t, 0);
} }
...@@ -547,11 +544,6 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -547,11 +544,6 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
SCTP_DEBUG_PRINTK("vtag mismatch!\n"); SCTP_DEBUG_PRINTK("vtag mismatch!\n");
break; break;
case SCTP_CMD_SET_BIND_ADDR:
sctp_cmd_set_bind_addrs(commands, asoc,
command->obj.bp);
break;
case SCTP_CMD_STRIKE: case SCTP_CMD_STRIKE:
/* Mark one strike against a transport. */ /* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc, sctp_do_8_2_transport_strike(asoc,
...@@ -572,9 +564,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -572,9 +564,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_hb_timers_start(commands, asoc); sctp_cmd_hb_timers_start(commands, asoc);
break; break;
case SCTP_CMD_HB_TIMERS_UPDATE: case SCTP_CMD_HB_TIMER_UPDATE:
t = command->obj.transport; t = command->obj.transport;
sctp_cmd_hb_timers_update(commands, asoc, t); sctp_cmd_hb_timer_update(commands, asoc, t);
break;
case SCTP_CMD_HB_TIMERS_STOP:
sctp_cmd_hb_timers_stop(commands, asoc);
break; break;
case SCTP_CMD_REPORT_ERROR: case SCTP_CMD_REPORT_ERROR:
...@@ -599,7 +595,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -599,7 +595,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_RTO_PENDING: case SCTP_CMD_RTO_PENDING:
t = command->obj.transport; t = command->obj.transport;
t->rto_pending = 1; t->rto_pending = 1;
break; break;
default: default:
...@@ -656,7 +652,7 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, ...@@ -656,7 +652,7 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
* recent than the last response. * recent than the last response.
*/ */
if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) { if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) {
sctp_transport_t *transport; struct sctp_transport *transport;
/* Find which transport's congestion variables /* Find which transport's congestion variables
* need to be adjusted. * need to be adjusted.
...@@ -743,7 +739,7 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands) ...@@ -743,7 +739,7 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands)
asoc->peer.sack_needed = 0; asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0; asoc->peer.next_dup_tsn = 0;
error = sctp_push_outqueue(&asoc->outqueue, sack); error = sctp_outq_tail(&asoc->outqueue, sack);
/* Stop the SACK timer. */ /* Stop the SACK timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
...@@ -802,7 +798,7 @@ void sctp_do_TSNdup(sctp_association_t *asoc, sctp_chunk_t *chunk, long gap) ...@@ -802,7 +798,7 @@ void sctp_do_TSNdup(sctp_association_t *asoc, sctp_chunk_t *chunk, long gap)
void sctp_generate_t3_rtx_event(unsigned long peer) void sctp_generate_t3_rtx_event(unsigned long peer)
{ {
int error; int error;
sctp_transport_t *transport = (sctp_transport_t *) peer; struct sctp_transport *transport = (struct sctp_transport *) peer;
sctp_association_t *asoc = transport->asoc; sctp_association_t *asoc = transport->asoc;
/* Check whether a task is in the sock. */ /* Check whether a task is in the sock. */
...@@ -917,7 +913,7 @@ void sctp_generate_autoclose_event(unsigned long data) ...@@ -917,7 +913,7 @@ void sctp_generate_autoclose_event(unsigned long data)
void sctp_generate_heartbeat_event(unsigned long data) void sctp_generate_heartbeat_event(unsigned long data)
{ {
int error = 0; int error = 0;
sctp_transport_t *transport = (sctp_transport_t *) data; struct sctp_transport *transport = (struct sctp_transport *) data;
sctp_association_t *asoc = transport->asoc; sctp_association_t *asoc = transport->asoc;
sctp_bh_lock_sock(asoc->base.sk); sctp_bh_lock_sock(asoc->base.sk);
...@@ -957,24 +953,16 @@ void sctp_generate_sack_event(unsigned long data) ...@@ -957,24 +953,16 @@ void sctp_generate_sack_event(unsigned long data)
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK);
} }
void sctp_generate_pmtu_raise_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_PMTU_RAISE);
}
sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
NULL, NULL,
sctp_generate_t1_cookie_event, sctp_generate_t1_cookie_event,
sctp_generate_t1_init_event, sctp_generate_t1_init_event,
sctp_generate_t2_shutdown_event, sctp_generate_t2_shutdown_event,
NULL, NULL,
NULL,
sctp_generate_t5_shutdown_guard_event, sctp_generate_t5_shutdown_guard_event,
sctp_generate_heartbeat_event, sctp_generate_heartbeat_event,
sctp_generate_sack_event, sctp_generate_sack_event,
sctp_generate_autoclose_event, sctp_generate_autoclose_event,
sctp_generate_pmtu_raise_event,
}; };
/******************************************************************** /********************************************************************
...@@ -997,7 +985,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { ...@@ -997,7 +985,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
* *
*/ */
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport) struct sctp_transport *transport)
{ {
/* The check for association's overall error counter exceeding the /* The check for association's overall error counter exceeding the
* threshold is done in the state function. * threshold is done in the state function.
...@@ -1095,7 +1083,7 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, ...@@ -1095,7 +1083,7 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT /* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT
* inside the cookie. In reality, this is only used for INIT-ACK processing * inside the cookie. In reality, this is only used for INIT-ACK processing
* since all other cases use "temporary" associations and can do all * since all other cases use "temporary" associations and can do all
* their work in statefuns directly. * their work in statefuns directly.
*/ */
static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
sctp_association_t *asoc, sctp_association_t *asoc,
...@@ -1125,7 +1113,7 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, ...@@ -1125,7 +1113,7 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc) sctp_association_t *asoc)
{ {
sctp_transport_t *t; struct sctp_transport *t;
struct list_head *pos; struct list_head *pos;
/* Start a heartbeat timer for each transport on the association. /* Start a heartbeat timer for each transport on the association.
...@@ -1133,45 +1121,43 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, ...@@ -1133,45 +1121,43 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
* the needed data structures go away. * the needed data structures go away.
*/ */
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports); t = list_entry(pos, struct sctp_transport, transports);
if (!mod_timer(&t->hb_timer,
t->hb_interval + t->rto + jiffies)) { if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
sctp_transport_hold(t); sctp_transport_hold(t);
}
} }
} }
/* Helper function to update the heartbeat timer. */ static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds,
static void sctp_cmd_hb_timers_update(sctp_cmd_seq_t *cmds, sctp_association_t *asoc)
sctp_association_t *asoc,
sctp_transport_t *t)
{ {
/* Update the heartbeat timer. */ struct sctp_transport *t;
if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies)) struct list_head *pos;
sctp_transport_hold(t);
}
/* Helper function to break out SCTP_CMD_SET_BIND_ADDR handling. */ /* Stop all heartbeat timers. */
void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_bind_addr_t *bp)
{
struct list_head *pos, *temp;
list_for_each_safe(pos, temp, &bp->address_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
list_del_init(pos); t = list_entry(pos, struct sctp_transport, transports);
list_add_tail(pos, &asoc->base.bind_addr.address_list); if (del_timer(&t->hb_timer))
sctp_transport_put(t);
} }
}
/* Free the temporary bind addr header, otherwise /* Helper function to update the heartbeat timer. */
* there will a memory leak. static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds,
*/ sctp_association_t *asoc,
sctp_bind_addr_free(bp); struct sctp_transport *t)
{
/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
sctp_transport_hold(t);
} }
/* Helper function to handle the reception of an HEARTBEAT ACK. */ /* Helper function to handle the reception of an HEARTBEAT ACK. */
static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc, sctp_association_t *asoc,
sctp_transport_t *t, sctp_chunk_t *chunk) struct sctp_transport *t,
sctp_chunk_t *chunk)
{ {
sctp_sender_hb_info_t *hbinfo; sctp_sender_hb_info_t *hbinfo;
...@@ -1203,7 +1189,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, ...@@ -1203,7 +1189,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
*/ */
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc, sctp_association_t *asoc,
sctp_transport_t *t) struct sctp_transport *t)
{ {
sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
...@@ -1218,7 +1204,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, ...@@ -1218,7 +1204,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
{ {
int err; int err;
if (sctp_sack_outqueue(&asoc->outqueue, sackh)) { if (sctp_outq_sack(&asoc->outqueue, sackh)) {
/* There are no more TSNs awaiting SACK. */ /* There are no more TSNs awaiting SACK. */
err = sctp_do_sm(SCTP_EVENT_T_OTHER, err = sctp_do_sm(SCTP_EVENT_T_OTHER,
SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN), SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN),
...@@ -1228,7 +1214,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, ...@@ -1228,7 +1214,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
/* Windows may have opened, so we need /* Windows may have opened, so we need
* to check if we have DATA to transmit * to check if we have DATA to transmit
*/ */
err = sctp_flush_outqueue(&asoc->outqueue, 0); err = sctp_outq_flush(&asoc->outqueue, 0);
} }
return err; return err;
...@@ -1240,7 +1226,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, ...@@ -1240,7 +1226,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_chunk_t *chunk) sctp_chunk_t *chunk)
{ {
sctp_transport_t *t; struct sctp_transport *t;
t = sctp_assoc_choose_shutdown_transport(asoc); t = sctp_assoc_choose_shutdown_transport(asoc);
asoc->shutdown_last_sent_to = t; asoc->shutdown_last_sent_to = t;
......
...@@ -191,7 +191,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -191,7 +191,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
int len; int len;
/* If the packet is an OOTB packet which is temporarily on the /* If the packet is an OOTB packet which is temporarily on the
* control endpoint, responding with an ABORT. * control endpoint, respond with an ABORT.
*/ */
if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
return sctp_sf_ootb(ep, asoc, type, arg, commands); return sctp_sf_ootb(ep, asoc, type, arg, commands);
...@@ -262,6 +262,9 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -262,6 +262,9 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
len = ntohs(err_chunk->chunk_hdr->length) - len = ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t); sizeof(sctp_chunkhdr_t);
if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
goto nomem_ack;
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len); repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
if (!repl) if (!repl)
goto nomem_ack; goto nomem_ack;
...@@ -506,7 +509,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep, ...@@ -506,7 +509,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
sctp_chunk_t *err_chk_p; sctp_chunk_t *err_chk_p;
/* If the packet is an OOTB packet which is temporarily on the /* If the packet is an OOTB packet which is temporarily on the
* control endpoint, responding with an ABORT. * control endpoint, respond with an ABORT.
*/ */
if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
return sctp_sf_ootb(ep, asoc, type, arg, commands); return sctp_sf_ootb(ep, asoc, type, arg, commands);
...@@ -678,7 +681,7 @@ sctp_disposition_t sctp_sf_heartbeat(const sctp_endpoint_t *ep, ...@@ -678,7 +681,7 @@ sctp_disposition_t sctp_sf_heartbeat(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_transport_t *transport = (sctp_transport_t *) arg; struct sctp_transport *transport = (struct sctp_transport *) arg;
sctp_chunk_t *reply; sctp_chunk_t *reply;
sctp_sender_hb_info_t hbinfo; sctp_sender_hb_info_t hbinfo;
size_t paylen = 0; size_t paylen = 0;
...@@ -711,7 +714,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, ...@@ -711,7 +714,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_transport_t *transport = (sctp_transport_t *) arg; struct sctp_transport *transport = (struct sctp_transport *) arg;
if (asoc->overall_error_count >= asoc->overall_error_threshold) { if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
...@@ -737,7 +740,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, ...@@ -737,7 +740,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET, sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
SCTP_TRANSPORT(transport)); SCTP_TRANSPORT(transport));
} }
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_UPDATE, sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMER_UPDATE,
SCTP_TRANSPORT(transport)); SCTP_TRANSPORT(transport));
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
...@@ -842,7 +845,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep, ...@@ -842,7 +845,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
{ {
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
union sctp_addr from_addr; union sctp_addr from_addr;
sctp_transport_t *link; struct sctp_transport *link;
sctp_sender_hb_info_t *hbinfo; sctp_sender_hb_info_t *hbinfo;
unsigned long max_interval; unsigned long max_interval;
...@@ -944,7 +947,7 @@ static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc, ...@@ -944,7 +947,7 @@ static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc,
sctp_chunk_t *init, sctp_chunk_t *init,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_transport_t *new_addr, *addr; struct sctp_transport *new_addr, *addr;
struct list_head *pos, *pos2; struct list_head *pos, *pos2;
int found; int found;
...@@ -963,10 +966,11 @@ static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc, ...@@ -963,10 +966,11 @@ static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc,
found = 0; found = 0;
list_for_each(pos, &new_asoc->peer.transport_addr_list) { list_for_each(pos, &new_asoc->peer.transport_addr_list) {
new_addr = list_entry(pos, sctp_transport_t, transports); new_addr = list_entry(pos, struct sctp_transport, transports);
found = 0; found = 0;
list_for_each(pos2, &asoc->peer.transport_addr_list) { list_for_each(pos2, &asoc->peer.transport_addr_list) {
addr = list_entry(pos2, sctp_transport_t, transports); addr = list_entry(pos2, struct sctp_transport,
transports);
if (sctp_cmp_addr_exact(&new_addr->ipaddr, if (sctp_cmp_addr_exact(&new_addr->ipaddr,
&addr->ipaddr)) { &addr->ipaddr)) {
found = 1; found = 1;
...@@ -1048,20 +1052,17 @@ static char sctp_tietags_compare(sctp_association_t *new_asoc, ...@@ -1048,20 +1052,17 @@ static char sctp_tietags_compare(sctp_association_t *new_asoc,
(asoc->c.peer_vtag == new_asoc->c.peer_ttag)) (asoc->c.peer_vtag == new_asoc->c.peer_ttag))
return 'A'; return 'A';
/* Collision case D.
* Note: Test case D first, otherwise it may be incorrectly
* identified as second case of B if the value of the Tie_tag is
* not filled into the state cookie.
*/
if ((asoc->c.my_vtag == new_asoc->c.my_vtag) &&
(asoc->c.peer_vtag == new_asoc->c.peer_vtag))
return 'D';
/* Collision case B. */ /* Collision case B. */
if ((asoc->c.my_vtag == new_asoc->c.my_vtag) && if ((asoc->c.my_vtag == new_asoc->c.my_vtag) &&
((asoc->c.peer_vtag != new_asoc->c.peer_vtag) || ((asoc->c.peer_vtag != new_asoc->c.peer_vtag) ||
(!new_asoc->c.my_ttag && !new_asoc->c.peer_ttag))) (0 == asoc->c.peer_vtag))) {
return 'B'; return 'B';
}
/* Collision case D. */
if ((asoc->c.my_vtag == new_asoc->c.my_vtag) &&
(asoc->c.peer_vtag == new_asoc->c.peer_vtag))
return 'D';
/* Collision case C. */ /* Collision case C. */
if ((asoc->c.my_vtag != new_asoc->c.my_vtag) && if ((asoc->c.my_vtag != new_asoc->c.my_vtag) &&
...@@ -1070,7 +1071,8 @@ static char sctp_tietags_compare(sctp_association_t *new_asoc, ...@@ -1070,7 +1071,8 @@ static char sctp_tietags_compare(sctp_association_t *new_asoc,
(0 == new_asoc->c.peer_ttag)) (0 == new_asoc->c.peer_ttag))
return 'C'; return 'C';
return 'E'; /* No such case available. */ /* No match to any of the special cases; discard this packet. */
return 'E';
} }
/* Common helper routine for both duplicate and simulataneous INIT /* Common helper routine for both duplicate and simulataneous INIT
...@@ -1182,6 +1184,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -1182,6 +1184,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
len = ntohs(err_chunk->chunk_hdr->length) - len = ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t); sizeof(sctp_chunkhdr_t);
} }
if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
goto nomem;
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len); repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
if (!repl) if (!repl)
goto nomem; goto nomem;
...@@ -1337,7 +1343,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const sctp_endpoint_t *ep, ...@@ -1337,7 +1343,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const sctp_endpoint_t *ep,
/* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A') /* Unexpected COOKIE-ECHO handler for peer restart (Table 2, action 'A')
* *
* Section 5.2.4 * Section 5.2.4
* A) In this case, the peer may have restarted. * A) In this case, the peer may have restarted.
...@@ -1500,11 +1506,17 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep, ...@@ -1500,11 +1506,17 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep,
sctp_ulpevent_t *ev = NULL; sctp_ulpevent_t *ev = NULL;
sctp_chunk_t *repl; sctp_chunk_t *repl;
/* The local endpoint cannot use any value from the received /* Clarification from Implementor's Guide:
* state cookie and need to immediately resend a COOKIE-ACK * D) When both local and remote tags match the endpoint should
* and move into ESTABLISHED if it hasn't done so. * enter the ESTABLISHED state, if it is in the COOKIE-ECHOED state.
* It should stop any cookie timer that may be running and send
* a COOKIE ACK.
*/ */
if (SCTP_STATE_ESTABLISHED != asoc->state) {
/* Don't accidentally move back into established state. */
if (asoc->state < SCTP_STATE_ESTABLISHED) {
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_ESTABLISHED)); SCTP_STATE(SCTP_STATE_ESTABLISHED));
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START,
...@@ -1528,13 +1540,14 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep, ...@@ -1528,13 +1540,14 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep,
SCTP_ULPEVENT(ev)); SCTP_ULPEVENT(ev));
} }
sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
repl = sctp_make_cookie_ack(new_asoc, chunk); repl = sctp_make_cookie_ack(new_asoc, chunk);
if (!repl) if (!repl)
goto nomem; goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL());
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem: nomem:
...@@ -1605,8 +1618,6 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep, ...@@ -1605,8 +1618,6 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
sctp_send_stale_cookie_err(ep, asoc, chunk, commands, sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
err_chk_p); err_chk_p);
return sctp_sf_pdiscard(ep, asoc, type, arg, commands); return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
break;
case -SCTP_IERROR_BAD_SIG: case -SCTP_IERROR_BAD_SIG:
default: default:
return sctp_sf_pdiscard(ep, asoc, type, arg, commands); return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
...@@ -1629,7 +1640,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep, ...@@ -1629,7 +1640,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
new_asoc); new_asoc);
break; break;
case 'C': /* Collisioun case C. */ case 'C': /* Collision case C. */
retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands, retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands,
new_asoc); new_asoc);
break; break;
...@@ -1639,9 +1650,8 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep, ...@@ -1639,9 +1650,8 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
new_asoc); new_asoc);
break; break;
default: /* No such case, discard it. */ default: /* Discard packet for all others. */
printk(KERN_WARNING "%s:unknown case\n", __FUNCTION__); retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands);
retval = SCTP_DISPOSITION_DISCARD;
break; break;
}; };
...@@ -1799,7 +1809,7 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep, ...@@ -1799,7 +1809,7 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep,
sctp_cookie_preserve_param_t bht; sctp_cookie_preserve_param_t bht;
sctp_errhdr_t *err; sctp_errhdr_t *err;
struct list_head *pos; struct list_head *pos;
sctp_transport_t *t; struct sctp_transport *t;
sctp_chunk_t *reply; sctp_chunk_t *reply;
sctp_bind_addr_t *bp; sctp_bind_addr_t *bp;
int attempts; int attempts;
...@@ -1848,9 +1858,11 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep, ...@@ -1848,9 +1858,11 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC, sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC,
SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR)); SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
/* If we've sent any data bundled with COOKIE-ECHO we need to resend. */ /* If we've sent any data bundled with COOKIE-ECHO we need to
* resend.
*/
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports); t = list_entry(pos, struct sctp_transport, transports);
sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(t)); sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(t));
} }
...@@ -2030,7 +2042,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const sctp_endpoint_t *ep, ...@@ -2030,7 +2042,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const sctp_endpoint_t *ep,
SCTP_STATE(SCTP_STATE_SHUTDOWN_RECEIVED)); SCTP_STATE(SCTP_STATE_SHUTDOWN_RECEIVED));
disposition = SCTP_DISPOSITION_CONSUME; disposition = SCTP_DISPOSITION_CONSUME;
if (sctp_outqueue_is_empty(&asoc->outqueue)) { if (sctp_outq_is_empty(&asoc->outqueue)) {
disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type, disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type,
arg, commands); arg, commands);
} }
...@@ -3229,10 +3241,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep, ...@@ -3229,10 +3241,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_bind_addr_t *bp;
sctp_scope_t scope;
int error;
int flags;
/* The comment below says that we enter COOKIE-WAIT AFTER /* The comment below says that we enter COOKIE-WAIT AFTER
* sending the INIT, but that doesn't actually work in our * sending the INIT, but that doesn't actually work in our
...@@ -3241,35 +3249,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep, ...@@ -3241,35 +3249,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_COOKIE_WAIT)); SCTP_STATE(SCTP_STATE_COOKIE_WAIT));
/* Build up the bind address list for the association based on
* info from the local endpoint and the remote peer.
*/
bp = sctp_bind_addr_new(GFP_ATOMIC);
if (!bp)
goto nomem;
/* Use scoping rules to determine the subset of addresses from
* the endpoint.
*/
scope = sctp_scope(&asoc->peer.active_path->ipaddr);
flags = (PF_INET6 == asoc->base.sk->family) ? SCTP_ADDR6_ALLOWED : 0;
if (asoc->peer.ipv4_address)
flags |= SCTP_ADDR4_PEERSUPP;
if (asoc->peer.ipv6_address)
flags |= SCTP_ADDR6_PEERSUPP;
error = sctp_bind_addr_copy(bp, &ep->base.bind_addr, scope,
GFP_ATOMIC, flags);
if (error)
goto nomem;
/* FIXME: Either move address assignment out of this function
* or else move the association allocation/init into this function.
* The association structure is brand new before calling this
* function, so would not be a sideeffect if the allocation
* moved into this function. --jgrimm
*/
sctp_add_cmd_sf(commands, SCTP_CMD_SET_BIND_ADDR, (sctp_arg_t) bp);
/* RFC 2960 5.1 Normal Establishment of an Association /* RFC 2960 5.1 Normal Establishment of an Association
* *
* A) "A" first sends an INIT chunk to "Z". In the INIT, "A" * A) "A" first sends an INIT chunk to "Z". In the INIT, "A"
...@@ -3278,7 +3257,7 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep, ...@@ -3278,7 +3257,7 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
* 1 to 4294967295 (see 5.3.1 for Tag value selection). ... * 1 to 4294967295 (see 5.3.1 for Tag value selection). ...
*/ */
repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0); repl = sctp_make_init(asoc, &asoc->base.bind_addr, GFP_ATOMIC, 0);
if (!repl) if (!repl)
goto nomem; goto nomem;
...@@ -3297,9 +3276,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep, ...@@ -3297,9 +3276,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem: nomem:
if (bp)
sctp_bind_addr_free(bp);
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -3429,7 +3405,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(const sctp_endpoint_t *ep, ...@@ -3429,7 +3405,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(const sctp_endpoint_t *ep,
SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
disposition = SCTP_DISPOSITION_CONSUME; disposition = SCTP_DISPOSITION_CONSUME;
if (sctp_outqueue_is_empty(&asoc->outqueue)) { if (sctp_outq_is_empty(&asoc->outqueue)) {
disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type, disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
arg, commands); arg, commands);
} }
...@@ -3767,7 +3743,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( ...@@ -3767,7 +3743,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
return sctp_sf_heartbeat(ep, asoc, type, (sctp_transport_t *)arg, return sctp_sf_heartbeat(ep, asoc, type, (struct sctp_transport *)arg,
commands); commands);
} }
...@@ -3837,6 +3813,13 @@ sctp_disposition_t sctp_sf_do_9_2_start_shutdown(const sctp_endpoint_t *ep, ...@@ -3837,6 +3813,13 @@ sctp_disposition_t sctp_sf_do_9_2_start_shutdown(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_SHUTDOWN_SENT)); SCTP_STATE(SCTP_STATE_SHUTDOWN_SENT));
/* sctp-implguide 2.10 Issues with Heartbeating and failover
*
* HEARTBEAT ... is discontinued after sending either SHUTDOWN
* or SHUTDOWN-ACK.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
...@@ -3889,6 +3872,14 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(const sctp_endpoint_t *ep, ...@@ -3889,6 +3872,14 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(const sctp_endpoint_t *ep,
/* Enter the SHUTDOWN-ACK-SENT state. */ /* Enter the SHUTDOWN-ACK-SENT state. */
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_SHUTDOWN_ACK_SENT)); SCTP_STATE(SCTP_STATE_SHUTDOWN_ACK_SENT));
/* sctp-implguide 2.10 Issues with Heartbeating and failover
*
* HEARTBEAT ... is discontinued after sending either SHUTDOWN
* or SHUTDOWN-ACK.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
...@@ -3933,7 +3924,7 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const sctp_endpoint_t *ep, ...@@ -3933,7 +3924,7 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_transport_t *transport = arg; struct sctp_transport *transport = arg;
if (asoc->overall_error_count >= asoc->overall_error_threshold) { if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
...@@ -4203,7 +4194,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(const sctp_endpoint_t *ep, ...@@ -4203,7 +4194,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
disposition = SCTP_DISPOSITION_CONSUME; disposition = SCTP_DISPOSITION_CONSUME;
if (sctp_outqueue_is_empty(&asoc->outqueue)) { if (sctp_outq_is_empty(&asoc->outqueue)) {
disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type, disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
arg, commands); arg, commands);
} }
...@@ -4333,7 +4324,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, ...@@ -4333,7 +4324,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
const sctp_chunk_t *chunk) const sctp_chunk_t *chunk)
{ {
sctp_packet_t *packet; sctp_packet_t *packet;
sctp_transport_t *transport; struct sctp_transport *transport;
__u16 sport; __u16 sport;
__u16 dport; __u16 dport;
__u32 vtag; __u32 vtag;
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp. * 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.
* *
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
sctp_sm_table_entry_t bug = { static sctp_sm_table_entry_t bug = {
.fn = sctp_sf_bug, .fn = sctp_sf_bug,
.name = "sctp_sf_bug" .name = "sctp_sf_bug"
}; };
...@@ -206,7 +206,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, ...@@ -206,7 +206,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* SCTP_STATE_COOKIE_WAIT */ \ /* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \ {.fn = sctp_sf_violation, .name = "sctp_sf_violation"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \ /* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \ /* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \ {.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \ /* SCTP_STATE_SHUTDOWN_PENDING */ \
...@@ -216,7 +216,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, ...@@ -216,7 +216,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \ /* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \ {.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_HEARTBEAT_ACK */ } /* TYPE_SCTP_HEARTBEAT_ACK */
#define TYPE_SCTP_ABORT { \ #define TYPE_SCTP_ABORT { \
...@@ -293,19 +293,19 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, ...@@ -293,19 +293,19 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* SCTP_STATE_CLOSED */ \ /* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \ {.fn = sctp_sf_tabort_8_4_8, .name = "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \ /* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \ /* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_cookie_echoed_err, .name = "sctp_sf_cookie_echoed_err"}, \ {.fn = sctp_sf_cookie_echoed_err, .name = "sctp_sf_cookie_echoed_err"}, \
/* SCTP_STATE_ESTABLISHED */ \ /* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \ {.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \ /* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \ /* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \ /* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_ERROR */ } /* TYPE_SCTP_ERROR */
#define TYPE_SCTP_COOKIE_ECHO { \ #define TYPE_SCTP_COOKIE_ECHO { \
...@@ -504,26 +504,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { ...@@ -504,26 +504,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"}, {.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
}; /* chunk unknown */ }; /* chunk unknown */
#define TYPE_SCTP_PRIMITIVE_INITIALIZE { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_INITIALIZE */
#define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \ #define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \
/* SCTP_STATE_EMPTY */ \ /* SCTP_STATE_EMPTY */ \
...@@ -619,90 +599,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { ...@@ -619,90 +599,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
{.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \ {.fn = sctp_sf_error_shutdown, .name = "sctp_sf_error_shutdown"}, \
} /* TYPE_SCTP_PRIMITIVE_SEND */ } /* TYPE_SCTP_PRIMITIVE_SEND */
#define TYPE_SCTP_PRIMITIVE_SETPRIMARY { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_SETPRIMARY */
#define TYPE_SCTP_PRIMITIVE_RECEIVE { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_RECEIVE */
#define TYPE_SCTP_PRIMITIVE_STATUS { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_STATUS */
#define TYPE_SCTP_PRIMITIVE_CHANGEHEARTBEAT { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_CHANGEHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \ #define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \
/* SCTP_STATE_EMPTY */ \ /* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
...@@ -731,152 +627,16 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { ...@@ -731,152 +627,16 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
.name = "sctp_sf_do_prm_requestheartbeat"}, \ .name = "sctp_sf_do_prm_requestheartbeat"}, \
} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */ } /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_GETSRTTREPORT { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_GETSRTTREPORT */
#define TYPE_SCTP_PRIMITIVE_SETFAILURETHRESHOLD { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_SETFAILURETHRESHOLD */
#define TYPE_SCTP_PRIMITIVE_SETPROTOPARAMETERS { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_SETPROTOPARAMETERS */
#define TYPE_SCTP_PRIMITIVE_RECEIVE_UNSENT { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_RECEIVE_UNSENT */
#define TYPE_SCTP_PRIMITIVE_RECEIVE_UNACKED { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_RECEIVE_UNACKED */
#define TYPE_SCTP_PRIMITIVE_DESTROY { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_DESTROY */
/* The primary index for this table is the primitive type. /* The primary index for this table is the primitive type.
* The secondary index for this table is the state. * The secondary index for this table is the state.
*/ */
sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = { sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_PRIMITIVE_INITIALIZE,
TYPE_SCTP_PRIMITIVE_ASSOCIATE, TYPE_SCTP_PRIMITIVE_ASSOCIATE,
TYPE_SCTP_PRIMITIVE_SHUTDOWN, TYPE_SCTP_PRIMITIVE_SHUTDOWN,
TYPE_SCTP_PRIMITIVE_ABORT, TYPE_SCTP_PRIMITIVE_ABORT,
TYPE_SCTP_PRIMITIVE_SEND, TYPE_SCTP_PRIMITIVE_SEND,
TYPE_SCTP_PRIMITIVE_SETPRIMARY,
TYPE_SCTP_PRIMITIVE_RECEIVE,
TYPE_SCTP_PRIMITIVE_STATUS,
TYPE_SCTP_PRIMITIVE_CHANGEHEARTBEAT,
TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT, TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT,
TYPE_SCTP_PRIMITIVE_GETSRTTREPORT,
TYPE_SCTP_PRIMITIVE_SETFAILURETHRESHOLD,
TYPE_SCTP_PRIMITIVE_SETPROTOPARAMETERS,
TYPE_SCTP_PRIMITIVE_RECEIVE_UNSENT,
TYPE_SCTP_PRIMITIVE_RECEIVE_UNACKED,
TYPE_SCTP_PRIMITIVE_DESTROY,
}; };
#define TYPE_SCTP_OTHER_NO_PENDING_TSN { \ #define TYPE_SCTP_OTHER_NO_PENDING_TSN { \
...@@ -902,30 +662,8 @@ sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE ...@@ -902,30 +662,8 @@ sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE
{.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \
} }
#define TYPE_SCTP_OTHER_ICMP_UNREACHFRAG { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
}
sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = { sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_OTHER_NO_PENDING_TSN, TYPE_SCTP_OTHER_NO_PENDING_TSN,
TYPE_SCTP_OTHER_ICMP_UNREACHFRAG,
}; };
#define TYPE_SCTP_EVENT_TIMEOUT_NONE { \ #define TYPE_SCTP_EVENT_TIMEOUT_NONE { \
...@@ -1033,27 +771,6 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA ...@@ -1033,27 +771,6 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
} }
#define TYPE_SCTP_EVENT_TIMEOUT_T4_RTO { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \ #define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \
/* SCTP_STATE_EMPTY */ \ /* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
...@@ -1089,11 +806,11 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA ...@@ -1089,11 +806,11 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
/* SCTP_STATE_SHUTDOWN_PENDING */ \ /* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \ {.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \ /* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \ /* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
} }
#define TYPE_SCTP_EVENT_TIMEOUT_SACK { \ #define TYPE_SCTP_EVENT_TIMEOUT_SACK { \
...@@ -1139,39 +856,16 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA ...@@ -1139,39 +856,16 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
} }
#define TYPE_SCTP_EVENT_TIMEOUT_PMTU_RAISE { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
}
sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = { sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_EVENT_TIMEOUT_NONE, TYPE_SCTP_EVENT_TIMEOUT_NONE,
TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE, TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE,
TYPE_SCTP_EVENT_TIMEOUT_T1_INIT, TYPE_SCTP_EVENT_TIMEOUT_T1_INIT,
TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN, TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN,
TYPE_SCTP_EVENT_TIMEOUT_T3_RTX, TYPE_SCTP_EVENT_TIMEOUT_T3_RTX,
TYPE_SCTP_EVENT_TIMEOUT_T4_RTO,
TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD, TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT, TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
TYPE_SCTP_EVENT_TIMEOUT_SACK, TYPE_SCTP_EVENT_TIMEOUT_SACK,
TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE, TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
TYPE_SCTP_EVENT_TIMEOUT_PMTU_RAISE,
}; };
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, sctp_state_t state) sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, sctp_state_t state)
......
/* 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-2002 International Business Machines, Corp. * Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001-2002 Intel Corp. * Copyright (c) 2001-2003 Intel Corp.
* Copyright (c) 2001-2002 Nokia, Inc. * Copyright (c) 2001-2002 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
* *
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <samudrala@us.ibm.com> * Sridhar Samudrala <samudrala@us.ibm.com>
* Inaky Perez-Gonzalez <inaky.gonzalez@intel.com> * Inaky Perez-Gonzalez <inaky.gonzalez@intel.com>
* Ardelle Fan <ardelle.fan@intel.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.
...@@ -131,7 +132,7 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -131,7 +132,7 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
static long sctp_get_port_local(struct sock *, union sctp_addr *); static long sctp_get_port_local(struct sock *, union sctp_addr *);
/* Verify this is a valid sockaddr. */ /* Verify this is a valid sockaddr. */
static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt, static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
union sctp_addr *addr, int len) union sctp_addr *addr, int len)
{ {
struct sctp_af *af; struct sctp_af *af;
...@@ -716,7 +717,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -716,7 +717,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_opt_t *sp; sctp_opt_t *sp;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_association_t *new_asoc=NULL, *asoc=NULL; sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_chunk_t *chunk = NULL; sctp_chunk_t *chunk = NULL;
union sctp_addr to; union sctp_addr to;
struct sockaddr *msg_name = NULL; struct sockaddr *msg_name = NULL;
...@@ -729,6 +730,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -729,6 +730,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_scope_t scope; sctp_scope_t scope;
long timeo; long timeo;
__u16 sinfo_flags = 0; __u16 sinfo_flags = 0;
struct sk_buff_head chunks;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n", SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
sk, msg, msg_len); sk, msg, msg_len);
...@@ -754,8 +756,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -754,8 +756,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
*/ */
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) { if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
int msg_namelen = msg->msg_namelen; int msg_namelen = msg->msg_namelen;
err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name, err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name,
msg_namelen); msg_namelen);
if (err) if (err)
return err; return err;
...@@ -806,7 +808,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -806,7 +808,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) { if (!asoc) {
/* If we could not find a matching association on the /* If we could not find a matching association on the
* endpoint, make sure that there is no peeled-off * endpoint, make sure that there is no peeled-off
* association on another socket. * association on another socket.
*/ */
if (sctp_endpoint_is_peeled_off(ep, &to)) { if (sctp_endpoint_is_peeled_off(ep, &to)) {
...@@ -868,13 +870,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -868,13 +870,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_unlock; goto out_unlock;
} }
} else { } else {
/* Check against the defaults. */
if (sinfo->sinfo_stream >=
sp->initmsg.sinit_num_ostreams) {
err = -EINVAL;
goto out_unlock;
}
/* Check against the requested. */ /* Check against the requested. */
if (sinfo->sinfo_stream >= if (sinfo->sinfo_stream >=
sinit->sinit_num_ostreams) { sinit->sinit_num_ostreams) {
...@@ -915,14 +910,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -915,14 +910,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sinit->sinit_num_ostreams; sinit->sinit_num_ostreams;
} }
if (sinit->sinit_max_instreams) { if (sinit->sinit_max_instreams) {
if (sinit->sinit_max_instreams <= asoc->c.sinit_max_instreams =
SCTP_MAX_STREAM) { sinit->sinit_max_instreams;
asoc->c.sinit_max_instreams =
sinit->sinit_max_instreams;
} else {
asoc->c.sinit_max_instreams =
SCTP_MAX_STREAM;
}
} }
if (sinit->sinit_max_attempts) { if (sinit->sinit_max_attempts) {
asoc->max_init_attempts asoc->max_init_attempts
...@@ -936,6 +925,15 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -936,6 +925,15 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* Prime the peer's transport structures. */ /* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL); transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
if (!transport) {
err = -ENOMEM;
goto out_free;
}
err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
if (err < 0) {
err = -ENOMEM;
goto out_free;
}
} }
/* ASSERT: we have a valid association at this point. */ /* ASSERT: we have a valid association at this point. */
...@@ -949,19 +947,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -949,19 +947,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_free; goto out_free;
} }
/* FIXME: In the current implementation, a single chunk is created
* for the entire message initially, even if it has to be fragmented
* later. As the length field in the chunkhdr is used to set
* the chunk length, the maximum size of the chunk and hence the
* message is limited by its type(__u16).
* The real fix is to fragment the message before creating the chunks.
*/
if (msg_len > ((__u16)(~(__u16)0) -
WORD_ROUND(sizeof(sctp_data_chunk_t)+1))) {
err = -EMSGSIZE;
goto out_free;
}
/* If fragmentation is disabled and the message length exceeds the /* If fragmentation is disabled and the message length exceeds the
* association fragmentation point, return EMSGSIZE. The I-D * association fragmentation point, return EMSGSIZE. The I-D
* does not specify what this error is, but this looks like * does not specify what this error is, but this looks like
...@@ -994,13 +979,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -994,13 +979,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_free; goto out_free;
} }
/* Get enough memory for the whole message. */
chunk = sctp_make_data_empty(asoc, sinfo, msg_len);
if (!chunk) {
err = -ENOMEM;
goto out_free;
}
#if 0 #if 0
/* FIXME: This looks wrong so I'll comment out. /* FIXME: This looks wrong so I'll comment out.
* We should be able to use this same technique for * We should be able to use this same technique for
...@@ -1016,20 +994,13 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1016,20 +994,13 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
#endif /* 0 */ #endif /* 0 */
/* Copy the message from the user. */ /* Break the message into multiple chunks of maximum size. */
err = sctp_user_addto_chunk(chunk, msg_len, msg->msg_iov); skb_queue_head_init(&chunks);
if (err < 0) err = sctp_datachunks_from_user(asoc, sinfo, msg, msg_len, &chunks);
if (err)
goto out_free; goto out_free;
SCTP_DEBUG_PRINTK("Copied message to chunk: %p.\n", chunk); /* Auto-connect, if we aren't connected already. */
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
/* Do accounting for the write space. */
sctp_set_owner_w(chunk);
if (SCTP_STATE_CLOSED == asoc->state) { if (SCTP_STATE_CLOSED == asoc->state) {
err = sctp_primitive_ASSOCIATE(asoc, NULL); err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) if (err < 0)
...@@ -1037,18 +1008,22 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1037,18 +1008,22 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
SCTP_DEBUG_PRINTK("We associated primitively.\n"); SCTP_DEBUG_PRINTK("We associated primitively.\n");
} }
/* Send it to the lower layers. */ /* Now send the (possibly) fragmented message. */
err = sctp_primitive_SEND(asoc, chunk); while ((chunk = (sctp_chunk_t *)__skb_dequeue(&chunks))) {
SCTP_DEBUG_PRINTK("We sent primitively.\n"); /* Do accounting for the write space. */
sctp_set_owner_w(chunk);
/* Send it to the lower layers. */
sctp_primitive_SEND(asoc, chunk);
SCTP_DEBUG_PRINTK("We sent primitively.\n");
}
/* BUG: SCTP_CHECK_TIMER(sk); */
if (!err) { if (!err) {
err = msg_len; err = msg_len;
goto out_unlock; goto out_unlock;
} }
/* If we are already past ASSOCIATE, the lower /* If we are already past ASSOCIATE, the lower
* layers are responsible for its cleanup. * layers are responsible for association cleanup.
*/ */
goto out_free_chunk; goto out_free_chunk;
...@@ -1086,23 +1061,30 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1086,23 +1061,30 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
* frag_list. len specifies the total amount of data that needs to be removed. * frag_list. len specifies the total amount of data that needs to be removed.
* when 'len' bytes could be removed from the skb, it returns 0. * when 'len' bytes could be removed from the skb, it returns 0.
* If 'len' exceeds the total skb length, it returns the no. of bytes that * If 'len' exceeds the total skb length, it returns the no. of bytes that
* could not be removed. * could not be removed.
*/ */
static int sctp_skb_pull(struct sk_buff *skb, int len) static int sctp_skb_pull(struct sk_buff *skb, int len)
{ {
struct sk_buff *list; struct sk_buff *list;
int skb_len = skb_headlen(skb);
int rlen;
if (len <= skb->len) { if (len <= skb_len) {
__skb_pull(skb, len); __skb_pull(skb, len);
return 0; return 0;
} }
len -= skb->len; len -= skb_len;
__skb_pull(skb, skb->len); __skb_pull(skb, skb_len);
for (list = skb_shinfo(skb)->frag_list; list; list = list->next) { for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
len = sctp_skb_pull(list, len); rlen = sctp_skb_pull(list, len);
if (!len) skb->len -= (len-rlen);
skb->data_len -= (len-rlen);
if (!rlen)
return 0; return 0;
len = rlen;
} }
return len; return len;
...@@ -1130,7 +1112,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1130,7 +1112,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
{ {
sctp_ulpevent_t *event = NULL; sctp_ulpevent_t *event = NULL;
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
struct sk_buff *skb, *list; struct sk_buff *skb;
int copied; int copied;
int err = 0; int err = 0;
int skb_len; int skb_len;
...@@ -1152,10 +1134,8 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1152,10 +1134,8 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
/* Get the total length of the skb including any skb's in the /* Get the total length of the skb including any skb's in the
* frag_list. * frag_list.
*/ */
skb_len = skb->len; skb_len = skb->len;
for (list = skb_shinfo(skb)->frag_list; list; list = list->next)
skb_len += list->len;
copied = skb_len; copied = skb_len;
if (copied > len) if (copied > len)
...@@ -1190,14 +1170,20 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1190,14 +1170,20 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
/* If skb's length exceeds the user's buffer, update the skb and /* If skb's length exceeds the user's buffer, update the skb and
* push it back to the receive_queue so that the next call to * push it back to the receive_queue so that the next call to
* recvmsg() will return the remaining data. Don't set MSG_EOR. * recvmsg() will return the remaining data. Don't set MSG_EOR.
* Otherwise, set MSG_EOR indicating the end of a message. * Otherwise, set MSG_EOR indicating the end of a message.
*/ */
if (skb_len > copied) { if (skb_len > copied) {
msg->msg_flags &= ~MSG_EOR; msg->msg_flags &= ~MSG_EOR;
if (flags & MSG_PEEK) if (flags & MSG_PEEK)
goto out_free; goto out_free;
sctp_skb_pull(skb, copied); sctp_skb_pull(skb, copied);
skb_queue_head(&sk->receive_queue, skb); skb_queue_head(&sk->receive_queue, skb);
/* When only partial message is copied to the user, increase
* rwnd by that amount. If all the data in the skb is read,
* rwnd is updated when the skb's destructor is called via
* sctp_ulpevent_free().
*/
sctp_assoc_rwnd_increase(event->asoc, copied);
goto out; goto out;
} else { } else {
msg->msg_flags |= MSG_EOR; msg->msg_flags |= MSG_EOR;
...@@ -1260,7 +1246,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk, ...@@ -1260,7 +1246,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
struct sctp_paddrparams params; struct sctp_paddrparams params;
sctp_association_t *asoc; sctp_association_t *asoc;
union sctp_addr *addr; union sctp_addr *addr;
sctp_transport_t *trans; struct sctp_transport *trans;
int error; int error;
if (optlen != sizeof(struct sctp_paddrparams)) if (optlen != sizeof(struct sctp_paddrparams))
...@@ -1449,7 +1435,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1449,7 +1435,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sctp_opt_t *sp; sctp_opt_t *sp;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_transport_t *transport; struct sctp_transport *transport;
union sctp_addr to; union sctp_addr to;
sctp_scope_t scope; sctp_scope_t scope;
long timeo; long timeo;
...@@ -1463,7 +1449,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1463,7 +1449,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sp = sctp_sk(sk); sp = sctp_sk(sk);
ep = sp->ep; ep = sp->ep;
/* connect() cannot be done on a peeled-off socket. */ /* connect() cannot be done on a peeled-off socket. */
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) { if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
err = -EISCONN; err = -EISCONN;
goto out_unlock; goto out_unlock;
...@@ -1471,7 +1457,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1471,7 +1457,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len); err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len);
if (err) if (err)
goto out_unlock; goto out_unlock;
memcpy(&to, uaddr, addr_len); memcpy(&to, uaddr, addr_len);
to.v4.sin_port = ntohs(to.v4.sin_port); to.v4.sin_port = ntohs(to.v4.sin_port);
...@@ -1479,7 +1465,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1479,7 +1465,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (asoc) { if (asoc) {
if (asoc->state >= SCTP_STATE_ESTABLISHED) if (asoc->state >= SCTP_STATE_ESTABLISHED)
err = -EISCONN; err = -EISCONN;
else else
err = -EALREADY; err = -EALREADY;
goto out_unlock; goto out_unlock;
...@@ -1514,10 +1500,19 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1514,10 +1500,19 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
/* Prime the peer's transport structures. */ /* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL); transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
if (!transport) {
sctp_association_free(asoc);
goto out_unlock;
}
err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
if (err < 0) {
sctp_association_free(asoc);
goto out_unlock;
}
err = sctp_primitive_ASSOCIATE(asoc, NULL); err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) { if (err < 0) {
sctp_association_free(asoc); sctp_association_free(asoc);
goto out_unlock; goto out_unlock;
} }
...@@ -1666,7 +1661,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1666,7 +1661,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
struct sctp_status status; struct sctp_status status;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_association_t *assoc = NULL; sctp_association_t *assoc = NULL;
sctp_transport_t *transport; struct sctp_transport *transport;
sctp_assoc_t associd; sctp_assoc_t associd;
int retval = 0; int retval = 0;
...@@ -1885,7 +1880,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, ...@@ -1885,7 +1880,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
struct sctp_paddrparams params; struct sctp_paddrparams params;
sctp_association_t *asoc; sctp_association_t *asoc;
union sctp_addr *addr; union sctp_addr *addr;
sctp_transport_t *trans; struct sctp_transport *trans;
if (len != sizeof(struct sctp_paddrparams)) if (len != sizeof(struct sctp_paddrparams))
return -EINVAL; return -EINVAL;
...@@ -1915,7 +1910,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk, ...@@ -1915,7 +1910,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
* before this address shall be considered unreachable. * before this address shall be considered unreachable.
*/ */
params.spp_pathmaxrxt = trans->error_threshold; params.spp_pathmaxrxt = trans->error_threshold;
if (copy_to_user(optval, &params, len)) if (copy_to_user(optval, &params, len))
return -EFAULT; return -EFAULT;
*optlen = len; *optlen = len;
...@@ -1932,6 +1927,166 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval ...@@ -1932,6 +1927,166 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval
return 0; return 0;
} }
static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_assoc_t id;
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
if (len != sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
return -EFAULT;
/*
* For UDP-style sockets, id specifies the association to query.
*/
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
list_for_each(pos, &asoc->peer.transport_addr_list) {
cnt ++;
}
if (copy_to_user(optval, &cnt, sizeof(int)))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
struct sctp_getaddrs getaddrs;
struct sctp_transport *from;
struct sockaddr_storage *to;
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
/*
* For UDP-style sockets, id specifies the association to query.
*/
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
to = getaddrs.addrs;
list_for_each(pos, &asoc->peer.transport_addr_list) {
from = list_entry(pos, struct sctp_transport, transports);
if (copy_to_user(to, &from->ipaddr, sizeof(from->ipaddr)))
return -EFAULT;
to ++;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_assoc_t id;
sctp_bind_addr_t *bp;
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
if (len != sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
return -EFAULT;
/*
* For UDP-style sockets, id specifies the association to query.
* If the id field is set to the value '0' then the locally bound
* addresses are returned without regard to any particular
* association.
*/
if (0 == id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
} else {
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
list_for_each(pos, &bp->address_list) {
cnt ++;
}
if (copy_to_user(optval, &cnt, sizeof(int)))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_bind_addr_t *bp;
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
struct sctp_getaddrs getaddrs;
struct sockaddr_storage_list *from;
struct sockaddr_storage *to;
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
/*
* For UDP-style sockets, id specifies the association to query.
* If the id field is set to the value '0' then the locally bound
* addresses are returned without regard to any particular
* association.
*/
if (0 == getaddrs.assoc_id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
} else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
to = getaddrs.addrs;
list_for_each(pos, &bp->address_list) {
from = list_entry(pos,
struct sockaddr_storage_list,
list);
if (copy_to_user(to, &from->a, sizeof(from->a)))
return -EFAULT;
to ++;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
return -EFAULT;
return 0;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen) char *optval, int *optlen)
{ {
...@@ -1989,6 +2144,26 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, ...@@ -1989,6 +2144,26 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen); retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break; break;
case SCTP_GET_PEER_ADDRS_NUM:
retval = sctp_getsockopt_get_peer_addrs_num(sk, len, optval,
optlen);
break;
case SCTP_GET_LOCAL_ADDRS_NUM:
retval = sctp_getsockopt_get_local_addrs_num(sk, len, optval,
optlen);
break;
case SCTP_GET_PEER_ADDRS:
retval = sctp_getsockopt_get_peer_addrs(sk, len, optval,
optlen);
break;
case SCTP_GET_LOCAL_ADDRS:
retval = sctp_getsockopt_get_local_addrs(sk, len, optval,
optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
...@@ -2029,7 +2204,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -2029,7 +2204,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
sctp_protocol_t *sctp = sctp_get_protocol(); sctp_protocol_t *sctp = sctp_get_protocol();
unsigned short snum; unsigned short snum;
int ret; int ret;
/* NOTE: Remember to put this back to net order. */ /* NOTE: Remember to put this back to net order. */
addr->v4.sin_port = ntohs(addr->v4.sin_port); addr->v4.sin_port = ntohs(addr->v4.sin_port);
snum = addr->v4.sin_port; snum = addr->v4.sin_port;
...@@ -2098,7 +2273,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -2098,7 +2273,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
} }
} }
if (pp != NULL && pp->sk != NULL) { if (pp != NULL && pp->sk != NULL) {
/* We had a port hash table hit - there is an /* We had a port hash table hit - there is an
* available port (pp != NULL) and it is being * available port (pp != NULL) and it is being
...@@ -2129,7 +2304,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -2129,7 +2304,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
if (sk_reuse && sk2->reuse) if (sk_reuse && sk2->reuse)
continue; continue;
if (sctp_bind_addr_match(&ep2->base.bind_addr, addr, if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
sctp_sk(sk))) sctp_sk(sk)))
goto found; goto found;
...@@ -2187,7 +2362,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) ...@@ -2187,7 +2362,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
} }
/* Assign a 'snum' port to the socket. If snum == 0, an ephemeral /* Assign a 'snum' port to the socket. If snum == 0, an ephemeral
* port is requested. * port is requested.
*/ */
static int sctp_get_port(struct sock *sk, unsigned short snum) static int sctp_get_port(struct sock *sk, unsigned short snum)
{ {
...@@ -2657,10 +2832,10 @@ static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len) ...@@ -2657,10 +2832,10 @@ static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len)
return -EINVAL; return -EINVAL;
/* Is this a valid SCTP address? */ /* Is this a valid SCTP address? */
if (!af->addr_valid((union sctp_addr *)addr)) if (!af->addr_valid((union sctp_addr *)addr))
return -EINVAL; return -EINVAL;
return 0; return 0;
} }
/* Get the sndbuf space available at the time on the association. */ /* Get the sndbuf space available at the time on the association. */
......
/* SCTP kernel reference Implementation
* Copyright (c) 2003 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* These functions manipulate sctp SSN tracker.
*
* The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
*
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
* Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
#include <linux/types.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
/* Storage size needed for map includes 2 headers and then the
* specific needs of in or out streams.
*/
static inline size_t sctp_ssnmap_size(__u16 in, __u16 out)
{
return sizeof(struct sctp_ssnmap) + (in + out) * sizeof(__u16);
}
/* Create a new sctp_ssnmap.
* Allocate room to store at least 'len' contiguous TSNs.
*/
struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int priority)
{
struct sctp_ssnmap *retval;
retval = kmalloc(sctp_ssnmap_size(in, out), priority);
if (!retval)
goto fail;
if (!sctp_ssnmap_init(retval, in, out))
goto fail_map;
retval->malloced = 1;
SCTP_DBG_OBJCNT_INC(ssnmap);
return retval;
fail_map:
kfree(retval);
fail:
return NULL;
}
/* Initialize a block of memory as a ssnmap. */
struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
__u16 out)
{
memset(map, 0x00, sctp_ssnmap_size(in, out));
/* Start 'in' stream just after the map header. */
map->in.ssn = (__u16 *)&map[1];
map->in.len = in;
/* Start 'out' stream just after 'in'. */
map->out.ssn = &map->in.ssn[in];
map->out.len = out;
return map;
}
/* Clear out the ssnmap streams. */
void sctp_ssnmap_clear(struct sctp_ssnmap *map)
{
size_t size;
size = (map->in.len + map->out.len) * sizeof(__u16);
memset(map->in.ssn, 0x00, size);
}
/* Dispose of a ssnmap. */
void sctp_ssnmap_free(struct sctp_ssnmap *map)
{
if (map && map->malloced) {
kfree(map);
SCTP_DBG_OBJCNT_DEC(ssnmap);
}
}
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines Corp. * Copyright (c) 2001-2003 International Business Machines Corp.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
* *
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
* Xingang Guo <xingang.guo@intel.com> * Xingang Guo <xingang.guo@intel.com>
* Hui Huang <hui.huang@nokia.com> * Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * 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.
...@@ -53,11 +54,12 @@ ...@@ -53,11 +54,12 @@
/* 1st Level Abstractions. */ /* 1st Level Abstractions. */
/* Allocate and initialize a new transport. */ /* Allocate and initialize a new transport. */
sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority) struct sctp_transport *sctp_transport_new(const union sctp_addr *addr,
int priority)
{ {
sctp_transport_t *transport; struct sctp_transport *transport;
transport = t_new(sctp_transport_t, priority); transport = t_new(struct sctp_transport, priority);
if (!transport) if (!transport)
goto fail; goto fail;
...@@ -77,9 +79,9 @@ sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority) ...@@ -77,9 +79,9 @@ sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority)
} }
/* Intialize a new transport from provided memory. */ /* Intialize a new transport from provided memory. */
sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
const union sctp_addr *addr, const union sctp_addr *addr,
int priority) int priority)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); sctp_protocol_t *proto = sctp_get_protocol();
...@@ -88,6 +90,9 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, ...@@ -88,6 +90,9 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->af_specific = sctp_get_af_specific(addr->sa.sa_family); peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
peer->asoc = NULL; peer->asoc = NULL;
peer->dst = NULL;
memset(&peer->saddr, 0, sizeof(union sctp_addr));
/* From 6.3.1 RTO Calculation: /* From 6.3.1 RTO Calculation:
* *
* C1) Until an RTT measurement has been made for a packet sent to the * C1) Until an RTT measurement has been made for a packet sent to the
...@@ -139,7 +144,7 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, ...@@ -139,7 +144,7 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
/* This transport is no longer needed. Free up if possible, or /* This transport is no longer needed. Free up if possible, or
* delay until it last reference count. * delay until it last reference count.
*/ */
void sctp_transport_free(sctp_transport_t *transport) void sctp_transport_free(struct sctp_transport *transport)
{ {
transport->dead = 1; transport->dead = 1;
...@@ -153,7 +158,7 @@ void sctp_transport_free(sctp_transport_t *transport) ...@@ -153,7 +158,7 @@ void sctp_transport_free(sctp_transport_t *transport)
/* Destroy the transport data structure. /* Destroy the transport data structure.
* Assumes there are no more users of this structure. * Assumes there are no more users of this structure.
*/ */
void sctp_transport_destroy(sctp_transport_t *transport) void sctp_transport_destroy(struct sctp_transport *transport)
{ {
SCTP_ASSERT(transport->dead, "Transport is not dead", return); SCTP_ASSERT(transport->dead, "Transport is not dead", return);
...@@ -168,7 +173,7 @@ void sctp_transport_destroy(sctp_transport_t *transport) ...@@ -168,7 +173,7 @@ void sctp_transport_destroy(sctp_transport_t *transport)
/* Start T3_rtx timer if it is not already running and update the heartbeat /* Start T3_rtx timer if it is not already running and update the heartbeat
* timer. This routine is called everytime a DATA chunk is sent. * timer. This routine is called everytime a DATA chunk is sent.
*/ */
void sctp_transport_reset_timers(sctp_transport_t *transport) void sctp_transport_reset_timers(struct sctp_transport *transport)
{ {
/* RFC 2960 6.3.2 Retransmission Timer Rules /* RFC 2960 6.3.2 Retransmission Timer Rules
* *
...@@ -177,96 +182,61 @@ void sctp_transport_reset_timers(sctp_transport_t *transport) ...@@ -177,96 +182,61 @@ void sctp_transport_reset_timers(sctp_transport_t *transport)
* start it running so that it will expire after the RTO of that * start it running so that it will expire after the RTO of that
* address. * address.
*/ */
if (!timer_pending(&transport->T3_rtx_timer)) {
if (!timer_pending(&transport->T3_rtx_timer))
if (!mod_timer(&transport->T3_rtx_timer, if (!mod_timer(&transport->T3_rtx_timer,
jiffies + transport->rto)) jiffies + transport->rto))
sctp_transport_hold(transport); sctp_transport_hold(transport);
}
/* When a data chunk is sent, reset the heartbeat interval. */ /* When a data chunk is sent, reset the heartbeat interval. */
if (!mod_timer(&transport->hb_timer, if (!mod_timer(&transport->hb_timer,
transport->hb_interval + transport->rto + jiffies)) sctp_transport_timeout(transport)))
sctp_transport_hold(transport); sctp_transport_hold(transport);
} }
/* This transport has been assigned to an association. /* This transport has been assigned to an association.
* Initialize fields from the association or from the sock itself. * Initialize fields from the association or from the sock itself.
* Register the reference count in the association. * Register the reference count in the association.
*/ */
void sctp_transport_set_owner(sctp_transport_t *transport, void sctp_transport_set_owner(struct sctp_transport *transport,
sctp_association_t *asoc) sctp_association_t *asoc)
{ {
transport->asoc = asoc; transport->asoc = asoc;
sctp_association_hold(asoc); sctp_association_hold(asoc);
} }
/* Caches the dst entry for a transport's destination address and an optional /* Initialize the pmtu of a transport. */
* souce address. void sctp_transport_pmtu(struct sctp_transport *transport)
*/
void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr,
struct sctp_opt *opt)
{ {
sctp_association_t *asoc = transport->asoc;
struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;
struct list_head *pos;
struct dst_entry *dst; struct dst_entry *dst;
union sctp_addr dst_saddr;
dst = af->get_dst(daddr, saddr); dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
/* If there is no association or if a source address is passed,
* no more validation is required.
*/
if (!asoc || saddr)
goto out;
if (SCTP_STATE_ESTABLISHED == asoc->state) {
bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
} else {
bp = &asoc->ep->base.bind_addr;
addr_lock = &asoc->ep->base.addr_lock;
}
if (dst) { if (dst) {
/* Walk through the bind address list and look for a bind transport->pmtu = dst_pmtu(dst);
* address that matches the source address of the returned dst.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list,
list);
af->dst_saddr(&dst_saddr, dst);
if (opt->pf->cmp_addr(&dst_saddr, &laddr->a, opt))
goto out_unlock;
}
sctp_read_unlock(addr_lock);
/* None of the bound addresses match the source address of the
* dst. So release it.
*/
dst_release(dst); dst_release(dst);
} } else
transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
}
/* Walk through the bind address list and try to get a dst that /* Caches the dst entry and source address for a transport's destination
* matches a bind address as the source address. * address.
*/ */
sctp_read_lock(addr_lock); void sctp_transport_route(struct sctp_transport *transport,
list_for_each(pos, &bp->address_list) { union sctp_addr *saddr, struct sctp_opt *opt)
laddr = list_entry(pos, struct sockaddr_storage_list, list); {
sctp_association_t *asoc = transport->asoc;
struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
struct dst_entry *dst;
dst = af->get_dst(daddr, &laddr->a); dst = af->get_dst(asoc, daddr, saddr);
if (dst)
goto out_unlock; if (saddr)
} memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
else
af->get_saddr(asoc, dst, daddr, &transport->saddr);
out_unlock:
sctp_read_unlock(addr_lock);
out:
transport->dst = dst; transport->dst = dst;
if (dst) if (dst)
transport->pmtu = dst_pmtu(dst); transport->pmtu = dst_pmtu(dst);
...@@ -275,7 +245,7 @@ void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr, ...@@ -275,7 +245,7 @@ void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr,
} }
/* Hold a reference to a transport. */ /* Hold a reference to a transport. */
void sctp_transport_hold(sctp_transport_t *transport) void sctp_transport_hold(struct sctp_transport *transport)
{ {
atomic_inc(&transport->refcnt); atomic_inc(&transport->refcnt);
} }
...@@ -283,14 +253,14 @@ void sctp_transport_hold(sctp_transport_t *transport) ...@@ -283,14 +253,14 @@ void sctp_transport_hold(sctp_transport_t *transport)
/* Release a reference to a transport and clean up /* Release a reference to a transport and clean up
* if there are no more references. * if there are no more references.
*/ */
void sctp_transport_put(sctp_transport_t *transport) void sctp_transport_put(struct sctp_transport *transport)
{ {
if (atomic_dec_and_test(&transport->refcnt)) if (atomic_dec_and_test(&transport->refcnt))
sctp_transport_destroy(transport); sctp_transport_destroy(transport);
} }
/* Update transport's RTO based on the newly calculated RTT. */ /* Update transport's RTO based on the newly calculated RTT. */
void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt) void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); sctp_protocol_t *proto = sctp_get_protocol();
...@@ -330,7 +300,7 @@ void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt) ...@@ -330,7 +300,7 @@ void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt)
if (tp->rttvar == 0) if (tp->rttvar == 0)
tp->rttvar = SCTP_CLOCK_GRANULARITY; tp->rttvar = SCTP_CLOCK_GRANULARITY;
/* 6.3.1 C3) After the computation, update RTO <- SRTT + 4 * RTTVAR. */ /* 6.3.1 C3) After the computation, update RTO <- SRTT + 4 * RTTVAR. */
tp->rto = tp->srtt + (tp->rttvar << 2); tp->rto = tp->srtt + (tp->rttvar << 2);
/* 6.3.1 C6) Whenever RTO is computed, if it is less than RTO.Min /* 6.3.1 C6) Whenever RTO is computed, if it is less than RTO.Min
...@@ -360,8 +330,8 @@ void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt) ...@@ -360,8 +330,8 @@ void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt)
/* This routine updates the transport's cwnd and partial_bytes_acked /* This routine updates the transport's cwnd and partial_bytes_acked
* parameters based on the bytes acked in the received SACK. * parameters based on the bytes acked in the received SACK.
*/ */
void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn, void sctp_transport_raise_cwnd(struct sctp_transport *transport,
__u32 bytes_acked) __u32 sack_ctsn, __u32 bytes_acked)
{ {
__u32 cwnd, ssthresh, flight_size, pba, pmtu; __u32 cwnd, ssthresh, flight_size, pba, pmtu;
...@@ -389,8 +359,8 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn, ...@@ -389,8 +359,8 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn,
* two conditions are met can the cwnd be increased otherwise * two conditions are met can the cwnd be increased otherwise
* the cwnd MUST not be increased. If these conditions are met * the cwnd MUST not be increased. If these conditions are met
* then cwnd MUST be increased by at most the lesser of * then cwnd MUST be increased by at most the lesser of
* 1) the total size of the previously outstanding DATA chunk(s) * 1) the total size of the previously outstanding DATA
* acknowledged, and 2) the destination's path MTU. * chunk(s) acknowledged, and 2) the destination's path MTU.
*/ */
if (bytes_acked > pmtu) if (bytes_acked > pmtu)
cwnd += pmtu; cwnd += pmtu;
...@@ -403,18 +373,18 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn, ...@@ -403,18 +373,18 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn,
transport, bytes_acked, cwnd, transport, bytes_acked, cwnd,
ssthresh, flight_size, pba); ssthresh, flight_size, pba);
} else { } else {
/* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh, upon /* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh,
* each SACK arrival that advances the Cumulative TSN Ack Point, * upon each SACK arrival that advances the Cumulative TSN Ack
* increase partial_bytes_acked by the total number of bytes of * Point, increase partial_bytes_acked by the total number of
* all new chunks acknowledged in that SACK including chunks * bytes of all new chunks acknowledged in that SACK including
* acknowledged by the new Cumulative TSN Ack and by Gap Ack * chunks acknowledged by the new Cumulative TSN Ack and by
* Blocks. * Gap Ack Blocks.
* *
* When partial_bytes_acked is equal to or greater than cwnd and * When partial_bytes_acked is equal to or greater than cwnd
* before the arrival of the SACK the sender had cwnd or more * and before the arrival of the SACK the sender had cwnd or
* bytes of data outstanding (i.e., before arrival of the SACK, * more bytes of data outstanding (i.e., before arrival of the
* flightsize was greater than or equal to cwnd), increase cwnd * SACK, flightsize was greater than or equal to cwnd),
* by MTU, and reset partial_bytes_acked to * increase cwnd by MTU, and reset partial_bytes_acked to
* (partial_bytes_acked - cwnd). * (partial_bytes_acked - cwnd).
*/ */
pba += bytes_acked; pba += bytes_acked;
...@@ -437,7 +407,7 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn, ...@@ -437,7 +407,7 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn,
/* This routine is used to lower the transport's cwnd when congestion is /* This routine is used to lower the transport's cwnd when congestion is
* detected. * detected.
*/ */
void sctp_transport_lower_cwnd(sctp_transport_t *transport, void sctp_transport_lower_cwnd(struct sctp_transport *transport,
sctp_lower_cwnd_t reason) sctp_lower_cwnd_t reason)
{ {
switch (reason) { switch (reason) {
...@@ -514,3 +484,12 @@ void sctp_transport_lower_cwnd(sctp_transport_t *transport, ...@@ -514,3 +484,12 @@ void sctp_transport_lower_cwnd(sctp_transport_t *transport,
transport, reason, transport, reason,
transport->cwnd, transport->ssthresh); transport->cwnd, transport->ssthresh);
} }
/* What is the next timeout value for this transport? */
unsigned long sctp_transport_timeout(struct sctp_transport *t)
{
unsigned long timeout;
timeout = t->hb_interval + t->rto + sctp_jitter(t->rto);
timeout += jiffies;
return timeout;
}
...@@ -606,9 +606,9 @@ sctp_ulpevent_t *sctp_ulpevent_make_shutdown_event( ...@@ -606,9 +606,9 @@ sctp_ulpevent_t *sctp_ulpevent_make_shutdown_event(
sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc, sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc,
sctp_chunk_t *chunk, int priority) sctp_chunk_t *chunk, int priority)
{ {
sctp_ulpevent_t *event; sctp_ulpevent_t *event, *levent;
struct sctp_sndrcvinfo *info; struct sctp_sndrcvinfo *info;
struct sk_buff *skb; struct sk_buff *skb, *list;
size_t padding, len; size_t padding, len;
/* Clone the original skb, sharing the data. */ /* Clone the original skb, sharing the data. */
...@@ -647,6 +647,16 @@ sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc, ...@@ -647,6 +647,16 @@ sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc,
event->malloced = 1; event->malloced = 1;
for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
sctp_ulpevent_set_owner_r(list, asoc);
/* Initialize event with flags 0. */
levent = sctp_ulpevent_init(event, skb, 0);
if (!levent)
goto fail_init;
levent->malloced = 1;
}
info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo; info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo;
/* Sockets API Extensions for SCTP /* Sockets API Extensions for SCTP
...@@ -762,8 +772,6 @@ static void sctp_rcvmsg_rfree(struct sk_buff *skb) ...@@ -762,8 +772,6 @@ static void sctp_rcvmsg_rfree(struct sk_buff *skb)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_ulpevent_t *event; sctp_ulpevent_t *event;
sctp_chunk_t *sack;
struct timer_list *timer;
/* Current stack structures assume that the rcv buffer is /* Current stack structures assume that the rcv buffer is
* per socket. For UDP style sockets this is not true as * per socket. For UDP style sockets this is not true as
...@@ -773,50 +781,7 @@ static void sctp_rcvmsg_rfree(struct sk_buff *skb) ...@@ -773,50 +781,7 @@ static void sctp_rcvmsg_rfree(struct sk_buff *skb)
*/ */
event = (sctp_ulpevent_t *) skb->cb; event = (sctp_ulpevent_t *) skb->cb;
asoc = event->asoc; asoc = event->asoc;
if (asoc->rwnd_over) { sctp_assoc_rwnd_increase(asoc, skb_headlen(skb));
if (asoc->rwnd_over >= skb->len) {
asoc->rwnd_over -= skb->len;
} else {
asoc->rwnd += (skb->len - asoc->rwnd_over);
asoc->rwnd_over = 0;
}
} else {
asoc->rwnd += skb->len;
}
SCTP_DEBUG_PRINTK("rwnd increased by %d to (%u, %u) - %u\n",
skb->len, asoc->rwnd, asoc->rwnd_over, asoc->a_rwnd);
/* Send a window update SACK if the rwnd has increased by at least the
* minimum of the association's PMTU and half of the receive buffer.
* The algorithm used is similar to the one described in Section 4.2.3.3
* of RFC 1122.
*/
if ((asoc->state == SCTP_STATE_ESTABLISHED) &&
(asoc->rwnd > asoc->a_rwnd) &&
((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) {
SCTP_DEBUG_PRINTK("Sending window update SACK- rwnd: %u "
"a_rwnd: %u\n", asoc->rwnd, asoc->a_rwnd);
sack = sctp_make_sack(asoc);
if (!sack)
goto out;
/* Update the last advertised rwnd value. */
asoc->a_rwnd = asoc->rwnd;
asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0;
sctp_push_outqueue(&asoc->outqueue, sack);
/* Stop the SACK timer. */
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
if (timer_pending(timer) && del_timer(timer))
sctp_association_put(asoc);
}
out:
sctp_association_put(asoc); sctp_association_put(asoc);
} }
...@@ -838,16 +803,7 @@ static void sctp_ulpevent_set_owner_r(struct sk_buff *skb, sctp_association_t *a ...@@ -838,16 +803,7 @@ static void sctp_ulpevent_set_owner_r(struct sk_buff *skb, sctp_association_t *a
skb->destructor = sctp_rcvmsg_rfree; skb->destructor = sctp_rcvmsg_rfree;
SCTP_ASSERT(asoc->rwnd, "rwnd zero", return); sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
if (asoc->rwnd >= skb->len) {
asoc->rwnd -= skb->len;
} else {
asoc->rwnd_over = skb->len - asoc->rwnd;
asoc->rwnd = 0;
}
SCTP_DEBUG_PRINTK("rwnd decreased by %d to (%u, %u)\n",
skb->len, asoc->rwnd, asoc->rwnd_over);
} }
/* A simple destructor to give up the reference to the association. */ /* A simple destructor to give up the reference to the association. */
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines, Corp. * 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
...@@ -49,51 +49,39 @@ ...@@ -49,51 +49,39 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Forward declarations for internal helpers. */ /* Forward declarations for internal helpers. */
static inline sctp_ulpevent_t * sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq, static inline struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event); struct sctp_ulpevent *);
static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq, static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *,
sctp_ulpevent_t *event); struct sctp_ulpevent *);
/* 1st Level Abstractions */ /* 1st Level Abstractions */
/* Create a new ULP queue. */ /* Create a new ULP queue. */
sctp_ulpqueue_t *sctp_ulpqueue_new(sctp_association_t *asoc, struct sctp_ulpq *sctp_ulpq_new(sctp_association_t *asoc, int priority)
__u16 inbound, int priority)
{ {
sctp_ulpqueue_t *ulpq; struct sctp_ulpq *ulpq;
size_t size;
/* Today, there is only a fixed size of storage needed for ulpq = kmalloc(sizeof(struct sctp_ulpq), priority);
* stream support, but make the interfaces acceptable for
* the future.
*/
size = sizeof(sctp_ulpqueue_t)+sctp_ulpqueue_storage_size(inbound);
ulpq = kmalloc(size, priority);
if (!ulpq) if (!ulpq)
goto fail; goto fail;
if (!sctp_ulpqueue_init(ulpq, asoc, inbound)) if (!sctp_ulpq_init(ulpq, asoc))
goto fail_init; goto fail_init;
ulpq->malloced = 1; ulpq->malloced = 1;
return ulpq; return ulpq;
fail_init: fail_init:
kfree(ulpq); kfree(ulpq);
fail: fail:
return NULL; return NULL;
} }
/* Initialize a ULP queue from a block of memory. */ /* Initialize a ULP queue from a block of memory. */
sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq, struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq,
sctp_association_t *asoc, sctp_association_t *asoc)
__u16 inbound)
{ {
memset(ulpq, memset(ulpq, sizeof(struct sctp_ulpq), 0x00);
sizeof(sctp_ulpqueue_t) + sctp_ulpqueue_storage_size(inbound),
0x00);
ulpq->asoc = asoc; ulpq->asoc = asoc;
spin_lock_init(&ulpq->lock);
skb_queue_head_init(&ulpq->reasm); skb_queue_head_init(&ulpq->reasm);
skb_queue_head_init(&ulpq->lobby); skb_queue_head_init(&ulpq->lobby);
ulpq->malloced = 0; ulpq->malloced = 0;
...@@ -101,38 +89,39 @@ sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq, ...@@ -101,38 +89,39 @@ sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq,
return ulpq; return ulpq;
} }
/* Flush the reassembly and ordering queues. */ /* Flush the reassembly and ordering queues. */
void sctp_ulpqueue_flush(sctp_ulpqueue_t *ulpq) void sctp_ulpq_flush(struct sctp_ulpq *ulpq)
{ {
struct sk_buff *skb; struct sk_buff *skb;
sctp_ulpevent_t *event; struct sctp_ulpevent *event;
while ((skb = skb_dequeue(&ulpq->lobby))) { while ((skb = skb_dequeue(&ulpq->lobby))) {
event = (sctp_ulpevent_t *) skb->cb; event = (struct sctp_ulpevent *) skb->cb;
sctp_ulpevent_free(event); sctp_ulpevent_free(event);
} }
while ((skb = skb_dequeue(&ulpq->reasm))) { while ((skb = skb_dequeue(&ulpq->reasm))) {
event = (sctp_ulpevent_t *) skb->cb; event = (struct sctp_ulpevent *) skb->cb;
sctp_ulpevent_free(event); sctp_ulpevent_free(event);
} }
} }
/* Dispose of a ulpqueue. */ /* Dispose of a ulpqueue. */
void sctp_ulpqueue_free(sctp_ulpqueue_t *ulpq) void sctp_ulpq_free(struct sctp_ulpq *ulpq)
{ {
sctp_ulpqueue_flush(ulpq); sctp_ulpq_flush(ulpq);
if (ulpq->malloced) if (ulpq->malloced)
kfree(ulpq); kfree(ulpq);
} }
/* Process an incoming DATA chunk. */ /* Process an incoming DATA chunk. */
int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk, int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, sctp_chunk_t *chunk,
int priority) int priority)
{ {
struct sk_buff_head temp; struct sk_buff_head temp;
sctp_data_chunk_t *hdr; sctp_data_chunk_t *hdr;
sctp_ulpevent_t *event; struct sctp_ulpevent *event;
hdr = (sctp_data_chunk_t *) chunk->chunk_hdr; hdr = (sctp_data_chunk_t *) chunk->chunk_hdr;
...@@ -147,7 +136,7 @@ int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk, ...@@ -147,7 +136,7 @@ int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk,
return -ENOMEM; return -ENOMEM;
/* Do reassembly if needed. */ /* Do reassembly if needed. */
event = sctp_ulpqueue_reasm(ulpq, event); event = sctp_ulpq_reasm(ulpq, event);
/* Do ordering if needed. */ /* Do ordering if needed. */
if (event) { if (event) {
...@@ -155,18 +144,18 @@ int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk, ...@@ -155,18 +144,18 @@ int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk,
skb_queue_head_init(&temp); skb_queue_head_init(&temp);
skb_queue_tail(&temp, event->parent); skb_queue_tail(&temp, event->parent);
event = sctp_ulpqueue_order(ulpq, event); event = sctp_ulpq_order(ulpq, event);
} }
/* Send event to the ULP. */ /* Send event to the ULP. */
if (event) if (event)
sctp_ulpqueue_tail_event(ulpq, event); sctp_ulpq_tail_event(ulpq, event);
return 0; return 0;
} }
/* Add a new event for propogation to the ULP. */ /* Add a new event for propogation to the ULP. */
int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event) int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
{ {
struct sock *sk = ulpq->asoc->base.sk; struct sock *sk = ulpq->asoc->base.sk;
...@@ -202,20 +191,18 @@ int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event) ...@@ -202,20 +191,18 @@ int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event)
/* 2nd Level Abstractions */ /* 2nd Level Abstractions */
/* Helper function to store chunks that need to be reassembled. */ /* Helper function to store chunks that need to be reassembled. */
static inline void sctp_ulpqueue_store_reasm(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event) static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
struct sctp_ulpevent *event)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent; struct sctp_ulpevent *cevent;
__u32 tsn, ctsn; __u32 tsn, ctsn;
unsigned long flags __attribute ((unused));
tsn = event->sndrcvinfo.sinfo_tsn; tsn = event->sndrcvinfo.sinfo_tsn;
sctp_spin_lock_irqsave(&ulpq->reasm.lock, flags);
/* Find the right place in this list. We store them by TSN. */ /* Find the right place in this list. We store them by TSN. */
sctp_skb_for_each(pos, &ulpq->reasm, tmp) { sctp_skb_for_each(pos, &ulpq->reasm, tmp) {
cevent = (sctp_ulpevent_t *)pos->cb; cevent = (struct sctp_ulpevent *)pos->cb;
ctsn = cevent->sndrcvinfo.sinfo_tsn; ctsn = cevent->sndrcvinfo.sinfo_tsn;
if (TSN_lt(tsn, ctsn)) if (TSN_lt(tsn, ctsn))
...@@ -227,29 +214,45 @@ static inline void sctp_ulpqueue_store_reasm(sctp_ulpqueue_t *ulpq, sctp_ulpeven ...@@ -227,29 +214,45 @@ static inline void sctp_ulpqueue_store_reasm(sctp_ulpqueue_t *ulpq, sctp_ulpeven
__skb_insert(event->parent, pos->prev, pos, &ulpq->reasm); __skb_insert(event->parent, pos->prev, pos, &ulpq->reasm);
else else
__skb_queue_tail(&ulpq->reasm, event->parent); __skb_queue_tail(&ulpq->reasm, event->parent);
sctp_spin_unlock_irqrestore(&ulpq->reasm.lock, flags);
} }
/* Helper function to return an event corresponding to the reassembled /* Helper function to return an event corresponding to the reassembled
* datagram. * datagram.
* This routine creates a re-assembled skb given the first and last skb's
* as stored in the reassembly queue. The skb's may be non-linear if the sctp
* payload was fragmented on the way and ip had to reassemble them.
* We add the rest of skb's to the first skb's fraglist.
*/ */
static inline sctp_ulpevent_t *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag) static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag)
{ {
struct sk_buff *pos; struct sk_buff *pos;
sctp_ulpevent_t *event; struct sctp_ulpevent *event;
struct sk_buff *pnext; struct sk_buff *pnext, *last;
struct sk_buff *list = skb_shinfo(f_frag)->frag_list;
/* Store the pointer to the 2nd skb */
pos = f_frag->next; pos = f_frag->next;
/* Set the first fragment's frag_list to point to the 2nd fragment. */ /* Get the last skb in the f_frag's frag_list if present. */
skb_shinfo(f_frag)->frag_list = pos; for (last = list; list; last = list, list = list->next);
/* Add the list of remaining fragments to the first fragments
* frag_list.
*/
if (last)
last->next = pos;
else
skb_shinfo(f_frag)->frag_list = pos;
/* Remove the first fragment from the reassembly queue. */ /* Remove the first fragment from the reassembly queue. */
__skb_unlink(f_frag, f_frag->list); __skb_unlink(f_frag, f_frag->list);
do { do {
pnext = pos->next; pnext = pos->next;
/* Update the len and data_len fields of the first fragment. */
f_frag->len += pos->len;
f_frag->data_len += pos->len;
/* Remove the fragment from the reassembly queue. */ /* Remove the fragment from the reassembly queue. */
__skb_unlink(pos, pos->list); __skb_unlink(pos, pos->list);
...@@ -269,13 +272,12 @@ static inline sctp_ulpevent_t *sctp_make_reassembled_event(struct sk_buff *f_fra ...@@ -269,13 +272,12 @@ static inline sctp_ulpevent_t *sctp_make_reassembled_event(struct sk_buff *f_fra
/* Helper function to check if an incoming chunk has filled up the last /* Helper function to check if an incoming chunk has filled up the last
* missing fragment in a SCTP datagram and return the corresponding event. * missing fragment in a SCTP datagram and return the corresponding event.
*/ */
static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_t *ulpq) static inline sctp_ulpevent_t *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent; sctp_ulpevent_t *cevent;
struct sk_buff *first_frag = NULL; struct sk_buff *first_frag = NULL;
__u32 ctsn, next_tsn; __u32 ctsn, next_tsn;
unsigned long flags __attribute ((unused));
sctp_ulpevent_t *retval = NULL; sctp_ulpevent_t *retval = NULL;
/* Initialized to 0 just to avoid compiler warning message. Will /* Initialized to 0 just to avoid compiler warning message. Will
...@@ -284,8 +286,6 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_ ...@@ -284,8 +286,6 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_
*/ */
next_tsn = 0; next_tsn = 0;
sctp_spin_lock_irqsave(&ulpq->reasm.lock, flags);
/* The chunks are held in the reasm queue sorted by TSN. /* The chunks are held in the reasm queue sorted by TSN.
* Walk through the queue sequentially and look for a sequence of * Walk through the queue sequentially and look for a sequence of
* fragmented chunks that complete a datagram. * fragmented chunks that complete a datagram.
...@@ -327,7 +327,6 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_ ...@@ -327,7 +327,6 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_
if (retval) if (retval)
break; break;
} }
sctp_spin_unlock_irqrestore(&ulpq->reasm.lock, flags);
return retval; return retval;
} }
...@@ -335,7 +334,7 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_ ...@@ -335,7 +334,7 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_
/* Helper function to reassemble chunks. Hold chunks on the reasm queue that /* Helper function to reassemble chunks. Hold chunks on the reasm queue that
* need reassembling. * need reassembling.
*/ */
static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq, static inline sctp_ulpevent_t *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event) sctp_ulpevent_t *event)
{ {
sctp_ulpevent_t *retval = NULL; sctp_ulpevent_t *retval = NULL;
...@@ -350,8 +349,8 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq, ...@@ -350,8 +349,8 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq,
if (SCTP_DATA_NOT_FRAG == (event->chunk_flags & SCTP_DATA_FRAG_MASK)) if (SCTP_DATA_NOT_FRAG == (event->chunk_flags & SCTP_DATA_FRAG_MASK))
return event; return event;
sctp_ulpqueue_store_reasm(ulpq, event); sctp_ulpq_store_reasm(ulpq, event);
retval = sctp_ulpqueue_retrieve_reassembled(ulpq); retval = sctp_ulpq_retrieve_reassembled(ulpq);
return retval; return retval;
} }
...@@ -359,20 +358,20 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq, ...@@ -359,20 +358,20 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq,
/* Helper function to gather skbs that have possibly become /* Helper function to gather skbs that have possibly become
* ordered by an an incoming chunk. * ordered by an an incoming chunk.
*/ */
static inline void sctp_ulpqueue_retrieve_ordered(sctp_ulpqueue_t *ulpq, static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event) sctp_ulpevent_t *event)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent; struct sctp_ulpevent *cevent;
struct sctp_stream *in;
__u16 sid, csid; __u16 sid, csid;
__u16 ssn, cssn; __u16 ssn, cssn;
unsigned long flags __attribute ((unused));
sid = event->sndrcvinfo.sinfo_stream; sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn; ssn = event->sndrcvinfo.sinfo_ssn;
in = &ulpq->asoc->ssnmap->in;
/* We are holding the chunks by stream, by SSN. */ /* We are holding the chunks by stream, by SSN. */
sctp_spin_lock_irqsave(&ulpq->lobby.lock, flags);
sctp_skb_for_each(pos, &ulpq->lobby, tmp) { sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
cevent = (sctp_ulpevent_t *) pos->cb; cevent = (sctp_ulpevent_t *) pos->cb;
csid = cevent->sndrcvinfo.sinfo_stream; csid = cevent->sndrcvinfo.sinfo_stream;
...@@ -386,32 +385,31 @@ static inline void sctp_ulpqueue_retrieve_ordered(sctp_ulpqueue_t *ulpq, ...@@ -386,32 +385,31 @@ static inline void sctp_ulpqueue_retrieve_ordered(sctp_ulpqueue_t *ulpq,
if (csid < sid) if (csid < sid)
continue; continue;
if (cssn != ulpq->ssn[sid]) if (cssn != sctp_ssn_peek(in, sid))
break; break;
ulpq->ssn[sid]++; /* Found it, so mark in the ssnmap. */
sctp_ssn_next(in, sid);
__skb_unlink(pos, pos->list); __skb_unlink(pos, pos->list);
/* Attach all gathered skbs to the event. */ /* Attach all gathered skbs to the event. */
__skb_queue_tail(event->parent->list, pos); __skb_queue_tail(event->parent->list, pos);
} }
sctp_spin_unlock_irqrestore(&ulpq->lobby.lock, flags);
} }
/* Helper function to store chunks needing ordering. */ /* Helper function to store chunks needing ordering. */
static inline void sctp_ulpqueue_store_ordered(sctp_ulpqueue_t *ulpq, static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event) sctp_ulpevent_t *event)
{ {
struct sk_buff *pos, *tmp; struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent; sctp_ulpevent_t *cevent;
__u16 sid, csid; __u16 sid, csid;
__u16 ssn, cssn; __u16 ssn, cssn;
unsigned long flags __attribute ((unused));
sid = event->sndrcvinfo.sinfo_stream; sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn; ssn = event->sndrcvinfo.sinfo_ssn;
sctp_spin_lock_irqsave(&ulpq->lobby.lock, flags);
/* Find the right place in this list. We store them by /* Find the right place in this list. We store them by
* stream ID and then by SSN. * stream ID and then by SSN.
...@@ -432,14 +430,13 @@ static inline void sctp_ulpqueue_store_ordered(sctp_ulpqueue_t *ulpq, ...@@ -432,14 +430,13 @@ static inline void sctp_ulpqueue_store_ordered(sctp_ulpqueue_t *ulpq,
__skb_insert(event->parent, pos->prev, pos, &ulpq->lobby); __skb_insert(event->parent, pos->prev, pos, &ulpq->lobby);
else else
__skb_queue_tail(&ulpq->lobby, event->parent); __skb_queue_tail(&ulpq->lobby, event->parent);
sctp_spin_unlock_irqrestore(&ulpq->lobby.lock, flags);
} }
static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq, static inline sctp_ulpevent_t *sctp_ulpq_order(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event) sctp_ulpevent_t *event)
{ {
__u16 sid, ssn; __u16 sid, ssn;
struct sctp_stream *in;
/* FIXME: We should be using some new chunk structure here /* FIXME: We should be using some new chunk structure here
* instead of carrying chunk fields in the event structure. * instead of carrying chunk fields in the event structure.
...@@ -454,23 +451,24 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq, ...@@ -454,23 +451,24 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq,
/* Note: The stream ID must be verified before this routine. */ /* Note: The stream ID must be verified before this routine. */
sid = event->sndrcvinfo.sinfo_stream; sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn; ssn = event->sndrcvinfo.sinfo_ssn;
in = &ulpq->asoc->ssnmap->in;
/* Is this the expected SSN for this stream ID? */ /* Is this the expected SSN for this stream ID? */
if (ssn != ulpq->ssn[sid]) { if (ssn != sctp_ssn_peek(in, sid)) {
/* We've received something out of order, so find where it /* We've received something out of order, so find where it
* needs to be placed. We order by stream and then by SSN. * needs to be placed. We order by stream and then by SSN.
*/ */
sctp_ulpqueue_store_ordered(ulpq, event); sctp_ulpq_store_ordered(ulpq, event);
return NULL; return NULL;
} }
/* Mark that the next chunk has been found. */ /* Mark that the next chunk has been found. */
ulpq->ssn[sid]++; sctp_ssn_next(in, sid);
/* Go find any other chunks that were waiting for /* Go find any other chunks that were waiting for
* ordering. * ordering.
*/ */
sctp_ulpqueue_retrieve_ordered(ulpq, event); sctp_ulpq_retrieve_ordered(ulpq, event);
return event; return event;
} }
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