Commit 3f7a87d2 authored by Frank Filz's avatar Frank Filz Committed by David S. Miller

[SCTP] sctp_connectx() API support

Implements sctp_connectx() as defined in the SCTP sockets API draft by
tunneling the request through a setsockopt().
Signed-off-by: default avatarFrank Filz <ffilzlnx@us.ibm.com>
Signed-off-by: default avatarSridhar Samudrala <sri@us.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8b22c249
...@@ -65,9 +65,11 @@ typedef enum { ...@@ -65,9 +65,11 @@ typedef enum {
SCTP_CMD_TIMER_START, /* Start a timer. */ SCTP_CMD_TIMER_START, /* Start a timer. */
SCTP_CMD_TIMER_RESTART, /* Restart a timer. */ SCTP_CMD_TIMER_RESTART, /* Restart a timer. */
SCTP_CMD_TIMER_STOP, /* Stop a timer. */ SCTP_CMD_TIMER_STOP, /* Stop a timer. */
SCTP_CMD_COUNTER_RESET, /* Reset a counter. */ SCTP_CMD_INIT_CHOOSE_TRANSPORT, /* Choose transport for an INIT. */
SCTP_CMD_COUNTER_INC, /* Increment a counter. */ SCTP_CMD_INIT_COUNTER_RESET, /* Reset init counter. */
SCTP_CMD_INIT_COUNTER_INC, /* Increment init counter. */
SCTP_CMD_INIT_RESTART, /* High level, do init timer work. */ SCTP_CMD_INIT_RESTART, /* High level, do init timer work. */
SCTP_CMD_COOKIEECHO_RESTART, /* High level, do cookie-echo timer work. */
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_STRIKE, /* Mark a strike against a transport. */ SCTP_CMD_STRIKE, /* Mark a strike against a transport. */
...@@ -118,7 +120,6 @@ typedef union { ...@@ -118,7 +120,6 @@ typedef union {
int error; int error;
sctp_state_t state; sctp_state_t state;
sctp_event_timeout_t to; sctp_event_timeout_t to;
sctp_counter_t counter;
void *ptr; void *ptr;
struct sctp_chunk *chunk; struct sctp_chunk *chunk;
struct sctp_association *asoc; struct sctp_association *asoc;
...@@ -165,7 +166,6 @@ SCTP_ARG_CONSTRUCTOR(U16, __u16, u16) ...@@ -165,7 +166,6 @@ SCTP_ARG_CONSTRUCTOR(U16, __u16, u16)
SCTP_ARG_CONSTRUCTOR(U8, __u8, u8) SCTP_ARG_CONSTRUCTOR(U8, __u8, u8)
SCTP_ARG_CONSTRUCTOR(ERROR, int, error) SCTP_ARG_CONSTRUCTOR(ERROR, int, error)
SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state) SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state)
SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter)
SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to)
SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr) SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr)
SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk) SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk)
......
...@@ -263,13 +263,6 @@ enum { SCTP_MIN_PMTU = 576 }; ...@@ -263,13 +263,6 @@ enum { SCTP_MIN_PMTU = 576 };
enum { SCTP_MAX_DUP_TSNS = 16 }; enum { SCTP_MAX_DUP_TSNS = 16 };
enum { SCTP_MAX_GABS = 16 }; enum { SCTP_MAX_GABS = 16 };
typedef enum {
SCTP_COUNTER_INIT_ERROR,
} sctp_counter_t;
/* How many counters does an association need? */
#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 */
......
...@@ -223,6 +223,22 @@ DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics); ...@@ -223,6 +223,22 @@ DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics);
extern int sctp_debug_flag; extern int sctp_debug_flag;
#define SCTP_DEBUG_PRINTK(whatever...) \ #define SCTP_DEBUG_PRINTK(whatever...) \
((void) (sctp_debug_flag && printk(KERN_DEBUG whatever))) ((void) (sctp_debug_flag && printk(KERN_DEBUG whatever)))
#define SCTP_DEBUG_PRINTK_IPADDR(lead, trail, leadparm, saddr, otherparms...) \
if (sctp_debug_flag) { \
if (saddr->sa.sa_family == AF_INET6) { \
printk(KERN_DEBUG \
lead "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" trail, \
leadparm, \
NIP6(saddr->v6.sin6_addr), \
otherparms); \
} else { \
printk(KERN_DEBUG \
lead "%u.%u.%u.%u" trail, \
leadparm, \
NIPQUAD(saddr->v4.sin_addr.s_addr), \
otherparms); \
} \
}
#define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; } #define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; }
#define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; } #define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; }
...@@ -236,6 +252,7 @@ extern int sctp_debug_flag; ...@@ -236,6 +252,7 @@ extern int sctp_debug_flag;
#else /* SCTP_DEBUG */ #else /* SCTP_DEBUG */
#define SCTP_DEBUG_PRINTK(whatever...) #define SCTP_DEBUG_PRINTK(whatever...)
#define SCTP_DEBUG_PRINTK_IPADDR(whatever...)
#define SCTP_ENABLE_DEBUG #define SCTP_ENABLE_DEBUG
#define SCTP_DISABLE_DEBUG #define SCTP_DISABLE_DEBUG
#define SCTP_ASSERT(expr, str, func) #define SCTP_ASSERT(expr, str, func)
......
...@@ -116,7 +116,8 @@ sctp_state_fn_t sctp_sf_eat_data_fast_4_4; ...@@ -116,7 +116,8 @@ sctp_state_fn_t sctp_sf_eat_data_fast_4_4;
sctp_state_fn_t sctp_sf_eat_sack_6_2; sctp_state_fn_t sctp_sf_eat_sack_6_2;
sctp_state_fn_t sctp_sf_tabort_8_4_8; sctp_state_fn_t sctp_sf_tabort_8_4_8;
sctp_state_fn_t sctp_sf_operr_notify; sctp_state_fn_t sctp_sf_operr_notify;
sctp_state_fn_t sctp_sf_t1_timer_expire; sctp_state_fn_t sctp_sf_t1_init_timer_expire;
sctp_state_fn_t sctp_sf_t1_cookie_timer_expire;
sctp_state_fn_t sctp_sf_t2_timer_expire; sctp_state_fn_t sctp_sf_t2_timer_expire;
sctp_state_fn_t sctp_sf_t4_timer_expire; sctp_state_fn_t sctp_sf_t4_timer_expire;
sctp_state_fn_t sctp_sf_t5_timer_expire; sctp_state_fn_t sctp_sf_t5_timer_expire;
...@@ -258,7 +259,10 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, ...@@ -258,7 +259,10 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *);
void sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, __u16 error); sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
__u16 error,
const struct sctp_association *asoc,
struct sctp_transport *transport);
/* Prototypes for statetable processing. */ /* Prototypes for statetable processing. */
......
...@@ -867,10 +867,13 @@ struct sctp_transport { ...@@ -867,10 +867,13 @@ struct sctp_transport {
*/ */
unsigned long last_time_ecne_reduced; unsigned long last_time_ecne_reduced;
/* active : The current active state of this destination, /* The number of times INIT has been sent on this transport. */
* : i.e. DOWN, UP, etc. int init_sent_count;
/* state : The current state of this destination,
* : i.e. SCTP_ACTIVE, SCTP_INACTIVE, SCTP_UNKOWN.
*/ */
int active; int state;
/* hb_allowed : The current heartbeat state of this destination, /* hb_allowed : The current heartbeat state of this destination,
* : i.e. ALLOW-HB, NO-HEARTBEAT, etc. * : i.e. ALLOW-HB, NO-HEARTBEAT, etc.
...@@ -1222,9 +1225,6 @@ struct sctp_endpoint { ...@@ -1222,9 +1225,6 @@ struct sctp_endpoint {
/* sendbuf acct. policy. */ /* sendbuf acct. policy. */
__u32 sndbuf_policy; __u32 sndbuf_policy;
/* Name for debugging output... */
char *debug_name;
}; };
/* Recover the outter endpoint structure. */ /* Recover the outter endpoint structure. */
...@@ -1314,11 +1314,23 @@ struct sctp_association { ...@@ -1314,11 +1314,23 @@ struct sctp_association {
* : association. Normally this information is * : association. Normally this information is
* : hashed or keyed for quick lookup and access * : hashed or keyed for quick lookup and access
* : of the TCB. * : of the TCB.
* : The list is also initialized with the list
* : of addresses passed with the sctp_connectx()
* : call.
* *
* It is a list of SCTP_transport's. * It is a list of SCTP_transport's.
*/ */
struct list_head transport_addr_list; struct list_head transport_addr_list;
/* transport_count
*
* Peer : A count of the number of peer addresses
* Transport : in the Peer Transport Address List.
* Address :
* Count :
*/
__u16 transport_count;
/* port /* port
* The transport layer port number. * The transport layer port number.
*/ */
...@@ -1486,6 +1498,9 @@ struct sctp_association { ...@@ -1486,6 +1498,9 @@ struct sctp_association {
/* Transport to which SHUTDOWN chunk was last sent. */ /* Transport to which SHUTDOWN chunk was last sent. */
struct sctp_transport *shutdown_last_sent_to; struct sctp_transport *shutdown_last_sent_to;
/* Transport to which INIT chunk was last sent. */
struct sctp_transport *init_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
* : ACK chunk to the peer and incremented each * : ACK chunk to the peer and incremented each
...@@ -1549,8 +1564,11 @@ struct sctp_association { ...@@ -1549,8 +1564,11 @@ struct sctp_association {
/* The message size at which SCTP fragmentation will occur. */ /* The message size at which SCTP fragmentation will occur. */
__u32 frag_point; __u32 frag_point;
/* Currently only one counter is used to count INIT errors. */ /* Counter used to count INIT errors. */
int counters[SCTP_NUMBER_COUNTERS]; int init_err_counter;
/* Count the number of INIT cycles (for doubling timeout). */
int init_cycle;
/* Default send parameters. */ /* Default send parameters. */
__u16 default_stream; __u16 default_stream;
...@@ -1708,6 +1726,8 @@ void sctp_association_free(struct sctp_association *); ...@@ -1708,6 +1726,8 @@ void sctp_association_free(struct sctp_association *);
void sctp_association_put(struct sctp_association *); void sctp_association_put(struct sctp_association *);
void sctp_association_hold(struct sctp_association *); void sctp_association_hold(struct sctp_association *);
struct sctp_transport *sctp_assoc_choose_init_transport(
struct sctp_association *);
struct sctp_transport *sctp_assoc_choose_shutdown_transport( struct sctp_transport *sctp_assoc_choose_shutdown_transport(
struct sctp_association *); struct sctp_association *);
void sctp_assoc_update_retran_path(struct sctp_association *); void sctp_assoc_update_retran_path(struct sctp_association *);
...@@ -1717,9 +1737,12 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc, ...@@ -1717,9 +1737,12 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
const union sctp_addr *laddr); const union sctp_addr *laddr);
struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *, struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *,
const union sctp_addr *address, const union sctp_addr *address,
const int gfp); const int gfp,
const int peer_state);
void sctp_assoc_del_peer(struct sctp_association *asoc, void sctp_assoc_del_peer(struct sctp_association *asoc,
const union sctp_addr *addr); const union sctp_addr *addr);
void sctp_assoc_rm_peer(struct sctp_association *asoc,
struct sctp_transport *peer);
void sctp_assoc_control_transport(struct sctp_association *, void sctp_assoc_control_transport(struct sctp_association *,
struct sctp_transport *, struct sctp_transport *,
sctp_transport_cmd_t, sctp_sn_error_t); sctp_transport_cmd_t, sctp_sn_error_t);
......
...@@ -111,6 +111,8 @@ enum sctp_optname { ...@@ -111,6 +111,8 @@ enum sctp_optname {
#define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM #define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM
SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */ SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS #define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
SCTP_SOCKOPT_CONNECTX, /* CONNECTX requests. */
#define SCTP_SOCKOPT_CONNECTX SCTP_SOCKOPT_CONNECTX
}; };
/* /*
...@@ -527,6 +529,7 @@ struct sctp_paddrinfo { ...@@ -527,6 +529,7 @@ struct sctp_paddrinfo {
enum sctp_spinfo_state { enum sctp_spinfo_state {
SCTP_INACTIVE, SCTP_INACTIVE,
SCTP_ACTIVE, SCTP_ACTIVE,
SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */
}; };
/* /*
......
...@@ -191,10 +191,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a ...@@ -191,10 +191,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->last_cwr_tsn = asoc->ctsn_ack_point; asoc->last_cwr_tsn = asoc->ctsn_ack_point;
asoc->unack_data = 0; asoc->unack_data = 0;
SCTP_DEBUG_PRINTK("myctsnap for %s INIT as 0x%x.\n",
asoc->ep->debug_name,
asoc->ctsn_ack_point);
/* 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
...@@ -211,6 +207,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a ...@@ -211,6 +207,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
/* Make an empty list of remote transport addresses. */ /* Make an empty list of remote transport addresses. */
INIT_LIST_HEAD(&asoc->peer.transport_addr_list); INIT_LIST_HEAD(&asoc->peer.transport_addr_list);
asoc->peer.transport_count = 0;
/* RFC 2960 5.1 Normal Establishment of an Association /* RFC 2960 5.1 Normal Establishment of an Association
* *
...@@ -288,6 +285,7 @@ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep, ...@@ -288,6 +285,7 @@ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep,
asoc->base.malloced = 1; asoc->base.malloced = 1;
SCTP_DBG_OBJCNT_INC(assoc); SCTP_DBG_OBJCNT_INC(assoc);
SCTP_DEBUG_PRINTK("Created asoc %p\n", asoc);
return asoc; return asoc;
...@@ -356,6 +354,8 @@ void sctp_association_free(struct sctp_association *asoc) ...@@ -356,6 +354,8 @@ void sctp_association_free(struct sctp_association *asoc)
sctp_transport_free(transport); sctp_transport_free(transport);
} }
asoc->peer.transport_count = 0;
/* Free any cached ASCONF_ACK chunk. */ /* Free any cached ASCONF_ACK chunk. */
if (asoc->addip_last_asconf_ack) if (asoc->addip_last_asconf_ack)
sctp_chunk_free(asoc->addip_last_asconf_ack); sctp_chunk_free(asoc->addip_last_asconf_ack);
...@@ -400,7 +400,7 @@ void sctp_assoc_set_primary(struct sctp_association *asoc, ...@@ -400,7 +400,7 @@ void sctp_assoc_set_primary(struct sctp_association *asoc,
/* If the primary path is changing, assume that the /* If the primary path is changing, assume that the
* user wants to use this new path. * user wants to use this new path.
*/ */
if (transport->active) if (transport->state != SCTP_INACTIVE)
asoc->peer.active_path = transport; asoc->peer.active_path = transport;
/* /*
...@@ -428,10 +428,58 @@ void sctp_assoc_set_primary(struct sctp_association *asoc, ...@@ -428,10 +428,58 @@ void sctp_assoc_set_primary(struct sctp_association *asoc,
transport->cacc.next_tsn_at_change = asoc->next_tsn; transport->cacc.next_tsn_at_change = asoc->next_tsn;
} }
/* Remove a transport from an association. */
void sctp_assoc_rm_peer(struct sctp_association *asoc,
struct sctp_transport *peer)
{
struct list_head *pos;
struct sctp_transport *transport;
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_rm_peer:association %p addr: ",
" port: %d\n",
asoc,
(&peer->ipaddr),
peer->ipaddr.v4.sin_port);
/* If we are to remove the current retran_path, update it
* to the next peer before removing this peer from the list.
*/
if (asoc->peer.retran_path == peer)
sctp_assoc_update_retran_path(asoc);
/* Remove this peer from the list. */
list_del(&peer->transports);
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
transport = list_entry(pos, struct sctp_transport, transports);
/* Update any entries that match the peer to be deleted. */
if (asoc->peer.primary_path == peer)
sctp_assoc_set_primary(asoc, transport);
if (asoc->peer.active_path == peer)
asoc->peer.active_path = transport;
if (asoc->peer.last_data_from == peer)
asoc->peer.last_data_from = transport;
/* If we remove the transport an INIT was last sent to, set it to
* NULL. Combined with the update of the retran path above, this
* will cause the next INIT to be sent to the next available
* transport, maintaining the cycle.
*/
if (asoc->init_last_sent_to == peer)
asoc->init_last_sent_to = NULL;
asoc->peer.transport_count--;
sctp_transport_free(peer);
}
/* Add a transport address to an association. */ /* Add a transport address to an association. */
struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
const union sctp_addr *addr, const union sctp_addr *addr,
int gfp) const int gfp,
const int peer_state)
{ {
struct sctp_transport *peer; struct sctp_transport *peer;
struct sctp_sock *sp; struct sctp_sock *sp;
...@@ -442,14 +490,25 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -442,14 +490,25 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* AF_INET and AF_INET6 share common port field. */ /* AF_INET and AF_INET6 share common port field. */
port = addr->v4.sin_port; port = addr->v4.sin_port;
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ",
" port: %d state:%s\n",
asoc,
addr,
addr->v4.sin_port,
peer_state == SCTP_UNKNOWN?"UNKNOWN":"ACTIVE");
/* Set the port if it has not been set yet. */ /* Set the port if it has not been set yet. */
if (0 == asoc->peer.port) if (0 == asoc->peer.port)
asoc->peer.port = port; asoc->peer.port = port;
/* Check to see if this is a duplicate. */ /* Check to see if this is a duplicate. */
peer = sctp_assoc_lookup_paddr(asoc, addr); peer = sctp_assoc_lookup_paddr(asoc, addr);
if (peer) if (peer) {
if (peer_state == SCTP_ACTIVE &&
peer->state == SCTP_UNKNOWN)
peer->state = SCTP_ACTIVE;
return peer; return peer;
}
peer = sctp_transport_new(addr, gfp); peer = sctp_transport_new(addr, gfp);
if (!peer) if (!peer)
...@@ -516,8 +575,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -516,8 +575,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Set the transport's RTO.initial value */ /* Set the transport's RTO.initial value */
peer->rto = asoc->rto_initial; peer->rto = asoc->rto_initial;
/* Set the peer's active state. */
peer->state = peer_state;
/* Attach the remote transport to our asoc. */ /* Attach the remote transport to our asoc. */
list_add_tail(&peer->transports, &asoc->peer.transport_addr_list); list_add_tail(&peer->transports, &asoc->peer.transport_addr_list);
asoc->peer.transport_count++;
/* If we do not yet have a primary path, set one. */ /* If we do not yet have a primary path, set one. */
if (!asoc->peer.primary_path) { if (!asoc->peer.primary_path) {
...@@ -525,8 +588,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -525,8 +588,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
asoc->peer.retran_path = peer; asoc->peer.retran_path = peer;
} }
if (asoc->peer.active_path == asoc->peer.retran_path) if (asoc->peer.active_path == asoc->peer.retran_path) {
asoc->peer.retran_path = peer; asoc->peer.retran_path = peer;
}
return peer; return peer;
} }
...@@ -537,37 +601,16 @@ void sctp_assoc_del_peer(struct sctp_association *asoc, ...@@ -537,37 +601,16 @@ void sctp_assoc_del_peer(struct sctp_association *asoc,
{ {
struct list_head *pos; struct list_head *pos;
struct list_head *temp; struct list_head *temp;
struct sctp_transport *peer = NULL;
struct sctp_transport *transport; struct sctp_transport *transport;
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, struct sctp_transport, transports); transport = list_entry(pos, struct sctp_transport, transports);
if (sctp_cmp_addr_exact(addr, &transport->ipaddr)) { if (sctp_cmp_addr_exact(addr, &transport->ipaddr)) {
peer = transport; /* Do book keeping for removing the peer and free it. */
list_del(pos); sctp_assoc_rm_peer(asoc, transport);
break; break;
} }
} }
/* The address we want delete is not in the association. */
if (!peer)
return;
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
transport = list_entry(pos, struct sctp_transport, transports);
/* Update any entries that match the peer to be deleted. */
if (asoc->peer.primary_path == peer)
sctp_assoc_set_primary(asoc, transport);
if (asoc->peer.active_path == peer)
asoc->peer.active_path = transport;
if (asoc->peer.retran_path == peer)
asoc->peer.retran_path = transport;
if (asoc->peer.last_data_from == peer)
asoc->peer.last_data_from = transport;
sctp_transport_free(peer);
} }
/* Lookup a transport by address. */ /* Lookup a transport by address. */
...@@ -608,12 +651,12 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, ...@@ -608,12 +651,12 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
/* Record the transition on the transport. */ /* Record the transition on the transport. */
switch (command) { switch (command) {
case SCTP_TRANSPORT_UP: case SCTP_TRANSPORT_UP:
transport->active = SCTP_ACTIVE; transport->state = SCTP_ACTIVE;
spc_state = SCTP_ADDR_AVAILABLE; spc_state = SCTP_ADDR_AVAILABLE;
break; break;
case SCTP_TRANSPORT_DOWN: case SCTP_TRANSPORT_DOWN:
transport->active = SCTP_INACTIVE; transport->state = SCTP_INACTIVE;
spc_state = SCTP_ADDR_UNREACHABLE; spc_state = SCTP_ADDR_UNREACHABLE;
break; break;
...@@ -643,7 +686,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, ...@@ -643,7 +686,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports); t = list_entry(pos, struct sctp_transport, transports);
if (!t->active) if (t->state == SCTP_INACTIVE)
continue; continue;
if (!first || t->last_time_heard > first->last_time_heard) { if (!first || t->last_time_heard > first->last_time_heard) {
second = first; second = first;
...@@ -663,7 +706,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, ...@@ -663,7 +706,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
* [If the primary is active but not most recent, bump the most * [If the primary is active but not most recent, bump the most
* recently used transport.] * recently used transport.]
*/ */
if (asoc->peer.primary_path->active && if (asoc->peer.primary_path->state != SCTP_INACTIVE &&
first != asoc->peer.primary_path) { first != asoc->peer.primary_path) {
second = first; second = first;
first = asoc->peer.primary_path; first = asoc->peer.primary_path;
...@@ -958,7 +1001,7 @@ void sctp_assoc_update(struct sctp_association *asoc, ...@@ -958,7 +1001,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
transports); transports);
if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr)) if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr))
sctp_assoc_add_peer(asoc, &trans->ipaddr, sctp_assoc_add_peer(asoc, &trans->ipaddr,
GFP_ATOMIC); GFP_ATOMIC, SCTP_ACTIVE);
} }
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
...@@ -998,7 +1041,7 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) ...@@ -998,7 +1041,7 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
/* Try to find an active transport. */ /* Try to find an active transport. */
if (t->active) { if (t->state != SCTP_INACTIVE) {
break; break;
} else { } else {
/* Keep track of the next transport in case /* Keep track of the next transport in case
...@@ -1019,6 +1062,40 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) ...@@ -1019,6 +1062,40 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
} }
asoc->peer.retran_path = t; asoc->peer.retran_path = t;
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
" %p addr: ",
" port: %d\n",
asoc,
(&t->ipaddr),
t->ipaddr.v4.sin_port);
}
/* Choose the transport for sending a INIT packet. */
struct sctp_transport *sctp_assoc_choose_init_transport(
struct sctp_association *asoc)
{
struct sctp_transport *t;
/* Use the retran path. If the last INIT was sent over the
* retran path, update the retran path and use it.
*/
if (!asoc->init_last_sent_to) {
t = asoc->peer.active_path;
} else {
if (asoc->init_last_sent_to == asoc->peer.retran_path)
sctp_assoc_update_retran_path(asoc);
t = asoc->peer.retran_path;
}
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
" %p addr: ",
" port: %d\n",
asoc,
(&t->ipaddr),
t->ipaddr.v4.sin_port);
return t;
} }
/* Choose the transport for sending a SHUTDOWN packet. */ /* Choose the transport for sending a SHUTDOWN packet. */
......
...@@ -134,7 +134,6 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, ...@@ -134,7 +134,6 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
ep->last_key = ep->current_key = 0; ep->last_key = ep->current_key = 0;
ep->key_changed_at = jiffies; ep->key_changed_at = jiffies;
ep->debug_name = "unnamedEndpoint";
return ep; return ep;
} }
......
...@@ -353,7 +353,7 @@ void sctp_icmp_proto_unreachable(struct sock *sk, ...@@ -353,7 +353,7 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
sctp_do_sm(SCTP_EVENT_T_OTHER, sctp_do_sm(SCTP_EVENT_T_OTHER,
SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH), SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
asoc->state, asoc->ep, asoc, NULL, asoc->state, asoc->ep, asoc, t,
GFP_ATOMIC); GFP_ATOMIC);
} }
......
...@@ -682,9 +682,9 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -682,9 +682,9 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
if (!new_transport) { if (!new_transport) {
new_transport = asoc->peer.active_path; new_transport = asoc->peer.active_path;
} else if (!new_transport->active) { } else if (new_transport->state == SCTP_INACTIVE) {
/* If the chunk is Heartbeat or Heartbeat Ack, /* If the chunk is Heartbeat or Heartbeat Ack,
* send it to chunk->transport, even if it's * send it to chunk->transport, even if it's
* inactive. * inactive.
* *
* 3.3.6 Heartbeat Acknowledgement: * 3.3.6 Heartbeat Acknowledgement:
...@@ -840,7 +840,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -840,7 +840,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
* Otherwise, we want to use the active path. * Otherwise, we want to use the active path.
*/ */
new_transport = chunk->transport; new_transport = chunk->transport;
if (!new_transport || !new_transport->active) if (!new_transport ||
new_transport->state == SCTP_INACTIVE)
new_transport = asoc->peer.active_path; new_transport = asoc->peer.active_path;
/* Change packets if necessary. */ /* Change packets if necessary. */
...@@ -1454,7 +1455,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, ...@@ -1454,7 +1455,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
/* Mark the destination transport address as /* Mark the destination transport address as
* active if it is not so marked. * active if it is not so marked.
*/ */
if (!transport->active) { if (transport->state == SCTP_INACTIVE) {
sctp_assoc_control_transport( sctp_assoc_control_transport(
transport->asoc, transport->asoc,
transport, transport,
......
...@@ -1830,7 +1830,7 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1830,7 +1830,7 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
* be a a better choice than any of the embedded addresses. * be a a better choice than any of the embedded addresses.
*/ */
if (peer_addr) if (peer_addr)
if(!sctp_assoc_add_peer(asoc, peer_addr, gfp)) if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE))
goto nomem; goto nomem;
/* Process the initialization parameters. */ /* Process the initialization parameters. */
...@@ -1841,6 +1841,14 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1841,6 +1841,14 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
goto clean_up; goto clean_up;
} }
/* Walk list of transports, removing transports in the UNKNOWN state. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
if (transport->state == SCTP_UNKNOWN) {
sctp_assoc_rm_peer(asoc, transport);
}
}
/* The fixed INIT headers are always in network byte /* The fixed INIT headers are always in network byte
* order. * order.
*/ */
...@@ -1906,7 +1914,8 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1906,7 +1914,8 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
* stream sequence number shall be set to 0. * stream sequence number shall be set to 0.
*/ */
/* Allocate storage for the negotiated streams if it is not a temporary * association. /* Allocate storage for the negotiated streams if it is not a temporary
* association.
*/ */
if (!asoc->temp) { if (!asoc->temp) {
int assoc_id; int assoc_id;
...@@ -1952,6 +1961,9 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, ...@@ -1952,6 +1961,9 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
list_del_init(pos); list_del_init(pos);
sctp_transport_free(transport); sctp_transport_free(transport);
} }
asoc->peer.transport_count = 0;
nomem: nomem:
return 0; return 0;
} }
...@@ -1995,7 +2007,7 @@ static int sctp_process_param(struct sctp_association *asoc, ...@@ -1995,7 +2007,7 @@ static int sctp_process_param(struct sctp_association *asoc,
af->from_addr_param(&addr, param.addr, asoc->peer.port, 0); af->from_addr_param(&addr, param.addr, asoc->peer.port, 0);
scope = sctp_scope(peer_addr); scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope)) if (sctp_in_scope(&addr, scope))
if (!sctp_assoc_add_peer(asoc, &addr, gfp)) if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_ACTIVE))
return 0; return 0;
break; break;
...@@ -2396,7 +2408,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc, ...@@ -2396,7 +2408,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc,
* Due to Resource Shortage'. * Due to Resource Shortage'.
*/ */
peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC); peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_ACTIVE);
if (!peer) if (!peer)
return SCTP_ERROR_RSRC_LOW; return SCTP_ERROR_RSRC_LOW;
......
...@@ -414,11 +414,13 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, ...@@ -414,11 +414,13 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
*/ */
asoc->overall_error_count++; asoc->overall_error_count++;
if (transport->active && if (transport->state != SCTP_INACTIVE &&
(transport->error_count++ >= transport->max_retrans)) { (transport->error_count++ >= transport->max_retrans)) {
SCTP_DEBUG_PRINTK("transport_strike: transport " SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
"IP:%d.%d.%d.%d failed.\n", " transport IP: port:%d failed.\n",
NIPQUAD(transport->ipaddr.v4.sin_addr)); asoc,
(&transport->ipaddr),
transport->ipaddr.v4.sin_port);
sctp_assoc_control_transport(asoc, transport, sctp_assoc_control_transport(asoc, transport,
SCTP_TRANSPORT_DOWN, SCTP_TRANSPORT_DOWN,
SCTP_FAILED_THRESHOLD); SCTP_FAILED_THRESHOLD);
...@@ -593,7 +595,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, ...@@ -593,7 +595,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
/* Mark the destination transport address as active if it is not so /* Mark the destination transport address as active if it is not so
* marked. * marked.
*/ */
if (!t->active) if (t->state == SCTP_INACTIVE)
sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
SCTP_HEARTBEAT_SUCCESS); SCTP_HEARTBEAT_SUCCESS);
...@@ -665,8 +667,11 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, ...@@ -665,8 +667,11 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
asoc->state = state; asoc->state = state;
SCTP_DEBUG_PRINTK("sctp_cmd_new_state: asoc %p[%s]\n",
asoc, sctp_state_tbl[state]);
if (sctp_style(sk, TCP)) { if (sctp_style(sk, TCP)) {
/* Change the sk->sk_state of a TCP-style socket that has /* Change the sk->sk_state of a TCP-style socket that has
* sucessfully completed a connect() call. * sucessfully completed a connect() call.
*/ */
if (sctp_state(asoc, ESTABLISHED) && sctp_sstate(sk, CLOSED)) if (sctp_state(asoc, ESTABLISHED) && sctp_sstate(sk, CLOSED))
...@@ -678,6 +683,16 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, ...@@ -678,6 +683,16 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
sk->sk_shutdown |= RCV_SHUTDOWN; sk->sk_shutdown |= RCV_SHUTDOWN;
} }
if (sctp_state(asoc, COOKIE_WAIT)) {
/* Reset init timeouts since they may have been
* increased due to timer expirations.
*/
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] =
asoc->ep->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT];
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] =
asoc->ep->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE];
}
if (sctp_state(asoc, ESTABLISHED) || if (sctp_state(asoc, ESTABLISHED) ||
sctp_state(asoc, CLOSED) || sctp_state(asoc, CLOSED) ||
sctp_state(asoc, SHUTDOWN_RECEIVED)) { sctp_state(asoc, SHUTDOWN_RECEIVED)) {
...@@ -1120,10 +1135,10 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1120,10 +1135,10 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
* to be executed only during failed attempts of * to be executed only during failed attempts of
* association establishment. * association establishment.
*/ */
if ((asoc->peer.retran_path != if ((asoc->peer.retran_path !=
asoc->peer.primary_path) && asoc->peer.primary_path) &&
(asoc->counters[SCTP_COUNTER_INIT_ERROR] > 0)) { (asoc->init_err_counter > 0)) {
sctp_add_cmd_sf(commands, sctp_add_cmd_sf(commands,
SCTP_CMD_FORCE_PRIM_RETRAN, SCTP_CMD_FORCE_PRIM_RETRAN,
SCTP_NULL()); SCTP_NULL());
} }
...@@ -1237,18 +1252,67 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1237,18 +1252,67 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
sctp_association_put(asoc); sctp_association_put(asoc);
break; break;
case SCTP_CMD_INIT_CHOOSE_TRANSPORT:
chunk = cmd->obj.ptr;
t = sctp_assoc_choose_init_transport(asoc);
asoc->init_last_sent_to = t;
chunk->transport = t;
t->init_sent_count++;
break;
case SCTP_CMD_INIT_RESTART: case SCTP_CMD_INIT_RESTART:
/* Do the needed accounting and updates /* Do the needed accounting and updates
* associated with restarting an initialization * associated with restarting an initialization
* timer. * timer. Only multiply the timeout by two if
* all transports have been tried at the current
* timeout.
*/
t = asoc->init_last_sent_to;
asoc->init_err_counter++;
if (t->init_sent_count > (asoc->init_cycle + 1)) {
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] *= 2;
if (asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] >
asoc->max_init_timeo) {
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] =
asoc->max_init_timeo;
}
asoc->init_cycle++;
SCTP_DEBUG_PRINTK(
"T1 INIT Timeout adjustment"
" init_err_counter: %d"
" cycle: %d"
" timeout: %d\n",
asoc->init_err_counter,
asoc->init_cycle,
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT]);
}
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
break;
case SCTP_CMD_COOKIEECHO_RESTART:
/* Do the needed accounting and updates
* associated with restarting an initialization
* timer. Only multiply the timeout by two if
* all transports have been tried at the current
* timeout.
*/ */
asoc->counters[SCTP_COUNTER_INIT_ERROR]++; asoc->init_err_counter++;
asoc->timeouts[cmd->obj.to] *= 2;
if (asoc->timeouts[cmd->obj.to] > asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] *= 2;
if (asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] >
asoc->max_init_timeo) { asoc->max_init_timeo) {
asoc->timeouts[cmd->obj.to] = asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] =
asoc->max_init_timeo; asoc->max_init_timeo;
} }
SCTP_DEBUG_PRINTK(
"T1 COOKIE Timeout adjustment"
" init_err_counter: %d"
" timeout: %d\n",
asoc->init_err_counter,
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE]);
/* If we've sent any data bundled with /* If we've sent any data bundled with
* COOKIE-ECHO we need to resend. * COOKIE-ECHO we need to resend.
...@@ -1261,7 +1325,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1261,7 +1325,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
sctp_add_cmd_sf(commands, sctp_add_cmd_sf(commands,
SCTP_CMD_TIMER_RESTART, SCTP_CMD_TIMER_RESTART,
SCTP_TO(cmd->obj.to)); SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
break; break;
case SCTP_CMD_INIT_FAILED: case SCTP_CMD_INIT_FAILED:
...@@ -1273,12 +1337,13 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1273,12 +1337,13 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
subtype, chunk, cmd->obj.u32); subtype, chunk, cmd->obj.u32);
break; break;
case SCTP_CMD_COUNTER_INC: case SCTP_CMD_INIT_COUNTER_INC:
asoc->counters[cmd->obj.counter]++; asoc->init_err_counter++;
break; break;
case SCTP_CMD_COUNTER_RESET: case SCTP_CMD_INIT_COUNTER_RESET:
asoc->counters[cmd->obj.counter] = 0; asoc->init_err_counter = 0;
asoc->init_cycle = 0;
break; break;
case SCTP_CMD_REPORT_DUP: case SCTP_CMD_REPORT_DUP:
......
...@@ -533,6 +533,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, ...@@ -533,6 +533,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT, sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT,
SCTP_PEER_INIT(initchunk)); SCTP_PEER_INIT(initchunk));
/* Reset init error count upon receipt of INIT-ACK. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());
/* 5.1 C) "A" shall stop the T1-init timer and leave /* 5.1 C) "A" shall stop the T1-init timer and leave
* COOKIE-WAIT state. "A" shall then ... start the T1-cookie * COOKIE-WAIT state. "A" shall then ... start the T1-cookie
* timer, and enter the COOKIE-ECHOED state. * timer, and enter the COOKIE-ECHOED state.
...@@ -775,8 +778,7 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, ...@@ -775,8 +778,7 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep,
* from the COOKIE-ECHOED state to the COOKIE-WAIT * from the COOKIE-ECHOED state to the COOKIE-WAIT
* state is performed. * state is performed.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_RESET, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_RESET, SCTP_NULL());
SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
/* RFC 2960 5.1 Normal Establishment of an Association /* RFC 2960 5.1 Normal Establishment of an Association
* *
...@@ -1019,10 +1021,22 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, ...@@ -1019,10 +1021,22 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
link = sctp_assoc_lookup_paddr(asoc, &from_addr); link = sctp_assoc_lookup_paddr(asoc, &from_addr);
/* This should never happen, but lets log it if so. */ /* This should never happen, but lets log it if so. */
if (!link) { if (unlikely(!link)) {
printk(KERN_WARNING if (from_addr.sa.sa_family == AF_INET6) {
"%s: Could not find address %d.%d.%d.%d\n", printk(KERN_WARNING
__FUNCTION__, NIPQUAD(from_addr.v4.sin_addr)); "%s association %p could not find address "
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__,
asoc,
NIP6(from_addr.v6.sin6_addr));
} else {
printk(KERN_WARNING
"%s association %p could not find address "
"%u.%u.%u.%u\n",
__FUNCTION__,
asoc,
NIPQUAD(from_addr.v4.sin_addr.s_addr));
}
return SCTP_DISPOSITION_DISCARD; return SCTP_DISPOSITION_DISCARD;
} }
...@@ -2095,9 +2109,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, ...@@ -2095,9 +2109,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
sctp_errhdr_t *err; sctp_errhdr_t *err;
struct sctp_chunk *reply; struct sctp_chunk *reply;
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
int attempts; int attempts = asoc->init_err_counter + 1;
attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1;
if (attempts >= asoc->max_init_attempts) { if (attempts >= asoc->max_init_attempts) {
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
...@@ -2157,8 +2169,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, ...@@ -2157,8 +2169,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
/* Cast away the const modifier, as we want to just /* Cast away the const modifier, as we want to just
* rerun it through as a sideffect. * rerun it through as a sideffect.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_COUNTER_INC, SCTP_NULL());
SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
...@@ -2281,8 +2292,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, ...@@ -2281,8 +2292,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
error = ((sctp_errhdr_t *)chunk->skb->data)->cause; error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
sctp_stop_t1_and_abort(commands, error); return sctp_stop_t1_and_abort(commands, error, asoc, chunk->transport);
return SCTP_DISPOSITION_ABORT;
} }
/* /*
...@@ -2294,8 +2304,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep ...@@ -2294,8 +2304,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR); return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR, asoc,
return SCTP_DISPOSITION_ABORT; (struct sctp_transport *)arg);
} }
/* /*
...@@ -2318,8 +2328,12 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, ...@@ -2318,8 +2328,12 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
* *
* This is common code called by several sctp_sf_*_abort() functions above. * This is common code called by several sctp_sf_*_abort() functions above.
*/ */
void sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, __u16 error) sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
__u16 error,
const struct sctp_association *asoc,
struct sctp_transport *transport)
{ {
SCTP_DEBUG_PRINTK("ABORT received (INIT).\n");
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED)); SCTP_STATE(SCTP_STATE_CLOSED));
SCTP_INC_STATS(SCTP_MIB_ABORTEDS); SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
...@@ -2328,6 +2342,7 @@ void sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, __u16 error) ...@@ -2328,6 +2342,7 @@ void sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, __u16 error)
/* CMD_INIT_FAILED will DELETE_TCB. */ /* CMD_INIT_FAILED will DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(error)); SCTP_U32(error));
return SCTP_DISPOSITION_ABORT;
} }
/* /*
...@@ -3805,6 +3820,10 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const struct sctp_endpoint *ep, ...@@ -3805,6 +3820,10 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const struct sctp_endpoint *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC,
SCTP_ASOC((struct sctp_association *) asoc)); SCTP_ASOC((struct sctp_association *) asoc));
/* Choose transport for INIT. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_CHOOSE_TRANSPORT,
SCTP_CHUNK(repl));
/* After sending the INIT, "A" starts the T1-init timer and /* After sending the INIT, "A" starts the T1-init timer and
* enters the COOKIE-WAIT state. * enters the COOKIE-WAIT state.
*/ */
...@@ -4589,7 +4608,7 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, ...@@ -4589,7 +4608,7 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep,
} }
/* /*
* sctp_sf_t1_timer_expire * sctp_sf_t1_init_timer_expire
* *
* Section: 4 Note: 2 * Section: 4 Note: 2
* Verification Tag: * Verification Tag:
...@@ -4603,7 +4622,59 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, ...@@ -4603,7 +4622,59 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep,
* endpoint MUST abort the initialization process and report the * endpoint MUST abort the initialization process and report the
* error to SCTP user. * error to SCTP user.
* *
* 3) If the T1-cookie timer expires, the endpoint MUST retransmit * Outputs
* (timers, events)
*
*/
sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *commands)
{
struct sctp_chunk *repl = NULL;
struct sctp_bind_addr *bp;
int attempts = asoc->init_err_counter + 1;
SCTP_DEBUG_PRINTK("Timer T1 expired (INIT).\n");
if (attempts < asoc->max_init_attempts) {
bp = (struct sctp_bind_addr *) &asoc->base.bind_addr;
repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0);
if (!repl)
return SCTP_DISPOSITION_NOMEM;
/* Choose transport for INIT. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_CHOOSE_TRANSPORT,
SCTP_CHUNK(repl));
/* Issue a sideeffect to do the needed accounting. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_RESTART,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
} else {
SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d"
" max_init_attempts: %d\n",
attempts, asoc->max_init_attempts);
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
return SCTP_DISPOSITION_DELETE_TCB;
}
return SCTP_DISPOSITION_CONSUME;
}
/*
* sctp_sf_t1_cookie_timer_expire
*
* Section: 4 Note: 2
* Verification Tag:
* Inputs
* (endpoint, asoc)
*
* RFC 2960 Section 4 Notes
* 3) If the T1-cookie timer expires, the endpoint MUST retransmit
* COOKIE ECHO and re-start the T1-cookie timer without changing * COOKIE ECHO and re-start the T1-cookie timer without changing
* state. This MUST be repeated up to 'Max.Init.Retransmits' times. * state. This MUST be repeated up to 'Max.Init.Retransmits' times.
* After that, the endpoint MUST abort the initialization process and * After that, the endpoint MUST abort the initialization process and
...@@ -4613,46 +4684,26 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep, ...@@ -4613,46 +4684,26 @@ sctp_disposition_t sctp_sf_do_6_2_sack(const struct sctp_endpoint *ep,
* (timers, events) * (timers, events)
* *
*/ */
sctp_disposition_t sctp_sf_t1_timer_expire(const struct sctp_endpoint *ep, sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep,
const struct sctp_association *asoc, const struct sctp_association *asoc,
const sctp_subtype_t type, const sctp_subtype_t type,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
struct sctp_chunk *repl; struct sctp_chunk *repl = NULL;
struct sctp_bind_addr *bp; int attempts = asoc->init_err_counter + 1;
sctp_event_timeout_t timer = (sctp_event_timeout_t) arg;
int timeout;
int attempts;
timeout = asoc->timeouts[timer];
attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1;
repl = NULL;
SCTP_DEBUG_PRINTK("Timer T1 expired.\n"); SCTP_DEBUG_PRINTK("Timer T1 expired (COOKIE-ECHO).\n");
if (attempts < asoc->max_init_attempts) { if (attempts < asoc->max_init_attempts) {
switch (timer) { repl = sctp_make_cookie_echo(asoc, NULL);
case SCTP_EVENT_TIMEOUT_T1_INIT:
bp = (struct sctp_bind_addr *) &asoc->base.bind_addr;
repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0);
break;
case SCTP_EVENT_TIMEOUT_T1_COOKIE:
repl = sctp_make_cookie_echo(asoc, NULL);
break;
default:
BUG();
break;
};
if (!repl) if (!repl)
goto nomem; return SCTP_DISPOSITION_NOMEM;
/* Issue a sideeffect to do the needed accounting. */ /* Issue a sideeffect to do the needed accounting. */
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_RESTART, sctp_add_cmd_sf(commands, SCTP_CMD_COOKIEECHO_RESTART,
SCTP_TO(timer)); SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
} else { } else {
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
...@@ -4661,9 +4712,6 @@ sctp_disposition_t sctp_sf_t1_timer_expire(const struct sctp_endpoint *ep, ...@@ -4661,9 +4712,6 @@ sctp_disposition_t sctp_sf_t1_timer_expire(const struct sctp_endpoint *ep,
} }
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem:
return SCTP_DISPOSITION_NOMEM;
} }
/* RFC2960 9.2 If the timer expires, the endpoint must re-send the SHUTDOWN /* RFC2960 9.2 If the timer expires, the endpoint must re-send the SHUTDOWN
......
...@@ -783,7 +783,8 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ ...@@ -783,7 +783,8 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_
/* SCTP_STATE_COOKIE_WAIT */ \ /* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \ /* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_t1_timer_expire, .name = "sctp_sf_t1_timer_expire"}, \ {.fn = sctp_sf_t1_cookie_timer_expire, \
.name = "sctp_sf_t1_cookie_timer_expire"}, \
/* SCTP_STATE_ESTABLISHED */ \ /* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \ /* SCTP_STATE_SHUTDOWN_PENDING */ \
...@@ -802,7 +803,8 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ ...@@ -802,7 +803,8 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_
/* SCTP_STATE_CLOSED */ \ /* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \ /* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_t1_timer_expire, .name = "sctp_sf_t1_timer_expire"}, \ {.fn = sctp_sf_t1_init_timer_expire, \
.name = "sctp_sf_t1_init_timer_expire"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \ /* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_ESTABLISHED */ \ /* SCTP_STATE_ESTABLISHED */ \
......
This diff is collapsed.
...@@ -83,7 +83,9 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, ...@@ -83,7 +83,9 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
peer->last_time_used = jiffies; peer->last_time_used = jiffies;
peer->last_time_ecne_reduced = jiffies; peer->last_time_ecne_reduced = jiffies;
peer->active = SCTP_ACTIVE; peer->init_sent_count = 0;
peer->state = SCTP_ACTIVE;
peer->hb_allowed = 0; peer->hb_allowed = 0;
/* Initialize the default path max_retrans. */ /* Initialize the default path max_retrans. */
......
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