SCTP: calculate RTO / RTT and RTTVAR as per RFC 99/10399/4
authorMarco Varlese <marco.varlese@suse.com>
Fri, 2 Feb 2018 16:17:51 +0000 (17:17 +0100)
committerFlorin Coras <florin.coras@gmail.com>
Mon, 5 Feb 2018 15:09:52 +0000 (15:09 +0000)
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 <marco.varlese@suse.com>
src/vnet/sctp/sctp.c
src/vnet/sctp/sctp.h
src/vnet/sctp/sctp_input.c
src/vnet/sctp/sctp_output.c

index d0f37f4..bd62c32 100644 (file)
@@ -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,
index 8f80d84..0634f01 100644 (file)
@@ -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)
index 4462450..0337e9f 100644 (file)
@@ -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);
 
index 3d870ff..ed9ffd3 100644 (file)
@@ -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;