diff options
Diffstat (limited to 'src/vnet')
-rw-r--r-- | src/vnet/sctp/sctp.c | 34 | ||||
-rw-r--r-- | src/vnet/sctp/sctp.h | 12 | ||||
-rw-r--r-- | src/vnet/sctp/sctp_input.c | 20 | ||||
-rw-r--r-- | src/vnet/sctp/sctp_output.c | 82 |
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, |