From 21c8baf9a77b49af3dd54a7f64ce34dc932f10c3 Mon Sep 17 00:00:00 2001 From: Marco Varlese Date: Fri, 2 Feb 2018 17:17:51 +0100 Subject: SCTP: calculate RTO / RTT and RTTVAR as per RFC This patch addresses the need to calculate the RTO / RTT and RTTVAR according to the rules depicted by the RFC4960 at section 6.3.1 Change-Id: I1d346f3c67610070b3f602f32c7738d58b99ffed Signed-off-by: Marco Varlese --- src/vnet/sctp/sctp.c | 14 +++++++-- src/vnet/sctp/sctp.h | 73 ++++++++++++++++++++++++++++++++++++--------- src/vnet/sctp/sctp_input.c | 26 ++++++++++------ src/vnet/sctp/sctp_output.c | 56 +++++++++++++++++++++------------- 4 files changed, 122 insertions(+), 47 deletions(-) diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c index d0f37f4346e..bd62c3213e9 100644 --- a/src/vnet/sctp/sctp.c +++ b/src/vnet/sctp/sctp.c @@ -135,6 +135,7 @@ sctp_connection_timers_init (sctp_connection_t * sctp_conn) for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { sctp_conn->sub_conn[i].RTO = SCTP_RTO_INIT; + for (j = 0; j < SCTP_N_TIMERS; j++) { sctp_conn->sub_conn[i].timers[j] = SCTP_TIMER_HANDLE_INVALID; @@ -601,7 +602,6 @@ sctp_timer_init_handler (u32 conn_index, u32 timer_id) { sctp_connection_t *sctp_conn; - clib_warning (""); sctp_conn = sctp_connection_get (conn_index, vlib_get_thread_index ()); /* note: the connection may have already disappeared */ if (PREDICT_FALSE (sctp_conn == 0)) @@ -650,8 +650,6 @@ sctp_expired_timers_dispatch (u32 * expired_timers) (*sctp_timer_expiration_handlers[timer_id]) (connection_index, timer_id); } - - clib_warning (""); } void @@ -783,6 +781,15 @@ format_sctp_half_open (u8 * s, va_list * args) return format (s, "%U", format_sctp_connection_id, sctp_conn); } +void +sctp_update_time (f64 now, u8 thread_index) +{ + sctp_set_time_now (thread_index); + tw_timer_expire_timers_16t_2w_512sl (&sctp_main.timer_wheels[thread_index], + now); + sctp_flush_frames_to_output (thread_index); +} + /* *INDENT OFF* */ const static transport_proto_vft_t sctp_proto = { .enable = sctp_enable_disable, @@ -795,6 +802,7 @@ const static transport_proto_vft_t sctp_proto = { .send_mss = sctp_session_send_mss, .send_space = sctp_session_send_space, .tx_fifo_offset = NULL, //sctp_session_tx_fifo_offset, + .update_time = sctp_update_time, .get_connection = sctp_session_get_transport, .get_listener = sctp_session_get_listener, .get_half_open = sctp_half_open_session_get_transport, diff --git a/src/vnet/sctp/sctp.h b/src/vnet/sctp/sctp.h index 8f80d840c33..0634f019af7 100644 --- a/src/vnet/sctp/sctp.h +++ b/src/vnet/sctp/sctp.h @@ -79,9 +79,11 @@ typedef struct _sctp_sub_connection u32 cwnd; /**< The current congestion window. */ u32 ssthresh; /**< The current ssthresh value. */ + u32 rtt_ts; /**< USED to hold the timestamp of when the packet has been sent */ + u32 RTO; /**< The current retransmission timeout value. */ u32 SRTT; /**< The current smoothed round-trip time. */ - u32 RTTVAR; /**< The current RTT variation. */ + f32 RTTVAR; /**< The current RTT variation. */ u32 partially_acked_bytes; /**< The tracking method for increase of cwnd when in congestion avoidance mode (see Section 7.2.2).*/ @@ -195,9 +197,6 @@ typedef struct _sctp_connection u32 rcv_a_rwnd; /**< LOCAL max seg size that includes options. To be updated by congestion algos, etc. */ u32 snd_a_rwnd; /**< REMOTE max seg size that includes options. To be updated if peer pushes back on window, etc.*/ - u32 rtt_ts; - u32 rtt_seq; - u8 overall_sending_status; /**< 0 indicates first fragment of a user message 1 indicates normal stream 2 indicates last fragment of a user message */ @@ -523,7 +522,7 @@ sctp_timer_set (sctp_connection_t * tc, u8 conn_idx, u8 timer_id, SCTP_TIMER_HANDLE_INVALID); sctp_sub_connection_t *sub = &tc->sub_conn[conn_idx]; - tc->sub_conn[conn_idx].timers[timer_id] = + sub->timers[timer_id] = tw_timer_start_16t_2w_512sl (&sctp_main.timer_wheels[sub->c_thread_index], sub->c_c_index, timer_id, interval); } @@ -542,15 +541,6 @@ sctp_timer_reset (sctp_connection_t * tc, u8 conn_idx, u8 timer_id) sub->timers[timer_id] = SCTP_TIMER_HANDLE_INVALID; } -always_inline void -sctp_update_time (f64 now, u32 thread_index) -{ - sctp_set_time_now (thread_index); - tw_timer_expire_timers_16t_2w_512sl (&sctp_main.timer_wheels[thread_index], - now); - sctp_flush_frames_to_output (thread_index); -} - /** * Try to cleanup half-open connection * @@ -599,6 +589,61 @@ sctp_time_now (void) return sctp_main.time_now[vlib_get_thread_index ()]; } +#define ABS(x) ((x) > 0) ? (x) : -(x); + +always_inline void +sctp_calculate_rto (sctp_connection_t * sctp_conn, u8 conn_idx) +{ + /* See RFC4960, 6.3.1. RTO Calculation */ + u32 RTO = 0; + f32 RTTVAR = 0; + u32 now = sctp_time_now (); + u32 prev_ts = sctp_conn->sub_conn[conn_idx].rtt_ts; + u32 R = prev_ts - now; + + if (sctp_conn->sub_conn[conn_idx].RTO == 0) // C1: Let's initialize our RTO + { + sctp_conn->sub_conn[conn_idx].RTO = SCTP_RTO_MIN; + return; + } + + if (sctp_conn->sub_conn[conn_idx].RTO == SCTP_RTO_MIN && sctp_conn->sub_conn[conn_idx].SRTT == 0) // C2: First RTT calculation + { + sctp_conn->sub_conn[conn_idx].SRTT = R; + RTTVAR = R / 2; + + if (RTTVAR == 0) + RTTVAR = 100e-3; /* 100 ms */ + + sctp_conn->sub_conn[conn_idx].RTTVAR = RTTVAR; + } + else // C3: RTT already exists; let's recalculate + { + RTTVAR = (1 - SCTP_RTO_BETA) * sctp_conn->sub_conn[conn_idx].RTTVAR + + SCTP_RTO_BETA * ABS (sctp_conn->sub_conn[conn_idx].SRTT - R); + + if (RTTVAR == 0) + RTTVAR = 100e-3; /* 100 ms */ + + sctp_conn->sub_conn[conn_idx].RTTVAR = RTTVAR; + + sctp_conn->sub_conn[conn_idx].SRTT = + (1 - SCTP_RTO_ALPHA) * sctp_conn->sub_conn[conn_idx].SRTT + + SCTP_RTO_ALPHA * R; + } + + RTO = + sctp_conn->sub_conn[conn_idx].SRTT + + 4 * sctp_conn->sub_conn[conn_idx].RTTVAR; + if (RTO < SCTP_RTO_MIN) // C6 + RTO = SCTP_RTO_MIN; + + if (RTO > SCTP_RTO_MAX) // C7 + RTO = SCTP_RTO_MAX; + + sctp_conn->sub_conn[conn_idx].RTO = RTO; +} + always_inline void sctp_timer_update (sctp_connection_t * tc, u8 conn_idx, u8 timer_id, u32 interval) diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c index 44624500016..0337e9f8aa0 100644 --- a/src/vnet/sctp/sctp_input.c +++ b/src/vnet/sctp/sctp_input.c @@ -427,8 +427,8 @@ sctp_is_valid_init_ack (sctp_header_t * sctp_hdr, always_inline u16 sctp_handle_init_ack (sctp_header_t * sctp_hdr, sctp_chunks_common_hdr_t * sctp_chunk_hdr, - sctp_connection_t * sctp_conn, vlib_buffer_t * b0, - u16 sctp_implied_length) + sctp_connection_t * sctp_conn, u8 idx, + vlib_buffer_t * b0, u16 sctp_implied_length) { sctp_init_ack_chunk_t *init_ack_chunk = (sctp_init_ack_chunk_t *) (sctp_hdr); @@ -450,6 +450,8 @@ 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; + sctp_calculate_rto (sctp_conn, idx); + /* remote_tag to be placed in the VERIFICATION_TAG field of the COOKIE_ECHO chunk */ sctp_conn->remote_tag = init_ack_chunk->initiate_tag; sctp_conn->remote_initial_tsn = @@ -535,7 +537,7 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr, /* Start the T1_COOKIE timer */ sctp_timer_set (sctp_conn, sctp_pick_conn_idx_on_chunk (COOKIE_ECHO), - SCTP_TIMER_T1_COOKIE, SCTP_RTO_INIT); + SCTP_TIMER_T1_COOKIE, sctp_conn->sub_conn[idx].RTO); return SCTP_ERROR_NONE; } @@ -764,6 +766,7 @@ sctp_handle_cookie_echo (sctp_header_t * sctp_hdr, sctp_chunks_common_hdr_t * sctp_chunk_hdr, sctp_connection_t * sctp_conn, vlib_buffer_t * b0) { + u32 now = sctp_time_now (); /* Build TCB */ u8 idx = sctp_pick_conn_idx_on_chunk (COOKIE_ECHO); @@ -777,7 +780,8 @@ sctp_handle_cookie_echo (sctp_header_t * sctp_hdr, return SCTP_ERROR_INVALID_TAG; } - u32 now = sctp_time_now (); + sctp_calculate_rto (sctp_conn, idx); + u32 creation_time = clib_net_to_host_u32 (cookie_echo->cookie.creation_time); u32 cookie_lifespan = @@ -815,14 +819,14 @@ sctp_handle_cookie_ack (sctp_header_t * sctp_hdr, return SCTP_ERROR_INVALID_TAG; } + sctp_calculate_rto (sctp_conn, idx); + sctp_timer_reset (sctp_conn, idx, SCTP_TIMER_T1_COOKIE); /* Change state */ sctp_conn->state = SCTP_STATE_ESTABLISHED; stream_session_accept_notify (&sctp_conn->sub_conn[idx].connection); - sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T3_RXTX, SCTP_RTO_INIT); - return SCTP_ERROR_NONE; } @@ -959,11 +963,10 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, error0 = sctp_handle_init_ack (sctp_hdr, sctp_chunk_hdr, - new_sctp_conn, b0, + new_sctp_conn, idx, b0, sctp_implied_length); sctp_init_mss (new_sctp_conn); - //sctp_init_snd_vars (new_sctp_conn); if (session_stream_connect_notify (&new_sctp_conn->sub_conn[idx].connection, 0)) @@ -1416,7 +1419,12 @@ sctp_handle_sack (sctp_selective_ack_chunk_t * sack_chunk, return SCTP_ERROR_INVALID_TAG; } - sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX, SCTP_RTO_INIT); + sctp_calculate_rto (sctp_conn, idx); + + sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX, + sctp_conn->sub_conn[idx].RTO); + + sctp_conn->sub_conn[idx].RTO_pending = 0; *next0 = sctp_next_output (sctp_conn->sub_conn[idx].connection.is_ip4); diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c index 3d870ff5bd4..ed9ffd36262 100644 --- a/src/vnet/sctp/sctp_output.c +++ b/src/vnet/sctp/sctp_output.c @@ -556,6 +556,9 @@ sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, vnet_sctp_set_chunk_type (&cookie_ack_chunk->chunk_hdr, COOKIE_ACK); vnet_sctp_set_chunk_length (&cookie_ack_chunk->chunk_hdr, chunk_len); + /* Measure RTT with this */ + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); + vnet_buffer (b)->sctp.connection_index = sctp_conn->sub_conn[idx].connection.c_index; } @@ -589,6 +592,10 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, vnet_sctp_set_chunk_length (&cookie_echo_chunk->chunk_hdr, chunk_len); clib_memcpy (&(cookie_echo_chunk->cookie), sc, sizeof (sctp_state_cookie_param_t)); + + /* Measure RTT with this */ + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); + vnet_buffer (b)->sctp.connection_index = sctp_conn->sub_conn[idx].connection.c_index; } @@ -732,6 +739,9 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b, sctp_conn->local_tag = init_ack_chunk->initiate_tag; + /* Measure RTT with this */ + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); + vnet_buffer (b)->sctp.connection_index = sctp_conn->sub_conn[idx].connection.c_index; } @@ -799,6 +809,9 @@ sctp_send_shutdown (sctp_connection_t * sctp_conn) sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b); sctp_enqueue_to_output_now (vm, b, bi, sctp_conn->sub_conn[idx].connection.is_ip4); + + /* Measure RTT with this */ + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); } /** @@ -858,8 +871,12 @@ sctp_send_shutdown_ack (sctp_connection_t * sctp_conn) sctp_enqueue_to_ip_lookup (vm, b, bi, sctp_conn->sub_conn[idx].connection.is_ip4); + /* Measure RTT with this */ + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); + /* Start the SCTP_TIMER_T2_SHUTDOWN timer */ - sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN, SCTP_RTO_INIT); + sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN, + sctp_conn->sub_conn[idx].RTO); sctp_conn->state = SCTP_STATE_SHUTDOWN_ACK_SENT; } @@ -1034,35 +1051,28 @@ sctp_send_init (sctp_connection_t * sctp_conn) sctp_init_buffer (vm, b); sctp_prepare_init_chunk (sctp_conn, b); - /* Measure RTT with this */ - sctp_conn->rtt_ts = sctp_time_now (); - sctp_conn->rtt_seq = sctp_conn->next_tsn; - sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b); sctp_enqueue_to_ip_lookup_now (vm, b, bi, sctp_conn->sub_conn[idx].c_is_ip4); + /* Measure RTT with this */ + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); + /* Start the T1_INIT timer */ - sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_INIT, SCTP_RTO_INIT); + 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; } -always_inline u8 -sctp_in_cong_recovery (sctp_connection_t * sctp_conn) -{ - return 0; -} - /** * Push SCTP header and update connection variables */ static void -sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b, +sctp_push_hdr_i (sctp_connection_t * sctp_conn, u8 idx, vlib_buffer_t * b, sctp_state_t next_state) { - u8 idx = sctp_pick_conn_idx_on_chunk (DATA); - u16 data_len = b->current_length + b->total_length_not_including_first_buffer; ASSERT (!b->total_length_not_including_first_buffer @@ -1112,13 +1122,17 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b) { sctp_connection_t *sctp_conn = sctp_get_connection_from_transport (trans_conn); - sctp_push_hdr_i (sctp_conn, b, SCTP_STATE_ESTABLISHED); - if (sctp_conn->rtt_ts == 0 && !sctp_in_cong_recovery (sctp_conn)) + u8 idx = sctp_pick_conn_idx_on_chunk (DATA); + + sctp_push_hdr_i (sctp_conn, idx, b, SCTP_STATE_ESTABLISHED); + + if (sctp_conn->sub_conn[idx].RTO_pending == 0) { - sctp_conn->rtt_ts = sctp_time_now (); - sctp_conn->rtt_seq = sctp_conn->next_tsn; + sctp_conn->sub_conn[idx].RTO_pending = 1; + sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); } + sctp_trajectory_add_start (b0, 3); return 0; @@ -1341,14 +1355,14 @@ sctp46_output_inline (vlib_main_t * vm, { /* Start the SCTP_TIMER_T2_SHUTDOWN timer */ sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN, - SCTP_RTO_INIT); + sctp_conn->sub_conn[idx].RTO); sctp_conn->state = SCTP_STATE_SHUTDOWN_SENT; } if (chunk_type == DATA) { sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX, - SCTP_RTO_INIT); + sctp_conn->sub_conn[idx].RTO); } vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; -- cgit 1.2.3-korg