SCTP: data retransmission & snd_space fix 21/11121/2
authorMarco Varlese <marco.varlese@suse.com>
Tue, 13 Mar 2018 14:44:56 +0000 (15:44 +0100)
committerFlorin Coras <florin.coras@gmail.com>
Tue, 13 Mar 2018 23:01:52 +0000 (23:01 +0000)
This patch addresses two things:
1) The data retransmission which needs to be taken care of when the
SCTP_TIMER_T3_RXTX;
2) The correct calculation of the amount of data transmittable
considered: the local window, the peer window and any data inflight.

Change-Id: I2d03a6cb43e4e7770c4910f8547c66e1026aeace
Signed-off-by: Marco Varlese <marco.varlese@suse.com>
src/vnet/sctp/sctp.c
src/vnet/sctp/sctp.h
src/vnet/sctp/sctp_output.c

index 76a1bf4..6e2dccc 100644 (file)
@@ -628,8 +628,17 @@ sctp_snd_space (sctp_connection_t * sctp_conn)
     return 0;
 
   u8 idx = sctp_data_subconn_select (sctp_conn);
+
+  u32 available_wnd =
+    clib_min (sctp_conn->peer_rwnd, sctp_conn->sub_conn[idx].cwnd);
+  int flight_size = (int) (sctp_conn->next_tsn - sctp_conn->last_unacked_tsn);
+
+  if (available_wnd <= flight_size)
+    return 0;
+
   /* Finally, let's subtract the DATA chunk headers overhead */
-  return sctp_conn->sub_conn[idx].cwnd -
+  return available_wnd -
+    flight_size -
     sizeof (sctp_payload_data_chunk_t) - sizeof (sctp_full_hdr_t);
 }
 
@@ -747,6 +756,7 @@ sctp_expired_timers_cb (u32 conn_index, u32 timer_id)
       break;
     case SCTP_TIMER_T3_RXTX:
       sctp_timer_reset (sctp_conn, conn_index, timer_id);
+      sctp_conn->flags |= SCTP_CONN_RECOVERY;
       sctp_data_retransmit (sctp_conn);
       break;
     case SCTP_TIMER_T4_HEARTBEAT:
index f0ce594..d9b2f56 100644 (file)
@@ -463,11 +463,12 @@ sctp_optparam_type_to_string (u8 type)
 #define SCTP_MAX_INIT_RETRANS 8        // number of attempts
 #define SCTP_HB_INTERVAL 30 * SHZ
 #define SCTP_HB_MAX_BURST 1
-
 #define SCTP_DATA_IDLE_INTERVAL 15 * SHZ       /* 15 seconds; the time-interval after which the connetion is considered IDLE */
-
 #define SCTP_TO_TIMER_TICK       SCTP_TICK*10  /* Period for converting from SCTP_TICK */
 
+#define SCTP_CONN_RECOVERY 1 << 1
+#define SCTP_FAST_RECOVERY 1 << 2
+
 typedef struct _sctp_lookup_dispatch
 {
   u8 next, error;
index 9ac3feb..f635192 100644 (file)
@@ -1456,10 +1456,65 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b)
   return 0;
 }
 
+u32
+sctp_prepare_data_retransmit (sctp_connection_t * sctp_conn,
+                             u8 idx,
+                             u32 offset,
+                             u32 max_deq_bytes, vlib_buffer_t ** b)
+{
+  sctp_main_t *tm = vnet_get_sctp_main ();
+  vlib_main_t *vm = vlib_get_main ();
+  int n_bytes = 0;
+  u32 bi, available_bytes, seg_size;
+  u8 *data;
+
+  ASSERT (sctp_conn->state >= SCTP_STATE_ESTABLISHED);
+  ASSERT (max_deq_bytes != 0);
+
+  /*
+   * Make sure we can retransmit something
+   */
+  available_bytes =
+    stream_session_tx_fifo_max_dequeue (&sctp_conn->sub_conn[idx].connection);
+  ASSERT (available_bytes >= offset);
+  available_bytes -= offset;
+  if (!available_bytes)
+    return 0;
+  max_deq_bytes = clib_min (sctp_conn->sub_conn[idx].cwnd, max_deq_bytes);
+  max_deq_bytes = clib_min (available_bytes, max_deq_bytes);
+
+  seg_size = max_deq_bytes;
+
+  /*
+   * Allocate and fill in buffer(s)
+   */
+
+  if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
+    return 0;
+  *b = vlib_get_buffer (vm, bi);
+  data = sctp_init_buffer (vm, *b);
+
+  /* Easy case, buffer size greater than mss */
+  if (PREDICT_TRUE (seg_size <= tm->bytes_per_buffer))
+    {
+      n_bytes =
+       stream_session_peek_bytes (&sctp_conn->sub_conn[idx].connection, data,
+                                  offset, max_deq_bytes);
+      ASSERT (n_bytes == max_deq_bytes);
+      b[0]->current_length = n_bytes;
+      sctp_push_hdr_i (sctp_conn, *b, sctp_conn->state);
+    }
+
+  return n_bytes;
+}
+
 void
 sctp_data_retransmit (sctp_connection_t * sctp_conn)
 {
-  /* TODO: requires use of PEEK/SEND */
+  vlib_main_t *vm = vlib_get_main ();
+  vlib_buffer_t *b = 0;
+  u32 bi, n_bytes = 0;
+
   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,
@@ -1467,6 +1522,24 @@ sctp_data_retransmit (sctp_connection_t * sctp_conn)
      sctp_conn->sub_conn[idx].connection.lcl_port,
      sctp_conn->sub_conn[idx].connection.rmt_port);
 
+  if (sctp_conn->state >= SCTP_STATE_ESTABLISHED)
+    {
+      return;
+    }
+
+  u8 idx = sctp_data_subconn_select (sctp_conn);
+
+  n_bytes =
+    sctp_prepare_data_retransmit (sctp_conn, idx, 0,
+                                 sctp_conn->sub_conn[idx].cwnd, &b);
+  if (n_bytes > 0)
+    SCTP_DBG_OUTPUT ("We have data (%u bytes) to retransmit", n_bytes);
+
+  bi = vlib_get_buffer_index (vm, b);
+
+  sctp_enqueue_to_output_now (vm, b, bi,
+                             sctp_conn->sub_conn[idx].connection.is_ip4);
+
   return;
 }