aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/sctp
diff options
context:
space:
mode:
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,