2005-04-17 02:20:36 +04:00
/* SCTP kernel reference Implementation
* ( C ) Copyright IBM Corp . 2001 , 2004
* Copyright ( c ) 1999 Cisco , Inc .
* Copyright ( c ) 1999 - 2001 Motorola , Inc .
*
* This file is part of the SCTP kernel reference Implementation
*
* These functions work with the state functions in sctp_sm_statefuns . c
* to implement that state operations . These functions implement the
* steps which require modifying existing data structures .
*
* 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 :
* La Monte H . P . Yarroll < piggy @ acm . org >
* Karl Knutson < karl @ athena . chicago . il . us >
* Jon Grimm < jgrimm @ austin . ibm . com >
* Hui Huang < hui . huang @ nokia . com >
* Dajiang Zhang < dajiang . zhang @ nokia . 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
* be incorporated into the next SCTP release .
*/
# include <linux/skbuff.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/ip.h>
# include <net/sock.h>
# include <net/sctp/sctp.h>
# include <net/sctp/sm.h>
static int sctp_cmd_interpreter ( sctp_event_t event_type ,
sctp_subtype_t subtype ,
sctp_state_t state ,
struct sctp_endpoint * ep ,
struct sctp_association * asoc ,
void * event_arg ,
sctp_disposition_t status ,
sctp_cmd_seq_t * commands ,
2005-10-07 10:46:04 +04:00
gfp_t gfp ) ;
2005-04-17 02:20:36 +04:00
static int sctp_side_effects ( sctp_event_t event_type , sctp_subtype_t subtype ,
sctp_state_t state ,
struct sctp_endpoint * ep ,
struct sctp_association * asoc ,
void * event_arg ,
sctp_disposition_t status ,
sctp_cmd_seq_t * commands ,
2005-10-07 10:46:04 +04:00
gfp_t gfp ) ;
2005-04-17 02:20:36 +04:00
/********************************************************************
* Helper functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* A helper function for delayed processing of INET ECN CE bit. */
static void sctp_do_ecn_ce_work ( struct sctp_association * asoc ,
__u32 lowest_tsn )
{
/* Save the TSN away for comparison when we receive CWR */
asoc - > last_ecne_tsn = lowest_tsn ;
asoc - > need_ecne = 1 ;
}
/* Helper function for delayed processing of SCTP ECNE chunk. */
/* RFC 2960 Appendix A
*
* RFC 2481 details a specific bit for a sender to send in
* the header of its next outbound TCP segment to indicate to
* its peer that it has reduced its congestion window . This
* is termed the CWR bit . For SCTP the same indication is made
* by including the CWR chunk . This chunk contains one data
* element , i . e . the TSN number that was sent in the ECNE chunk .
* This element represents the lowest TSN number in the datagram
* that was originally marked with the CE bit .
*/
static struct sctp_chunk * sctp_do_ecn_ecne_work ( struct sctp_association * asoc ,
__u32 lowest_tsn ,
struct sctp_chunk * chunk )
{
struct sctp_chunk * repl ;
/* Our previously transmitted packet ran into some congestion
* so we should take action by reducing cwnd and ssthresh
* and then ACK our peer that we we ' ve done so by
* sending a CWR .
*/
/* First, try to determine if we want to actually lower
* our cwnd variables . Only lower them if the ECNE looks more
* recent than the last response .
*/
if ( TSN_lt ( asoc - > last_cwr_tsn , lowest_tsn ) ) {
struct sctp_transport * transport ;
/* Find which transport's congestion variables
* need to be adjusted .
*/
transport = sctp_assoc_lookup_tsn ( asoc , lowest_tsn ) ;
/* Update the congestion variables. */
if ( transport )
sctp_transport_lower_cwnd ( transport ,
SCTP_LOWER_CWND_ECNE ) ;
asoc - > last_cwr_tsn = lowest_tsn ;
}
/* Always try to quiet the other end. In case of lost CWR,
* resend last_cwr_tsn .
*/
repl = sctp_make_cwr ( asoc , asoc - > last_cwr_tsn , chunk ) ;
/* If we run out of memory, it will look like a lost CWR. We'll
* get back in sync eventually .
*/
return repl ;
}
/* Helper function to do delayed processing of ECN CWR chunk. */
static void sctp_do_ecn_cwr_work ( struct sctp_association * asoc ,
__u32 lowest_tsn )
{
/* Turn off ECNE getting auto-prepended to every outgoing
* packet
*/
asoc - > need_ecne = 0 ;
}
/* Generate SACK if necessary. We call this at the end of a packet. */
static int sctp_gen_sack ( struct sctp_association * asoc , int force ,
sctp_cmd_seq_t * commands )
{
__u32 ctsn , max_tsn_seen ;
struct sctp_chunk * sack ;
2005-12-22 22:36:46 +03:00
struct sctp_transport * trans = asoc - > peer . last_data_from ;
2005-04-17 02:20:36 +04:00
int error = 0 ;
2005-12-22 22:36:46 +03:00
if ( force | |
( ! trans & & ( asoc - > param_flags & SPP_SACKDELAY_DISABLE ) ) | |
( trans & & ( trans - > param_flags & SPP_SACKDELAY_DISABLE ) ) )
2005-04-17 02:20:36 +04:00
asoc - > peer . sack_needed = 1 ;
ctsn = sctp_tsnmap_get_ctsn ( & asoc - > peer . tsn_map ) ;
max_tsn_seen = sctp_tsnmap_get_max_tsn_seen ( & asoc - > peer . tsn_map ) ;
/* From 12.2 Parameters necessary per association (i.e. the TCB):
*
* Ack State : This flag indicates if the next received packet
* : is to be responded to with a SACK . . . .
* : When DATA chunks are out of order , SACK ' s
* : are not delayed ( see Section 6 ) .
*
* [ This is actually not mentioned in Section 6 , but we
* implement it here anyway . - - piggy ]
*/
if ( max_tsn_seen ! = ctsn )
asoc - > peer . sack_needed = 1 ;
/* From 6.2 Acknowledgement on Reception of DATA Chunks:
*
* Section 4.2 of [ RFC2581 ] SHOULD be followed . Specifically ,
* an acknowledgement SHOULD be generated for at least every
* second packet ( not every second DATA chunk ) received , and
* SHOULD be generated within 200 ms of the arrival of any
* unacknowledged DATA chunk . . . .
*/
if ( ! asoc - > peer . sack_needed ) {
/* We will need a SACK for the next packet. */
asoc - > peer . sack_needed = 1 ;
2005-12-22 22:36:46 +03:00
/* Set the SACK delay timeout based on the
* SACK delay for the last transport
* data was received from , or the default
* for the association .
*/
if ( trans )
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_SACK ] =
trans - > sackdelay ;
else
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_SACK ] =
asoc - > sackdelay ;
/* Restart the SACK timer. */
sctp_add_cmd_sf ( commands , SCTP_CMD_TIMER_RESTART ,
SCTP_TO ( SCTP_EVENT_TIMEOUT_SACK ) ) ;
2005-04-17 02:20:36 +04:00
} else {
if ( asoc - > a_rwnd > asoc - > rwnd )
asoc - > a_rwnd = asoc - > rwnd ;
sack = sctp_make_sack ( asoc ) ;
if ( ! sack )
goto nomem ;
asoc - > peer . sack_needed = 0 ;
error = sctp_outq_tail ( & asoc - > outqueue , sack ) ;
/* Stop the SACK timer. */
sctp_add_cmd_sf ( commands , SCTP_CMD_TIMER_STOP ,
SCTP_TO ( SCTP_EVENT_TIMEOUT_SACK ) ) ;
}
2005-12-22 22:36:46 +03:00
2005-04-17 02:20:36 +04:00
return error ;
nomem :
error = - ENOMEM ;
return error ;
}
/* When the T3-RTX timer expires, it calls this function to create the
* relevant state machine event .
*/
void sctp_generate_t3_rtx_event ( unsigned long peer )
{
int error ;
struct sctp_transport * transport = ( struct sctp_transport * ) peer ;
struct sctp_association * asoc = transport - > asoc ;
/* Check whether a task is in the sock. */
sctp_bh_lock_sock ( asoc - > base . sk ) ;
if ( sock_owned_by_user ( asoc - > base . sk ) ) {
SCTP_DEBUG_PRINTK ( " %s:Sock is busy. \n " , __FUNCTION__ ) ;
/* Try again later. */
if ( ! mod_timer ( & transport - > T3_rtx_timer , jiffies + ( HZ / 20 ) ) )
sctp_transport_hold ( transport ) ;
goto out_unlock ;
}
/* Is this transport really dead and just waiting around for
* the timer to let go of the reference ?
*/
if ( transport - > dead )
goto out_unlock ;
/* Run through the state machine. */
error = sctp_do_sm ( SCTP_EVENT_T_TIMEOUT ,
SCTP_ST_TIMEOUT ( SCTP_EVENT_TIMEOUT_T3_RTX ) ,
asoc - > state ,
asoc - > ep , asoc ,
transport , GFP_ATOMIC ) ;
if ( error )
asoc - > base . sk - > sk_err = - error ;
out_unlock :
sctp_bh_unlock_sock ( asoc - > base . sk ) ;
sctp_transport_put ( transport ) ;
}
/* This is a sa interface for producing timeout events. It works
* for timeouts which use the association as their parameter .
*/
static void sctp_generate_timeout_event ( struct sctp_association * asoc ,
sctp_event_timeout_t timeout_type )
{
int error = 0 ;
sctp_bh_lock_sock ( asoc - > base . sk ) ;
if ( sock_owned_by_user ( asoc - > base . sk ) ) {
SCTP_DEBUG_PRINTK ( " %s:Sock is busy: timer %d \n " ,
__FUNCTION__ ,
timeout_type ) ;
/* Try again later. */
if ( ! mod_timer ( & asoc - > timers [ timeout_type ] , jiffies + ( HZ / 20 ) ) )
sctp_association_hold ( asoc ) ;
goto out_unlock ;
}
/* Is this association really dead and just waiting around for
* the timer to let go of the reference ?
*/
if ( asoc - > base . dead )
goto out_unlock ;
/* Run through the state machine. */
error = sctp_do_sm ( SCTP_EVENT_T_TIMEOUT ,
SCTP_ST_TIMEOUT ( timeout_type ) ,
asoc - > state , asoc - > ep , asoc ,
( void * ) timeout_type , GFP_ATOMIC ) ;
if ( error )
asoc - > base . sk - > sk_err = - error ;
out_unlock :
sctp_bh_unlock_sock ( asoc - > base . sk ) ;
sctp_association_put ( asoc ) ;
}
static void sctp_generate_t1_cookie_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc , SCTP_EVENT_TIMEOUT_T1_COOKIE ) ;
}
static void sctp_generate_t1_init_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc , SCTP_EVENT_TIMEOUT_T1_INIT ) ;
}
static void sctp_generate_t2_shutdown_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc , SCTP_EVENT_TIMEOUT_T2_SHUTDOWN ) ;
}
static void sctp_generate_t4_rto_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc , SCTP_EVENT_TIMEOUT_T4_RTO ) ;
}
static void sctp_generate_t5_shutdown_guard_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc ,
SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD ) ;
} /* sctp_generate_t5_shutdown_guard_event() */
static void sctp_generate_autoclose_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc , SCTP_EVENT_TIMEOUT_AUTOCLOSE ) ;
}
/* Generate a heart beat event. If the sock is busy, reschedule. Make
* sure that the transport is still valid .
*/
void sctp_generate_heartbeat_event ( unsigned long data )
{
int error = 0 ;
struct sctp_transport * transport = ( struct sctp_transport * ) data ;
struct sctp_association * asoc = transport - > asoc ;
sctp_bh_lock_sock ( asoc - > base . sk ) ;
if ( sock_owned_by_user ( asoc - > base . sk ) ) {
SCTP_DEBUG_PRINTK ( " %s:Sock is busy. \n " , __FUNCTION__ ) ;
/* Try again later. */
if ( ! mod_timer ( & transport - > hb_timer , jiffies + ( HZ / 20 ) ) )
sctp_transport_hold ( transport ) ;
goto out_unlock ;
}
/* Is this structure just waiting around for us to actually
* get destroyed ?
*/
if ( transport - > dead )
goto out_unlock ;
error = sctp_do_sm ( SCTP_EVENT_T_TIMEOUT ,
SCTP_ST_TIMEOUT ( SCTP_EVENT_TIMEOUT_HEARTBEAT ) ,
asoc - > state , asoc - > ep , asoc ,
transport , GFP_ATOMIC ) ;
if ( error )
asoc - > base . sk - > sk_err = - error ;
out_unlock :
sctp_bh_unlock_sock ( asoc - > base . sk ) ;
sctp_transport_put ( transport ) ;
}
/* Inject a SACK Timeout event into the state machine. */
static void sctp_generate_sack_event ( unsigned long data )
{
struct sctp_association * asoc = ( struct sctp_association * ) data ;
sctp_generate_timeout_event ( asoc , SCTP_EVENT_TIMEOUT_SACK ) ;
}
sctp_timer_event_t * sctp_timer_events [ SCTP_NUM_TIMEOUT_TYPES ] = {
NULL ,
sctp_generate_t1_cookie_event ,
sctp_generate_t1_init_event ,
sctp_generate_t2_shutdown_event ,
NULL ,
sctp_generate_t4_rto_event ,
sctp_generate_t5_shutdown_guard_event ,
2005-11-12 03:06:16 +03:00
NULL ,
2005-04-17 02:20:36 +04:00
sctp_generate_sack_event ,
sctp_generate_autoclose_event ,
} ;
/* RFC 2960 8.2 Path Failure Detection
*
* When its peer endpoint is multi - homed , an endpoint should keep a
* error counter for each of the destination transport addresses of the
* peer endpoint .
*
* Each time the T3 - rtx timer expires on any address , or when a
* HEARTBEAT sent to an idle address is not acknowledged within a RTO ,
* the error counter of that destination address will be incremented .
* When the value in the error counter exceeds the protocol parameter
* ' Path . Max . Retrans ' of that destination address , the endpoint should
* mark the destination transport address as inactive , and a
* notification SHOULD be sent to the upper layer .
*
*/
static void sctp_do_8_2_transport_strike ( struct sctp_association * asoc ,
struct sctp_transport * transport )
{
/* The check for association's overall error counter exceeding the
* threshold is done in the state function .
*/
2006-07-22 01:48:50 +04:00
/* When probing UNCONFIRMED addresses, the association overall
* error count is NOT incremented
*/
if ( transport - > state ! = SCTP_UNCONFIRMED )
asoc - > overall_error_count + + ;
2005-04-17 02:20:36 +04:00
2005-06-21 00:14:57 +04:00
if ( transport - > state ! = SCTP_INACTIVE & &
2005-12-22 22:36:46 +03:00
( transport - > error_count + + > = transport - > pathmaxrxt ) ) {
2005-06-21 00:14:57 +04:00
SCTP_DEBUG_PRINTK_IPADDR ( " transport_strike:association %p " ,
" transport IP: port:%d failed. \n " ,
asoc ,
2006-11-21 04:04:10 +03:00
( & transport - > ipaddr_h ) ,
transport - > ipaddr_h . v4 . sin_port ) ;
2005-04-17 02:20:36 +04:00
sctp_assoc_control_transport ( asoc , transport ,
SCTP_TRANSPORT_DOWN ,
SCTP_FAILED_THRESHOLD ) ;
}
/* E2) For the destination address for which the timer
* expires , set RTO < - RTO * 2 ( " back off the timer " ) . The
* maximum value discussed in rule C7 above ( RTO . max ) may be
* used to provide an upper bound to this doubling operation .
*/
transport - > rto = min ( ( transport - > rto * 2 ) , transport - > asoc - > rto_max ) ;
}
/* Worker routine to handle INIT command failure. */
static void sctp_cmd_init_failed ( sctp_cmd_seq_t * commands ,
struct sctp_association * asoc ,
unsigned error )
{
struct sctp_ulpevent * event ;
event = sctp_ulpevent_make_assoc_change ( asoc , 0 , SCTP_CANT_STR_ASSOC ,
( __u16 ) error , 0 , 0 ,
GFP_ATOMIC ) ;
if ( event )
sctp_add_cmd_sf ( commands , SCTP_CMD_EVENT_ULP ,
SCTP_ULPEVENT ( event ) ) ;
sctp_add_cmd_sf ( commands , SCTP_CMD_NEW_STATE ,
SCTP_STATE ( SCTP_STATE_CLOSED ) ) ;
/* SEND_FAILED sent later when cleaning up the association. */
asoc - > outqueue . error = error ;
sctp_add_cmd_sf ( commands , SCTP_CMD_DELETE_TCB , SCTP_NULL ( ) ) ;
}
/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */
static void sctp_cmd_assoc_failed ( sctp_cmd_seq_t * commands ,
struct sctp_association * asoc ,
sctp_event_t event_type ,
sctp_subtype_t subtype ,
struct sctp_chunk * chunk ,
unsigned error )
{
struct sctp_ulpevent * event ;
/* Cancel any partial delivery in progress. */
sctp_ulpq_abort_pd ( & asoc - > ulpq , GFP_ATOMIC ) ;
event = sctp_ulpevent_make_assoc_change ( asoc , 0 , SCTP_COMM_LOST ,
( __u16 ) error , 0 , 0 ,
GFP_ATOMIC ) ;
if ( event )
sctp_add_cmd_sf ( commands , SCTP_CMD_EVENT_ULP ,
SCTP_ULPEVENT ( event ) ) ;
sctp_add_cmd_sf ( commands , SCTP_CMD_NEW_STATE ,
SCTP_STATE ( SCTP_STATE_CLOSED ) ) ;
/* SEND_FAILED sent later when cleaning up the association. */
asoc - > outqueue . error = error ;
sctp_add_cmd_sf ( commands , SCTP_CMD_DELETE_TCB , SCTP_NULL ( ) ) ;
}
/* 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
* since all other cases use " temporary " associations and can do all
* their work in statefuns directly .
*/
static int sctp_cmd_process_init ( sctp_cmd_seq_t * commands ,
struct sctp_association * asoc ,
struct sctp_chunk * chunk ,
2005-07-12 07:57:47 +04:00
sctp_init_chunk_t * peer_init ,
2005-10-07 10:46:04 +04:00
gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
int error ;
/* We only process the init as a sideeffect in a single
* case . This is when we process the INIT - ACK . If we
* fail during INIT processing ( due to malloc problems ) ,
* just return the error and stop processing the stack .
*/
if ( ! sctp_process_init ( asoc , chunk - > chunk_hdr - > type ,
sctp_source ( chunk ) , peer_init , gfp ) )
error = - ENOMEM ;
else
error = 0 ;
return error ;
}
/* Helper function to break out starting up of heartbeat timers. */
static void sctp_cmd_hb_timers_start ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc )
{
struct sctp_transport * t ;
struct list_head * pos ;
/* Start a heartbeat timer for each transport on the association.
* hold a reference on the transport to make sure none of
* the needed data structures go away .
*/
list_for_each ( pos , & asoc - > peer . transport_addr_list ) {
t = list_entry ( pos , struct sctp_transport , transports ) ;
if ( ! mod_timer ( & t - > hb_timer , sctp_transport_timeout ( t ) ) )
sctp_transport_hold ( t ) ;
}
}
static void sctp_cmd_hb_timers_stop ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc )
{
struct sctp_transport * t ;
struct list_head * pos ;
/* Stop all heartbeat timers. */
list_for_each ( pos , & asoc - > peer . transport_addr_list ) {
t = list_entry ( pos , struct sctp_transport , transports ) ;
if ( del_timer ( & t - > hb_timer ) )
sctp_transport_put ( t ) ;
}
}
/* Helper function to stop any pending T3-RTX timers */
static void sctp_cmd_t3_rtx_timers_stop ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc )
{
struct sctp_transport * t ;
struct list_head * pos ;
list_for_each ( pos , & asoc - > peer . transport_addr_list ) {
t = list_entry ( pos , struct sctp_transport , transports ) ;
if ( timer_pending ( & t - > T3_rtx_timer ) & &
del_timer ( & t - > T3_rtx_timer ) ) {
sctp_transport_put ( t ) ;
}
}
}
/* Helper function to update the heartbeat timer. */
static void sctp_cmd_hb_timer_update ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
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. */
static void sctp_cmd_transport_on ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
struct sctp_transport * t ,
struct sctp_chunk * chunk )
{
sctp_sender_hb_info_t * hbinfo ;
/* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the
* HEARTBEAT should clear the error counter of the destination
* transport address to which the HEARTBEAT was sent .
* The association ' s overall error count is also cleared .
*/
t - > error_count = 0 ;
t - > asoc - > overall_error_count = 0 ;
/* Mark the destination transport address as active if it is not so
* marked .
*/
2006-07-22 01:48:50 +04:00
if ( ( t - > state = = SCTP_INACTIVE ) | | ( t - > state = = SCTP_UNCONFIRMED ) )
2005-04-17 02:20:36 +04:00
sctp_assoc_control_transport ( asoc , t , SCTP_TRANSPORT_UP ,
SCTP_HEARTBEAT_SUCCESS ) ;
/* The receiver of the HEARTBEAT ACK should also perform an
* RTT measurement for that destination transport address
* using the time value carried in the HEARTBEAT ACK chunk .
*/
hbinfo = ( sctp_sender_hb_info_t * ) chunk - > skb - > data ;
sctp_transport_update_rto ( t , ( jiffies - hbinfo - > sent_at ) ) ;
2006-07-22 01:48:50 +04:00
/* Update the heartbeat timer. */
if ( ! mod_timer ( & t - > hb_timer , sctp_transport_timeout ( t ) ) )
sctp_transport_hold ( t ) ;
2005-04-17 02:20:36 +04:00
}
/* Helper function to do a transport reset at the expiry of the hearbeat
* timer .
*/
static void sctp_cmd_transport_reset ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
struct sctp_transport * t )
{
sctp_transport_lower_cwnd ( t , SCTP_LOWER_CWND_INACTIVE ) ;
/* Mark one strike against a transport. */
sctp_do_8_2_transport_strike ( asoc , t ) ;
}
/* Helper function to process the process SACK command. */
static int sctp_cmd_process_sack ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
struct sctp_sackhdr * sackh )
{
int err ;
if ( sctp_outq_sack ( & asoc - > outqueue , sackh ) ) {
/* There are no more TSNs awaiting SACK. */
err = sctp_do_sm ( SCTP_EVENT_T_OTHER ,
SCTP_ST_OTHER ( SCTP_EVENT_NO_PENDING_TSN ) ,
asoc - > state , asoc - > ep , asoc , NULL ,
GFP_ATOMIC ) ;
} else {
/* Windows may have opened, so we need
* to check if we have DATA to transmit
*/
err = sctp_outq_flush ( & asoc - > outqueue , 0 ) ;
}
return err ;
}
/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set
* the transport for a shutdown chunk .
*/
static void sctp_cmd_setup_t2 ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
struct sctp_chunk * chunk )
{
struct sctp_transport * t ;
t = sctp_assoc_choose_shutdown_transport ( asoc ) ;
asoc - > shutdown_last_sent_to = t ;
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T2_SHUTDOWN ] = t - > rto ;
chunk - > transport = t ;
}
/* Helper function to change the state of an association. */
static void sctp_cmd_new_state ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
sctp_state_t state )
{
struct sock * sk = asoc - > base . sk ;
asoc - > state = state ;
2005-06-21 00:14:57 +04:00
SCTP_DEBUG_PRINTK ( " sctp_cmd_new_state: asoc %p[%s] \n " ,
asoc , sctp_state_tbl [ state ] ) ;
2005-04-17 02:20:36 +04:00
if ( sctp_style ( sk , TCP ) ) {
2005-06-21 00:14:57 +04:00
/* Change the sk->sk_state of a TCP-style socket that has
2005-04-17 02:20:36 +04:00
* sucessfully completed a connect ( ) call .
*/
if ( sctp_state ( asoc , ESTABLISHED ) & & sctp_sstate ( sk , CLOSED ) )
sk - > sk_state = SCTP_SS_ESTABLISHED ;
/* Set the RCV_SHUTDOWN flag when a SHUTDOWN is received. */
if ( sctp_state ( asoc , SHUTDOWN_RECEIVED ) & &
sctp_sstate ( sk , ESTABLISHED ) )
sk - > sk_shutdown | = RCV_SHUTDOWN ;
}
2005-06-21 00:14:57 +04:00
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 ] =
2005-11-12 03:06:16 +03:00
asoc - > rto_initial ;
2005-06-21 00:14:57 +04:00
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T1_COOKIE ] =
2005-11-12 03:06:16 +03:00
asoc - > rto_initial ;
2005-06-21 00:14:57 +04:00
}
2005-04-17 02:20:36 +04:00
if ( sctp_state ( asoc , ESTABLISHED ) | |
sctp_state ( asoc , CLOSED ) | |
sctp_state ( asoc , SHUTDOWN_RECEIVED ) ) {
/* Wake up any processes waiting in the asoc's wait queue in
* sctp_wait_for_connect ( ) or sctp_wait_for_sndbuf ( ) .
*/
if ( waitqueue_active ( & asoc - > wait ) )
wake_up_interruptible ( & asoc - > wait ) ;
/* Wake up any processes waiting in the sk's sleep queue of
* a TCP - style or UDP - style peeled - off socket in
* sctp_wait_for_accept ( ) or sctp_wait_for_packet ( ) .
* For a UDP - style socket , the waiters are woken up by the
* notifications .
*/
if ( ! sctp_style ( sk , UDP ) )
sk - > sk_state_change ( sk ) ;
}
}
/* Helper function to delete an association. */
static void sctp_cmd_delete_tcb ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc )
{
struct sock * sk = asoc - > base . sk ;
/* If it is a non-temporary association belonging to a TCP-style
* listening socket that is not closed , do not free it so that accept ( )
* can pick it up later .
*/
if ( sctp_style ( sk , TCP ) & & sctp_sstate ( sk , LISTENING ) & &
( ! asoc - > temp ) & & ( sk - > sk_shutdown ! = SHUTDOWN_MASK ) )
return ;
sctp_unhash_established ( asoc ) ;
sctp_association_free ( asoc ) ;
}
/*
* ADDIP Section 4.1 ASCONF Chunk Procedures
* A4 ) Start a T - 4 RTO timer , using the RTO value of the selected
* destination address ( we use active path instead of primary path just
* because primary path may be inactive .
*/
static void sctp_cmd_setup_t4 ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
struct sctp_chunk * chunk )
{
struct sctp_transport * t ;
t = asoc - > peer . active_path ;
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T4_RTO ] = t - > rto ;
chunk - > transport = t ;
}
/* Process an incoming Operation Error Chunk. */
static void sctp_cmd_process_operr ( sctp_cmd_seq_t * cmds ,
struct sctp_association * asoc ,
struct sctp_chunk * chunk )
{
struct sctp_operr_chunk * operr_chunk ;
struct sctp_errhdr * err_hdr ;
operr_chunk = ( struct sctp_operr_chunk * ) chunk - > chunk_hdr ;
err_hdr = & operr_chunk - > err_hdr ;
switch ( err_hdr - > cause ) {
case SCTP_ERROR_UNKNOWN_CHUNK :
{
struct sctp_chunkhdr * unk_chunk_hdr ;
unk_chunk_hdr = ( struct sctp_chunkhdr * ) err_hdr - > variable ;
switch ( unk_chunk_hdr - > type ) {
/* ADDIP 4.1 A9) If the peer responds to an ASCONF with an
* ERROR chunk reporting that it did not recognized the ASCONF
* chunk type , the sender of the ASCONF MUST NOT send any
* further ASCONF chunks and MUST stop its T - 4 timer .
*/
case SCTP_CID_ASCONF :
asoc - > peer . asconf_capable = 0 ;
sctp_add_cmd_sf ( cmds , SCTP_CMD_TIMER_STOP ,
SCTP_TO ( SCTP_EVENT_TIMEOUT_T4_RTO ) ) ;
break ;
default :
break ;
}
break ;
}
default :
break ;
}
}
/* Process variable FWDTSN chunk information. */
static void sctp_cmd_process_fwdtsn ( struct sctp_ulpq * ulpq ,
struct sctp_chunk * chunk )
{
struct sctp_fwdtsn_skip * skip ;
/* Walk through all the skipped SSNs */
sctp_walk_fwdtsn ( skip , chunk ) {
sctp_ulpq_skip ( ulpq , ntohs ( skip - > stream ) , ntohs ( skip - > ssn ) ) ;
}
return ;
}
/* Helper function to remove the association non-primary peer
* transports .
*/
static void sctp_cmd_del_non_primary ( struct sctp_association * asoc )
{
struct sctp_transport * t ;
struct list_head * pos ;
struct list_head * temp ;
list_for_each_safe ( pos , temp , & asoc - > peer . transport_addr_list ) {
t = list_entry ( pos , struct sctp_transport , transports ) ;
2006-11-21 04:04:10 +03:00
if ( ! sctp_cmp_addr_exact ( & t - > ipaddr_h ,
2005-04-17 02:20:36 +04:00
& asoc - > peer . primary_addr ) ) {
2006-11-21 04:04:10 +03:00
sctp_assoc_del_peer ( asoc , & t - > ipaddr_h ) ;
2005-04-17 02:20:36 +04:00
}
}
return ;
}
2006-05-19 21:58:12 +04:00
/* Helper function to set sk_err on a 1-1 style socket. */
static void sctp_cmd_set_sk_err ( struct sctp_association * asoc , int error )
{
struct sock * sk = asoc - > base . sk ;
if ( ! sctp_style ( sk , UDP ) )
sk - > sk_err = error ;
}
2005-04-17 02:20:36 +04:00
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm ( ) to keep attention focused on the real
* functionality there .
*/
# define DEBUG_PRE \
SCTP_DEBUG_PRINTK ( " sctp_do_sm prefn: " \
" ep %p, %s, %s, asoc %p[%s], %s \n " , \
ep , sctp_evttype_tbl [ event_type ] , \
( * debug_fn ) ( subtype ) , asoc , \
sctp_state_tbl [ state ] , state_fn - > name )
# define DEBUG_POST \
SCTP_DEBUG_PRINTK ( " sctp_do_sm postfn: " \
" asoc %p, status: %s \n " , \
asoc , sctp_status_tbl [ status ] )
# define DEBUG_POST_SFX \
SCTP_DEBUG_PRINTK ( " sctp_do_sm post sfx: error %d, asoc %p[%s] \n " , \
error , asoc , \
sctp_state_tbl [ ( asoc & & sctp_id2assoc ( ep - > base . sk , \
sctp_assoc2id ( asoc ) ) ) ? asoc - > state : SCTP_STATE_CLOSED ] )
/*
* This is the master state machine processing function .
*
* If you want to understand all of lksctp , this is a
* good place to start .
*/
int sctp_do_sm ( sctp_event_t event_type , sctp_subtype_t subtype ,
sctp_state_t state ,
struct sctp_endpoint * ep ,
struct sctp_association * asoc ,
void * event_arg ,
2005-10-07 10:46:04 +04:00
gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
sctp_cmd_seq_t commands ;
const sctp_sm_table_entry_t * state_fn ;
sctp_disposition_t status ;
int error = 0 ;
typedef const char * ( printfn_t ) ( sctp_subtype_t ) ;
static printfn_t * table [ ] = {
NULL , sctp_cname , sctp_tname , sctp_oname , sctp_pname ,
} ;
printfn_t * debug_fn __attribute__ ( ( unused ) ) = table [ event_type ] ;
/* Look up the state function, run it, and then process the
* side effects . These three steps are the heart of lksctp .
*/
state_fn = sctp_sm_lookup_event ( event_type , state , subtype ) ;
sctp_init_cmd_seq ( & commands ) ;
DEBUG_PRE ;
status = ( * state_fn - > fn ) ( ep , asoc , subtype , event_arg , & commands ) ;
DEBUG_POST ;
error = sctp_side_effects ( event_type , subtype , state ,
ep , asoc , event_arg , status ,
& commands , gfp ) ;
DEBUG_POST_SFX ;
return error ;
}
# undef DEBUG_PRE
# undef DEBUG_POST
/*****************************************************************
* This the master state function side effect processing function .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int sctp_side_effects ( sctp_event_t event_type , sctp_subtype_t subtype ,
sctp_state_t state ,
struct sctp_endpoint * ep ,
struct sctp_association * asoc ,
void * event_arg ,
sctp_disposition_t status ,
sctp_cmd_seq_t * commands ,
2005-10-07 10:46:04 +04:00
gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
int error ;
/* FIXME - Most of the dispositions left today would be categorized
* as " exceptional " dispositions . For those dispositions , it
* may not be proper to run through any of the commands at all .
* For example , the command interpreter might be run only with
* disposition SCTP_DISPOSITION_CONSUME .
*/
if ( 0 ! = ( error = sctp_cmd_interpreter ( event_type , subtype , state ,
ep , asoc ,
event_arg , status ,
commands , gfp ) ) )
goto bail ;
switch ( status ) {
case SCTP_DISPOSITION_DISCARD :
SCTP_DEBUG_PRINTK ( " Ignored sctp protocol event - state %d, "
" event_type %d, event_id %d \n " ,
state , event_type , subtype . chunk ) ;
break ;
case SCTP_DISPOSITION_NOMEM :
/* We ran out of memory, so we need to discard this
* packet .
*/
/* BUG--we should now recover some memory, probably by
* reneging . . .
*/
error = - ENOMEM ;
break ;
case SCTP_DISPOSITION_DELETE_TCB :
/* This should now be a command. */
break ;
case SCTP_DISPOSITION_CONSUME :
case SCTP_DISPOSITION_ABORT :
/*
* We should no longer have much work to do here as the
* real work has been done as explicit commands above .
*/
break ;
case SCTP_DISPOSITION_VIOLATION :
printk ( KERN_ERR " sctp protocol violation state %d "
" chunkid %d \n " , state , subtype . chunk ) ;
break ;
case SCTP_DISPOSITION_NOT_IMPL :
printk ( KERN_WARNING " sctp unimplemented feature in state %d, "
" event_type %d, event_id %d \n " ,
state , event_type , subtype . chunk ) ;
break ;
case SCTP_DISPOSITION_BUG :
printk ( KERN_ERR " sctp bug in state %d, "
" event_type %d, event_id %d \n " ,
state , event_type , subtype . chunk ) ;
BUG ( ) ;
break ;
default :
printk ( KERN_ERR " sctp impossible disposition %d "
" in state %d, event_type %d, event_id %d \n " ,
status , state , event_type , subtype . chunk ) ;
BUG ( ) ;
break ;
} ;
bail :
return error ;
}
/********************************************************************
* 2 nd Level Abstractions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* This is the side-effect interpreter. */
static int sctp_cmd_interpreter ( sctp_event_t event_type ,
sctp_subtype_t subtype ,
sctp_state_t state ,
struct sctp_endpoint * ep ,
struct sctp_association * asoc ,
void * event_arg ,
sctp_disposition_t status ,
sctp_cmd_seq_t * commands ,
2005-10-07 10:46:04 +04:00
gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
int error = 0 ;
int force ;
sctp_cmd_t * cmd ;
struct sctp_chunk * new_obj ;
struct sctp_chunk * chunk = NULL ;
struct sctp_packet * packet ;
struct list_head * pos ;
struct timer_list * timer ;
unsigned long timeout ;
struct sctp_transport * t ;
struct sctp_sackhdr sackh ;
int local_cork = 0 ;
if ( SCTP_EVENT_T_TIMEOUT ! = event_type )
chunk = ( struct sctp_chunk * ) event_arg ;
/* Note: This whole file is a huge candidate for rework.
* For example , each command could either have its own handler , so
* the loop would look like :
* while ( cmds )
* cmd - > handle ( x , y , z )
* - - jgrimm
*/
while ( NULL ! = ( cmd = sctp_next_cmd ( commands ) ) ) {
switch ( cmd - > verb ) {
case SCTP_CMD_NOP :
/* Do nothing. */
break ;
case SCTP_CMD_NEW_ASOC :
/* Register a new association. */
if ( local_cork ) {
sctp_outq_uncork ( & asoc - > outqueue ) ;
local_cork = 0 ;
}
asoc = cmd - > obj . ptr ;
/* Register with the endpoint. */
sctp_endpoint_add_asoc ( ep , asoc ) ;
sctp_hash_established ( asoc ) ;
break ;
case SCTP_CMD_UPDATE_ASSOC :
sctp_assoc_update ( asoc , cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_PURGE_OUTQUEUE :
sctp_outq_teardown ( & asoc - > outqueue ) ;
break ;
case SCTP_CMD_DELETE_TCB :
if ( local_cork ) {
sctp_outq_uncork ( & asoc - > outqueue ) ;
local_cork = 0 ;
}
/* Delete the current association. */
sctp_cmd_delete_tcb ( commands , asoc ) ;
asoc = NULL ;
break ;
case SCTP_CMD_NEW_STATE :
/* Enter a new state. */
sctp_cmd_new_state ( commands , asoc , cmd - > obj . state ) ;
break ;
case SCTP_CMD_REPORT_TSN :
/* Record the arrival of a TSN. */
sctp_tsnmap_mark ( & asoc - > peer . tsn_map , cmd - > obj . u32 ) ;
break ;
case SCTP_CMD_REPORT_FWDTSN :
/* Move the Cumulattive TSN Ack ahead. */
sctp_tsnmap_skip ( & asoc - > peer . tsn_map , cmd - > obj . u32 ) ;
/* Abort any in progress partial delivery. */
sctp_ulpq_abort_pd ( & asoc - > ulpq , GFP_ATOMIC ) ;
break ;
case SCTP_CMD_PROCESS_FWDTSN :
sctp_cmd_process_fwdtsn ( & asoc - > ulpq , cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_GEN_SACK :
/* Generate a Selective ACK.
* The argument tells us whether to just count
* the packet and MAYBE generate a SACK , or
* force a SACK out .
*/
force = cmd - > obj . i32 ;
error = sctp_gen_sack ( asoc , force , commands ) ;
break ;
case SCTP_CMD_PROCESS_SACK :
/* Process an inbound SACK. */
error = sctp_cmd_process_sack ( commands , asoc ,
cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_GEN_INIT_ACK :
/* Generate an INIT ACK chunk. */
new_obj = sctp_make_init_ack ( asoc , chunk , GFP_ATOMIC ,
0 ) ;
if ( ! new_obj )
goto nomem ;
sctp_add_cmd_sf ( commands , SCTP_CMD_REPLY ,
SCTP_CHUNK ( new_obj ) ) ;
break ;
case SCTP_CMD_PEER_INIT :
/* Process a unified INIT from the peer.
* Note : Only used during INIT - ACK processing . If
* there is an error just return to the outter
* layer which will bail .
*/
error = sctp_cmd_process_init ( commands , asoc , chunk ,
cmd - > obj . ptr , gfp ) ;
break ;
case SCTP_CMD_GEN_COOKIE_ECHO :
/* Generate a COOKIE ECHO chunk. */
new_obj = sctp_make_cookie_echo ( asoc , chunk ) ;
if ( ! new_obj ) {
if ( cmd - > obj . ptr )
sctp_chunk_free ( cmd - > obj . ptr ) ;
goto nomem ;
}
sctp_add_cmd_sf ( commands , SCTP_CMD_REPLY ,
SCTP_CHUNK ( new_obj ) ) ;
/* If there is an ERROR chunk to be sent along with
* the COOKIE_ECHO , send it , too .
*/
if ( cmd - > obj . ptr )
sctp_add_cmd_sf ( commands , SCTP_CMD_REPLY ,
SCTP_CHUNK ( cmd - > obj . ptr ) ) ;
/* FIXME - Eventually come up with a cleaner way to
* enabling COOKIE - ECHO + DATA bundling during
* multihoming stale cookie scenarios , the following
* command plays with asoc - > peer . retran_path to
* avoid the problem of sending the COOKIE - ECHO and
* DATA in different paths , which could result
* in the association being ABORTed if the DATA chunk
* is processed first by the server . Checking the
* init error counter simply causes this command
* to be executed only during failed attempts of
* association establishment .
*/
2005-06-21 00:14:57 +04:00
if ( ( asoc - > peer . retran_path ! =
asoc - > peer . primary_path ) & &
( asoc - > init_err_counter > 0 ) ) {
sctp_add_cmd_sf ( commands ,
2005-04-17 02:20:36 +04:00
SCTP_CMD_FORCE_PRIM_RETRAN ,
SCTP_NULL ( ) ) ;
}
break ;
case SCTP_CMD_GEN_SHUTDOWN :
/* Generate SHUTDOWN when in SHUTDOWN_SENT state.
* Reset error counts .
*/
asoc - > overall_error_count = 0 ;
/* Generate a SHUTDOWN chunk. */
new_obj = sctp_make_shutdown ( asoc , chunk ) ;
if ( ! new_obj )
goto nomem ;
sctp_add_cmd_sf ( commands , SCTP_CMD_REPLY ,
SCTP_CHUNK ( new_obj ) ) ;
break ;
case SCTP_CMD_CHUNK_ULP :
/* Send a chunk to the sockets layer. */
SCTP_DEBUG_PRINTK ( " sm_sideff: %s %p, %s %p. \n " ,
" chunk_up: " , cmd - > obj . ptr ,
" ulpq: " , & asoc - > ulpq ) ;
sctp_ulpq_tail_data ( & asoc - > ulpq , cmd - > obj . ptr ,
GFP_ATOMIC ) ;
break ;
case SCTP_CMD_EVENT_ULP :
/* Send a notification to the sockets layer. */
SCTP_DEBUG_PRINTK ( " sm_sideff: %s %p, %s %p. \n " ,
" event_up: " , cmd - > obj . ptr ,
" ulpq: " , & asoc - > ulpq ) ;
sctp_ulpq_tail_event ( & asoc - > ulpq , cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_REPLY :
/* If an caller has not already corked, do cork. */
if ( ! asoc - > outqueue . cork ) {
sctp_outq_cork ( & asoc - > outqueue ) ;
local_cork = 1 ;
}
/* Send a chunk to our peer. */
error = sctp_outq_tail ( & asoc - > outqueue , cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_SEND_PKT :
/* Send a full packet to our peer. */
packet = cmd - > obj . ptr ;
sctp_packet_transmit ( packet ) ;
sctp_ootb_pkt_free ( packet ) ;
break ;
case SCTP_CMD_RETRAN :
/* Mark a transport for retransmission. */
sctp_retransmit ( & asoc - > outqueue , cmd - > obj . transport ,
SCTP_RTXR_T3_RTX ) ;
break ;
case SCTP_CMD_TRANSMIT :
/* Kick start transmission. */
error = sctp_outq_uncork ( & asoc - > outqueue ) ;
local_cork = 0 ;
break ;
case SCTP_CMD_ECN_CE :
/* Do delayed CE processing. */
sctp_do_ecn_ce_work ( asoc , cmd - > obj . u32 ) ;
break ;
case SCTP_CMD_ECN_ECNE :
/* Do delayed ECNE processing. */
new_obj = sctp_do_ecn_ecne_work ( asoc , cmd - > obj . u32 ,
chunk ) ;
if ( new_obj )
sctp_add_cmd_sf ( commands , SCTP_CMD_REPLY ,
SCTP_CHUNK ( new_obj ) ) ;
break ;
case SCTP_CMD_ECN_CWR :
/* Do delayed CWR processing. */
sctp_do_ecn_cwr_work ( asoc , cmd - > obj . u32 ) ;
break ;
case SCTP_CMD_SETUP_T2 :
sctp_cmd_setup_t2 ( commands , asoc , cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_TIMER_START :
timer = & asoc - > timers [ cmd - > obj . to ] ;
timeout = asoc - > timeouts [ cmd - > obj . to ] ;
2006-01-09 09:24:28 +03:00
BUG_ON ( ! timeout ) ;
2005-04-17 02:20:36 +04:00
timer - > expires = jiffies + timeout ;
sctp_association_hold ( asoc ) ;
add_timer ( timer ) ;
break ;
case SCTP_CMD_TIMER_RESTART :
timer = & asoc - > timers [ cmd - > obj . to ] ;
timeout = asoc - > timeouts [ cmd - > obj . to ] ;
if ( ! mod_timer ( timer , jiffies + timeout ) )
sctp_association_hold ( asoc ) ;
break ;
case SCTP_CMD_TIMER_STOP :
timer = & asoc - > timers [ cmd - > obj . to ] ;
if ( timer_pending ( timer ) & & del_timer ( timer ) )
sctp_association_put ( asoc ) ;
break ;
2005-06-21 00:14:57 +04:00
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 ;
2005-04-17 02:20:36 +04:00
case SCTP_CMD_INIT_RESTART :
/* Do the needed accounting and updates
* associated with restarting an initialization
2005-06-21 00:14:57 +04:00
* 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 "
2006-01-17 22:55:17 +03:00
" timeout: %ld \n " ,
2005-06-21 00:14:57 +04:00
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 .
2005-04-17 02:20:36 +04:00
*/
2005-06-21 00:14:57 +04:00
asoc - > init_err_counter + + ;
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T1_COOKIE ] * = 2 ;
if ( asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T1_COOKIE ] >
2005-04-17 02:20:36 +04:00
asoc - > max_init_timeo ) {
2005-06-21 00:14:57 +04:00
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T1_COOKIE ] =
2005-04-17 02:20:36 +04:00
asoc - > max_init_timeo ;
}
2005-06-21 00:14:57 +04:00
SCTP_DEBUG_PRINTK (
" T1 COOKIE Timeout adjustment "
" init_err_counter: %d "
2006-01-17 22:55:17 +03:00
" timeout: %ld \n " ,
2005-06-21 00:14:57 +04:00
asoc - > init_err_counter ,
asoc - > timeouts [ SCTP_EVENT_TIMEOUT_T1_COOKIE ] ) ;
2005-04-17 02:20:36 +04:00
/* If we've sent any data bundled with
* COOKIE - ECHO we need to resend .
*/
list_for_each ( pos , & asoc - > peer . transport_addr_list ) {
t = list_entry ( pos , struct sctp_transport ,
transports ) ;
sctp_retransmit_mark ( & asoc - > outqueue , t , 0 ) ;
}
sctp_add_cmd_sf ( commands ,
SCTP_CMD_TIMER_RESTART ,
2005-06-21 00:14:57 +04:00
SCTP_TO ( SCTP_EVENT_TIMEOUT_T1_COOKIE ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case SCTP_CMD_INIT_FAILED :
2006-11-21 04:00:44 +03:00
sctp_cmd_init_failed ( commands , asoc , cmd - > obj . err ) ;
2005-04-17 02:20:36 +04:00
break ;
case SCTP_CMD_ASSOC_FAILED :
sctp_cmd_assoc_failed ( commands , asoc , event_type ,
2006-11-21 04:01:06 +03:00
subtype , chunk , cmd - > obj . err ) ;
2005-04-17 02:20:36 +04:00
break ;
2005-06-21 00:14:57 +04:00
case SCTP_CMD_INIT_COUNTER_INC :
asoc - > init_err_counter + + ;
2005-04-17 02:20:36 +04:00
break ;
2005-06-21 00:14:57 +04:00
case SCTP_CMD_INIT_COUNTER_RESET :
asoc - > init_err_counter = 0 ;
asoc - > init_cycle = 0 ;
2005-04-17 02:20:36 +04:00
break ;
case SCTP_CMD_REPORT_DUP :
sctp_tsnmap_mark_dup ( & asoc - > peer . tsn_map ,
cmd - > obj . u32 ) ;
break ;
case SCTP_CMD_REPORT_BAD_TAG :
SCTP_DEBUG_PRINTK ( " vtag mismatch! \n " ) ;
break ;
case SCTP_CMD_STRIKE :
/* Mark one strike against a transport. */
sctp_do_8_2_transport_strike ( asoc , cmd - > obj . transport ) ;
break ;
case SCTP_CMD_TRANSPORT_RESET :
t = cmd - > obj . transport ;
sctp_cmd_transport_reset ( commands , asoc , t ) ;
break ;
case SCTP_CMD_TRANSPORT_ON :
t = cmd - > obj . transport ;
sctp_cmd_transport_on ( commands , asoc , t , chunk ) ;
break ;
case SCTP_CMD_HB_TIMERS_START :
sctp_cmd_hb_timers_start ( commands , asoc ) ;
break ;
case SCTP_CMD_HB_TIMER_UPDATE :
t = cmd - > obj . transport ;
sctp_cmd_hb_timer_update ( commands , asoc , t ) ;
break ;
case SCTP_CMD_HB_TIMERS_STOP :
sctp_cmd_hb_timers_stop ( commands , asoc ) ;
break ;
case SCTP_CMD_REPORT_ERROR :
error = cmd - > obj . error ;
break ;
case SCTP_CMD_PROCESS_CTSN :
/* Dummy up a SACK for processing. */
sackh . cum_tsn_ack = cmd - > obj . u32 ;
sackh . a_rwnd = 0 ;
sackh . num_gap_ack_blocks = 0 ;
sackh . num_dup_tsns = 0 ;
sctp_add_cmd_sf ( commands , SCTP_CMD_PROCESS_SACK ,
SCTP_SACKH ( & sackh ) ) ;
break ;
case SCTP_CMD_DISCARD_PACKET :
/* We need to discard the whole packet. */
chunk - > pdiscard = 1 ;
break ;
case SCTP_CMD_RTO_PENDING :
t = cmd - > obj . transport ;
t - > rto_pending = 1 ;
break ;
case SCTP_CMD_PART_DELIVER :
sctp_ulpq_partial_delivery ( & asoc - > ulpq , cmd - > obj . ptr ,
GFP_ATOMIC ) ;
break ;
case SCTP_CMD_RENEGE :
sctp_ulpq_renege ( & asoc - > ulpq , cmd - > obj . ptr ,
GFP_ATOMIC ) ;
break ;
case SCTP_CMD_SETUP_T4 :
sctp_cmd_setup_t4 ( commands , asoc , cmd - > obj . ptr ) ;
break ;
case SCTP_CMD_PROCESS_OPERR :
sctp_cmd_process_operr ( commands , asoc , chunk ) ;
break ;
case SCTP_CMD_CLEAR_INIT_TAG :
asoc - > peer . i . init_tag = 0 ;
break ;
case SCTP_CMD_DEL_NON_PRIMARY :
sctp_cmd_del_non_primary ( asoc ) ;
break ;
case SCTP_CMD_T3_RTX_TIMERS_STOP :
sctp_cmd_t3_rtx_timers_stop ( commands , asoc ) ;
break ;
case SCTP_CMD_FORCE_PRIM_RETRAN :
t = asoc - > peer . retran_path ;
asoc - > peer . retran_path = asoc - > peer . primary_path ;
error = sctp_outq_uncork ( & asoc - > outqueue ) ;
local_cork = 0 ;
asoc - > peer . retran_path = t ;
break ;
2006-05-19 21:58:12 +04:00
case SCTP_CMD_SET_SK_ERR :
sctp_cmd_set_sk_err ( asoc , cmd - > obj . error ) ;
break ;
2005-04-17 02:20:36 +04:00
default :
printk ( KERN_WARNING " Impossible command: %u, %p \n " ,
cmd - > verb , cmd - > obj . ptr ) ;
break ;
} ;
if ( error )
break ;
}
out :
if ( local_cork )
sctp_outq_uncork ( & asoc - > outqueue ) ;
return error ;
nomem :
error = - ENOMEM ;
goto out ;
}