summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/vnet/sctp/sctp.c12
-rw-r--r--src/vnet/sctp/sctp.h5
-rw-r--r--src/vnet/sctp/sctp_output.c75
3 files changed, 88 insertions, 4 deletions
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c
index 76a1bf41eeb..6e2dccc552e 100644
--- a/src/vnet/sctp/sctp.c
+++ b/src/vnet/sctp/sctp.c
@@ -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:
diff --git a/src/vnet/sctp/sctp.h b/src/vnet/sctp/sctp.h
index f0ce5949285..d9b2f569e4a 100644
--- a/src/vnet/sctp/sctp.h
+++ b/src/vnet/sctp/sctp.h
@@ -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;
diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c
index 9ac3feb8aab..f63519212ec 100644
--- a/src/vnet/sctp/sctp_output.c
+++ b/src/vnet/sctp/sctp_output.c
@@ -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;
}