SCTP: congestion control
This patch addresses the requirements depicted by section 7.1.1 and
7.1.2 of the RFC 4960. Specifically, it implements the Slow-start and
Congestion-avoidance policies.
The patch also took care of correctly implementing some 'formatting'
functions required - for instance - in packet(s) tracing.
Change-Id: I68eade1b30345de3acb3ac8a653a5ef76eb6d2ac
Signed-off-by: Marco Varlese <marco.varlese@suse.com>
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c
index 224c97d..b81d4d0 100644
--- a/src/vnet/sctp/sctp.c
+++ b/src/vnet/sctp/sctp.c
@@ -42,6 +42,8 @@
ip_copy (&listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.lcl_ip,
&tep->ip, tep->is_ip4);
+ listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].PMTU =
+ vnet_sw_interface_get_mtu (vnet_get_main (), tep->sw_if_index, VLIB_TX);
listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.is_ip4 = tep->is_ip4;
listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.proto =
TRANSPORT_PROTO_SCTP;
@@ -178,25 +180,44 @@
u8 *
format_sctp_connection_id (u8 * s, va_list * args)
{
- /*
- sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *);
- if (!sctp_conn)
- return s;
- if (sctp_conn->c_is_ip4)
- {
- s = format (s, "[#%d][%s] %U:%d->%U:%d", sctp_conn->c_thread_index, "T",
- format_ip4_address, &sctp_conn->c_lcl_ip4,
- clib_net_to_host_u16 (sctp_conn->c_lcl_port), format_ip4_address,
- &sctp_conn->c_rmt_ip4, clib_net_to_host_u16 (sctp_conn->c_rmt_port));
- }
- else
- {
- s = format (s, "[#%d][%s] %U:%d->%U:%d", sctp_conn->c_thread_index, "T",
- format_ip6_address, &sctp_conn->c_lcl_ip6,
- clib_net_to_host_u16 (sctp_conn->c_lcl_port), format_ip6_address,
- &sctp_conn->c_rmt_ip6, clib_net_to_host_u16 (sctp_conn->c_rmt_port));
- }
- */
+ sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *);
+ if (!sctp_conn)
+ return s;
+
+ u8 i;
+ for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
+ {
+ if (sctp_conn->sub_conn[i].connection.is_ip4)
+ {
+ s = format (s, "%U[#%d][%s] %U:%d->%U:%d",
+ s,
+ sctp_conn->sub_conn[i].connection.thread_index,
+ "T",
+ format_ip4_address,
+ &sctp_conn->sub_conn[i].connection.lcl_ip.ip4,
+ clib_net_to_host_u16 (sctp_conn->sub_conn[i].
+ connection.lcl_port),
+ format_ip4_address,
+ &sctp_conn->sub_conn[i].connection.rmt_ip.ip4,
+ clib_net_to_host_u16 (sctp_conn->sub_conn[i].
+ connection.rmt_port));
+ }
+ else
+ {
+ s = format (s, "%U[#%d][%s] %U:%d->%U:%d",
+ s,
+ sctp_conn->sub_conn[i].connection.thread_index,
+ "T",
+ format_ip6_address,
+ &sctp_conn->sub_conn[i].connection.lcl_ip.ip6,
+ clib_net_to_host_u16 (sctp_conn->sub_conn[i].
+ connection.lcl_port),
+ format_ip6_address,
+ &sctp_conn->sub_conn[i].connection.rmt_ip.ip6,
+ clib_net_to_host_u16 (sctp_conn->sub_conn[i].
+ connection.rmt_port));
+ }
+ }
return s;
}
@@ -235,48 +256,11 @@
time_now = sctp_time_now ();
sctp_conn->local_initial_tsn = random_u32 (&time_now);
+ sctp_conn->last_unacked_tsn = sctp_conn->local_initial_tsn;
+ sctp_conn->next_tsn = sctp_conn->local_initial_tsn + 1;
+
sctp_conn->remote_initial_tsn = 0x0;
sctp_conn->last_rcvd_tsn = sctp_conn->remote_initial_tsn;
- sctp_conn->next_tsn = sctp_conn->local_initial_tsn + 1;
-}
-
-/**
- * Update max segment size we're able to process.
- *
- * The value is constrained by our interface's MTU and IP options. It is
- * also what we advertise to our peer.
- */
-void
-sctp_update_rcv_mss (sctp_connection_t * sctp_conn)
-{
- sctp_conn->smallest_PMTU = DEFAULT_A_RWND; /* TODO find our iface MTU */
- sctp_conn->a_rwnd = DEFAULT_A_RWND - sizeof (sctp_full_hdr_t);
- sctp_conn->rcv_opts.a_rwnd = sctp_conn->a_rwnd;
- sctp_conn->rcv_a_rwnd = sctp_conn->a_rwnd; /* This will be updated by our congestion algos */
-}
-
-void
-sctp_init_mss (sctp_connection_t * sctp_conn)
-{
- SCTP_DBG ("CONN_INDEX = %u",
- sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.c_index);
-
- u16 default_a_rwnd = 536;
- sctp_update_rcv_mss (sctp_conn);
-
- /* TODO cache mss and consider PMTU discovery */
- sctp_conn->snd_a_rwnd =
- clib_min (sctp_conn->rcv_opts.a_rwnd, sctp_conn->a_rwnd);
-
- if (sctp_conn->snd_a_rwnd < sizeof (sctp_full_hdr_t))
- {
- SCTP_ADV_DBG ("sctp_conn->snd_a_rwnd < sizeof(sctp_full_hdr_t)");
- /* Assume that at least the min default mss works */
- sctp_conn->snd_a_rwnd = default_a_rwnd;
- sctp_conn->rcv_opts.a_rwnd = default_a_rwnd;
- }
-
- ASSERT (sctp_conn->snd_a_rwnd > sizeof (sctp_full_hdr_t));
}
always_inline sctp_connection_t *
@@ -384,6 +368,8 @@
clib_spinlock_lock_if_init (&tm->half_open_lock);
sctp_conn = sctp_half_open_connection_new (thread_id);
+ sctp_conn->sub_conn[idx].PMTU =
+ vnet_sw_interface_get_mtu (vnet_get_main (), rmt->sw_if_index, VLIB_TX);
transport_connection_t *trans_conn = &sctp_conn->sub_conn[idx].connection;
ip_copy (&trans_conn->rmt_ip, &rmt->ip, rmt->is_ip4);
@@ -462,7 +448,8 @@
sctp_conn->sub_conn[i].enqueue_state != SCTP_ERROR_ENQUEUED)
{
SCTP_DBG_OUTPUT
- ("Connection %u has still DATA to be enqueued inboud / outboud");
+ ("Connection %u has still DATA to be enqueued inboud / outboud",
+ sctp_conn->sub_conn[i].connection.c_index);
return 1;
}
@@ -485,6 +472,7 @@
sctp_session_close (u32 conn_index, u32 thread_index)
{
ASSERT (thread_index == 0);
+
sctp_connection_t *sctp_conn;
sctp_conn = sctp_connection_get (conn_index, thread_index);
if (sctp_conn != NULL)
@@ -506,80 +494,40 @@
}
/**
- * Update snd_mss to reflect the effective segment size that we can send
+ * Compute maximum segment size for session layer.
*/
-void
-sctp_update_snd_mss (sctp_connection_t * sctp_conn)
-{
- /* The overhead for the sctp_header_t and sctp_chunks_common_hdr_t
- * (the sum equals to sctp_full_hdr_t) is already taken into account
- * for the sctp_conn->a_rwnd computation.
- * So let's not account it again here.
- */
- sctp_conn->snd_hdr_length =
- sizeof (sctp_payload_data_chunk_t) - sizeof (sctp_full_hdr_t);
- sctp_conn->snd_a_rwnd =
- clib_min (sctp_conn->a_rwnd,
- sctp_conn->rcv_opts.a_rwnd) - sctp_conn->snd_hdr_length;
-
- SCTP_DBG ("sctp_conn->snd_a_rwnd = %u, sctp_conn->snd_hdr_length = %u ",
- sctp_conn->snd_a_rwnd, sctp_conn->snd_hdr_length);
-
- ASSERT (sctp_conn->snd_a_rwnd > 0);
-}
-
u16
sctp_session_send_mss (transport_connection_t * trans_conn)
{
- SCTP_DBG ("CONN_INDEX: %u", trans_conn->c_index);
-
sctp_connection_t *sctp_conn =
sctp_get_connection_from_transport (trans_conn);
- if (trans_conn == NULL)
- {
- SCTP_DBG ("trans_conn == NULL");
- return 0;
- }
-
if (sctp_conn == NULL)
{
SCTP_DBG ("sctp_conn == NULL");
return 0;
}
- /* Ensure snd_mss does accurately reflect the amount of data we can push
- * in a segment. This also makes sure that options are updated according to
- * the current state of the connection. */
- sctp_update_snd_mss (sctp_conn);
- return sctp_conn->snd_a_rwnd;
+ update_cwnd (sctp_conn);
+ update_smallest_pmtu_idx (sctp_conn);
+
+ return sctp_conn->sub_conn[sctp_conn->smallest_PMTU_idx].cwnd;
}
u16
sctp_snd_space (sctp_connection_t * sctp_conn)
{
- /* TODO: This requires a real implementation */
- if (sctp_conn == NULL)
- {
- SCTP_DBG ("sctp_conn == NULL");
- return 0;
- }
-
- if (sctp_conn->state != SCTP_STATE_ESTABLISHED)
- {
- SCTP_DBG_STATE_MACHINE
- ("Trying to send DATA while not in SCTP_STATE_ESTABLISHED");
- return 0;
- }
-
- return sctp_conn->snd_a_rwnd;
+ /* Finally, let's subtract the DATA chunk headers overhead */
+ return sctp_conn->sub_conn[sctp_conn->smallest_PMTU_idx].cwnd -
+ sizeof (sctp_payload_data_chunk_t) - sizeof (sctp_full_hdr_t);
}
+/**
+ * Compute TX window session is allowed to fill.
+ */
u32
sctp_session_send_space (transport_connection_t * trans_conn)
{
- SCTP_DBG ("CONN_INDEX: %u", trans_conn->c_index);
-
sctp_connection_t *sctp_conn =
sctp_get_connection_from_transport (trans_conn);
@@ -610,13 +558,25 @@
u8 *
format_sctp_session (u8 * s, va_list * args)
{
- return NULL;
+ u32 tci = va_arg (*args, u32);
+ u32 thread_index = va_arg (*args, u32);
+ u32 verbose = va_arg (*args, u32);
+ sctp_connection_t *tc;
+
+ tc = sctp_connection_get (tci, thread_index);
+ if (tc)
+ s = format (s, "%U", format_sctp_connection, tc, verbose);
+ else
+ s = format (s, "empty\n");
+ return s;
}
u8 *
format_sctp_listener_session (u8 * s, va_list * args)
{
- return NULL;
+ u32 tci = va_arg (*args, u32);
+ sctp_connection_t *tc = sctp_listener_get (tci);
+ return format (s, "%U", format_sctp_connection_id, tc);
}
void
@@ -849,7 +809,6 @@
.push_header = sctp_push_header,
.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,