aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/sctp
diff options
context:
space:
mode:
authorMarco Varlese <marco.varlese@suse.com>2018-03-05 12:31:45 +0100
committerDamjan Marion <dmarion.lists@gmail.com>2018-03-05 13:46:32 +0000
commit9e09ff394ac0e731b5b33caf4d0cddff8de570b4 (patch)
tree5fdb5784570dadce34afc4cfe824f51a3f00e948 /src/vnet/sctp
parenteac3b11b67a9ee2d5b85c3e2e90220a04cd5db61 (diff)
SCTP: retransmission in INIT/SHUTDOWN phase
This patch addresses the need to handle timers timeouts (e.g. sent chunks not being acked) for both the INIT and SHUTDOWN phases. The INIT phase requires the handling of two timers the T1-init and T1-cookie timers whilst the SHUTDOWN phase requires the handling of the T2-shutdown timer only for the retransmission case. Left to be implemented is the handling of the DATA chunks retransmission (e.g. T3-rxtx expiration) but that will be submitted with a separate patch. Change-Id: I2b2e13dce11000aea3c7d965f02b27b76c97e605 Signed-off-by: Marco Varlese <marco.varlese@suse.com>
Diffstat (limited to 'src/vnet/sctp')
-rw-r--r--src/vnet/sctp/sctp.c34
-rw-r--r--src/vnet/sctp/sctp.h12
-rw-r--r--src/vnet/sctp/sctp_input.c20
-rw-r--r--src/vnet/sctp/sctp_output.c82
4 files changed, 124 insertions, 24 deletions
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c
index df5d86094a5..0e086a1f072 100644
--- a/src/vnet/sctp/sctp.c
+++ b/src/vnet/sctp/sctp.c
@@ -680,19 +680,6 @@ sctp_expired_timers_cb (u32 conn_index, u32 timer_id)
SCTP_DBG ("%s expired", sctp_timer_to_string (timer_id));
- switch (timer_id)
- {
- case SCTP_TIMER_T1_INIT:
- case SCTP_TIMER_T1_COOKIE:
- case SCTP_TIMER_T2_SHUTDOWN:
- case SCTP_TIMER_T3_RXTX:
- sctp_timer_reset (sctp_conn, conn_index, timer_id);
- break;
- case SCTP_TIMER_T4_HEARTBEAT:
- sctp_timer_reset (sctp_conn, conn_index, timer_id);
- goto heartbeat;
- }
-
if (sctp_conn->sub_conn[conn_index].unacknowledged_hb >
SCTP_PATH_MAX_RETRANS)
{
@@ -722,6 +709,27 @@ sctp_expired_timers_cb (u32 conn_index, u32 timer_id)
sctp_connection_cleanup (sctp_conn);
}
+ return;
+ }
+
+ switch (timer_id)
+ {
+ case SCTP_TIMER_T1_INIT:
+ sctp_send_init (sctp_conn);
+ break;
+ case SCTP_TIMER_T1_COOKIE:
+ sctp_send_cookie_echo (sctp_conn);
+ break;
+ case SCTP_TIMER_T2_SHUTDOWN:
+ sctp_send_shutdown (sctp_conn);
+ break;
+ case SCTP_TIMER_T3_RXTX:
+ sctp_timer_reset (sctp_conn, conn_index, timer_id);
+ sctp_data_retransmit (sctp_conn);
+ break;
+ case SCTP_TIMER_T4_HEARTBEAT:
+ sctp_timer_reset (sctp_conn, conn_index, timer_id);
+ goto heartbeat;
}
return;
diff --git a/src/vnet/sctp/sctp.h b/src/vnet/sctp/sctp.h
index 9f17e5337ba..2c649c9538a 100644
--- a/src/vnet/sctp/sctp.h
+++ b/src/vnet/sctp/sctp.h
@@ -200,6 +200,8 @@ typedef struct _sctp_connection
u32 overall_err_treshold; /**< The threshold for this association that if the Overall Error Count
reaches will cause this association to be torn down. */
+ u8 init_retransmit_err; /**< Error counter for the INIT transmission phase */
+
u32 peer_rwnd; /**< Current calculated value of the peer's rwnd. */
u32 next_tsn; /**< The next TSN number to be assigned to a new DATA chunk.
@@ -233,11 +235,12 @@ typedef struct _sctp_connection
1 indicates normal stream
2 indicates last fragment of a user message */
- sctp_options_t snd_opts;
-
u8 forming_association_changed; /**< This is a flag indicating whether the original association has been modified during
the life-span of the association itself. For instance, a new sub-connection might have been added. */
+ sctp_state_cookie_param_t cookie_param; /**< Temporary location to save cookie information; it can be used to
+ when timeout expires and sending again a COOKIE is require. */
+
} sctp_connection_t;
typedef void (sctp_timer_expiration_handler) (u32 conn_index, u32 timer_id);
@@ -268,12 +271,14 @@ void sctp_connection_del (sctp_connection_t * sctp_conn);
u32 sctp_push_header (transport_connection_t * tconn, vlib_buffer_t * b);
void sctp_send_init (sctp_connection_t * sctp_conn);
+void sctp_send_cookie_echo (sctp_connection_t * sctp_conn);
void sctp_send_shutdown (sctp_connection_t * sctp_conn);
void sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, u8 idx,
vlib_buffer_t * b);
void sctp_send_shutdown_complete (sctp_connection_t * sctp_conn, u8 idx,
vlib_buffer_t * b0);
void sctp_send_heartbeat (sctp_connection_t * sctp_conn);
+void sctp_data_retransmit (sctp_connection_t * sctp_conn);
void sctp_flush_frame_to_output (vlib_main_t * vm, u8 thread_index,
u8 is_ip4);
void sctp_flush_frames_to_output (u8 thread_index);
@@ -309,8 +314,7 @@ void
sctp_prepare_operation_error (sctp_connection_t * sctp_conn, u8 idx,
vlib_buffer_t * b, u8 err_cause);
void sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
- vlib_buffer_t * b,
- sctp_state_cookie_param_t * sc);
+ vlib_buffer_t * b, u8 reuse_buffer);
void sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
vlib_buffer_t * b);
void sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, u8 idx,
diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c
index 00d0a61feff..70adc7c3e34 100644
--- a/src/vnet/sctp/sctp_input.c
+++ b/src/vnet/sctp/sctp_input.c
@@ -374,7 +374,7 @@ sctp_handle_init (sctp_header_t * sctp_hdr,
SCTP_CONN_TRACKING_DBG ("sctp_conn->remote_initial_tsn = %u",
sctp_conn->remote_initial_tsn);
- sctp_conn->snd_opts.a_rwnd = clib_net_to_host_u32 (init_chunk->a_rwnd);
+ sctp_conn->peer_rwnd = clib_net_to_host_u32 (init_chunk->a_rwnd);
/*
* If the length specified in the INIT message is bigger than the size in bytes of our structure it means that
@@ -500,6 +500,9 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr,
if (sctp_is_bundling (sctp_implied_length, &init_ack_chunk->chunk_hdr))
return SCTP_ERROR_BUNDLING_VIOLATION;
+ /* Stop the T1_INIT timer */
+ sctp_timer_reset (sctp_conn, idx, SCTP_TIMER_T1_INIT);
+
sctp_calculate_rto (sctp_conn, idx);
/* remote_tag to be placed in the VERIFICATION_TAG field of the COOKIE_ECHO chunk */
@@ -510,7 +513,7 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr,
sctp_conn->next_tsn_expected = sctp_conn->remote_initial_tsn + 1;
SCTP_CONN_TRACKING_DBG ("sctp_conn->remote_initial_tsn = %u",
sctp_conn->remote_initial_tsn);
- sctp_conn->snd_opts.a_rwnd = clib_net_to_host_u32 (init_ack_chunk->a_rwnd);
+ sctp_conn->peer_rwnd = clib_net_to_host_u32 (init_ack_chunk->a_rwnd);
u16 length = vnet_sctp_get_chunk_length (sctp_chunk_hdr);
@@ -585,7 +588,10 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr,
}
}
- sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b0, &state_cookie);
+ clib_memcpy (&(sctp_conn->cookie_param), &state_cookie,
+ sizeof (sctp_state_cookie_param_t));
+
+ sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b0, 1);
/* Start the T1_COOKIE timer */
sctp_timer_set (sctp_conn, idx,
@@ -705,6 +711,14 @@ sctp_session_enqueue_data (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
always_inline u8
sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 idx, u8 is_gapping)
{
+ /* Section 4.4 of the RFC4960 */
+ if (sctp_conn->state == SCTP_STATE_SHUTDOWN_SENT)
+ {
+ SCTP_CONN_TRACKING_DBG ("sctp_conn->state = %s; SACK not delayable",
+ sctp_state_to_string (sctp_conn->state));
+ return 0;
+ }
+
if (is_gapping != 0)
{
SCTP_CONN_TRACKING_DBG
diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c
index 0c2fee1bb69..41fa1b31709 100644
--- a/src/vnet/sctp/sctp_output.c
+++ b/src/vnet/sctp/sctp_output.c
@@ -556,12 +556,12 @@ sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
void
sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
- vlib_buffer_t * b,
- sctp_state_cookie_param_t * sc)
+ vlib_buffer_t * b, u8 reuse_buffer)
{
vlib_main_t *vm = vlib_get_main ();
- sctp_reuse_buffer (vm, b);
+ if (reuse_buffer)
+ sctp_reuse_buffer (vm, b);
/* The minimum size of the message is given by the sctp_init_ack_chunk_t */
u16 alloc_bytes = sizeof (sctp_cookie_echo_chunk_t);
@@ -580,7 +580,7 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
cookie_echo_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag;
vnet_sctp_set_chunk_type (&cookie_echo_chunk->chunk_hdr, COOKIE_ECHO);
vnet_sctp_set_chunk_length (&cookie_echo_chunk->chunk_hdr, chunk_len);
- clib_memcpy (&(cookie_echo_chunk->cookie), sc,
+ clib_memcpy (&(cookie_echo_chunk->cookie), &sctp_conn->cookie_param,
sizeof (sctp_state_cookie_param_t));
vnet_buffer (b)->sctp.connection_index =
@@ -588,6 +588,52 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
vnet_buffer (b)->sctp.subconn_idx = idx;
}
+
+/*
+ * Send COOKIE_ECHO
+ */
+void
+sctp_send_cookie_echo (sctp_connection_t * sctp_conn)
+{
+ vlib_buffer_t *b;
+ u32 bi;
+ sctp_main_t *tm = vnet_get_sctp_main ();
+ vlib_main_t *vm = vlib_get_main ();
+
+ if (PREDICT_FALSE (sctp_conn->init_retransmit_err > SCTP_MAX_INIT_RETRANS))
+ {
+ clib_warning ("Reached MAX_INIT_RETRANS times. Aborting connection.");
+
+ session_stream_connect_notify (&sctp_conn->sub_conn
+ [MAIN_SCTP_SUB_CONN_IDX].connection, 1);
+
+ sctp_connection_timers_reset (sctp_conn);
+
+ sctp_connection_cleanup (sctp_conn);
+ }
+
+ if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
+ return;
+
+ b = vlib_get_buffer (vm, bi);
+ u8 idx = MAIN_SCTP_SUB_CONN_IDX;
+
+ sctp_init_buffer (vm, b);
+ sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b, 0);
+ sctp_enqueue_to_output_now (vm, b, bi, sctp_conn->sub_conn[idx].c_is_ip4);
+
+ /* Start the T1_INIT timer */
+ sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_INIT,
+ sctp_conn->sub_conn[idx].RTO);
+
+ /* Change state to COOKIE_WAIT */
+ sctp_conn->state = SCTP_STATE_COOKIE_WAIT;
+
+ /* Measure RTT with this */
+ sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
+}
+
+
/**
* Convert buffer to ERROR
*/
@@ -1270,6 +1316,20 @@ sctp_send_init (sctp_connection_t * sctp_conn)
sctp_main_t *tm = vnet_get_sctp_main ();
vlib_main_t *vm = vlib_get_main ();
+ if (PREDICT_FALSE (sctp_conn->init_retransmit_err > SCTP_MAX_INIT_RETRANS))
+ {
+ clib_warning ("Reached MAX_INIT_RETRANS times. Aborting connection.");
+
+ session_stream_connect_notify (&sctp_conn->sub_conn
+ [MAIN_SCTP_SUB_CONN_IDX].connection, 1);
+
+ sctp_connection_timers_reset (sctp_conn);
+
+ sctp_connection_cleanup (sctp_conn);
+
+ return;
+ }
+
if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
return;
@@ -1389,6 +1449,20 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b)
return 0;
}
+void
+sctp_data_retransmit (sctp_connection_t * sctp_conn)
+{
+ /* TODO: requires use of PEEK/SEND */
+ SCTP_DBG_OUTPUT
+ ("SCTP_CONN = %p, IDX = %u, S_INDEX = %u, C_INDEX = %u, sctp_conn->[...].LCL_PORT = %u, sctp_conn->[...].RMT_PORT = %u",
+ sctp_conn, idx, sctp_conn->sub_conn[idx].connection.s_index,
+ sctp_conn->sub_conn[idx].connection.c_index,
+ sctp_conn->sub_conn[idx].connection.lcl_port,
+ sctp_conn->sub_conn[idx].connection.rmt_port);
+
+ return;
+}
+
#if SCTP_DEBUG_STATE_MACHINE
always_inline u8
sctp_validate_output_state_machine (sctp_connection_t * sctp_conn,