#include <vnet/sctp/sctp.h>
#include <vnet/sctp/sctp_debug.h>
#include <vppinfra/random.h>
+#include <openssl/hmac.h>
vlib_node_registration_t sctp4_output_node;
vlib_node_registration_t sctp6_output_node;
sctp_init_buffer (vlib_main_t * vm, vlib_buffer_t * b)
{
ASSERT ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0);
- b->flags &= VLIB_BUFFER_FREE_LIST_INDEX_MASK;
+ b->flags &= VLIB_BUFFER_NON_DEFAULT_FREELIST;
b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
b->total_length_not_including_first_buffer = 0;
vnet_buffer (b)->sctp.flags = 0;
}
}
+always_inline void
+sctp_enqueue_to_output (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
+ u8 is_ip4)
+{
+ sctp_enqueue_to_output_i (vm, b, bi, is_ip4, 0);
+}
+
always_inline void
sctp_enqueue_to_output_now (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
u8 is_ip4)
sctp_enqueue_to_ip_lookup_i (vm, b, bi, is_ip4, 0);
}
-always_inline void
-sctp_enqueue_to_ip_lookup_now (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
- u8 is_ip4)
-{
- sctp_enqueue_to_ip_lookup_i (vm, b, bi, is_ip4, 1);
-}
-
/**
* Convert buffer to INIT
*/
init_chunk->sctp_hdr.dst_port);
}
-u64
-sctp_compute_mac ()
+void
+sctp_compute_mac (sctp_connection_t * sctp_conn,
+ sctp_state_cookie_param_t * state_cookie)
{
- return 0x0;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ HMAC_CTX *ctx;
+#else
+ HMAC_CTX ctx;
+#endif
+ unsigned int len = 0;
+ const EVP_MD *md = EVP_sha1 ();
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ ctx = HMAC_CTX_new ();
+ HMAC_Init_ex (ctx, &state_cookie->creation_time,
+ sizeof (state_cookie->creation_time), md, NULL);
+ HMAC_Update (ctx, (const unsigned char *) &sctp_conn, sizeof (sctp_conn));
+ HMAC_Final (ctx, state_cookie->mac, &len);
+#else
+ HMAC_CTX_init (&ctx);
+ HMAC_Init_ex (&ctx, &state_cookie->creation_time,
+ sizeof (state_cookie->creation_time), md, NULL);
+ HMAC_Update (&ctx, (const unsigned char *) &sctp_conn, sizeof (sctp_conn));
+ HMAC_Final (&ctx, state_cookie->mac, &len);
+ HMAC_CTX_cleanup (&ctx);
+#endif
+
+ ENDIANESS_SWAP (state_cookie->mac);
}
void
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;
}
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;
}
state_cookie_param->creation_time = clib_host_to_net_u32 (sctp_time_now ());
state_cookie_param->cookie_lifespan =
clib_host_to_net_u32 (SCTP_VALID_COOKIE_LIFE);
- state_cookie_param->mac = clib_host_to_net_u64 (sctp_compute_mac ());
+
+ sctp_compute_mac (sctp_conn, state_cookie_param);
pointer_offset += sizeof (sctp_state_cookie_param_t);
if (PREDICT_TRUE (ip6_addr != NULL))
{
sctp_ipv6_addr_param_t *ipv6_addr =
- (sctp_ipv6_addr_param_t *) init_ack_chunk +
- sizeof (sctp_init_chunk_t) + pointer_offset;
+ (sctp_ipv6_addr_param_t *) init_ack_chunk + pointer_offset;
ipv6_addr->param_hdr.type =
clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE);
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;
}
void
sctp_prepare_shutdown_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
{
- vlib_main_t *vm = vlib_get_main ();
u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN);
u16 alloc_bytes = sizeof (sctp_shutdown_association_chunk_t);
- b = sctp_reuse_buffer (vm, b);
-
/* As per RFC 4960 the chunk_length value does NOT contemplate
* the size of the first header (see sctp_header_t) and any padding
*/
sctp_prepare_shutdown_chunk (sctp_conn, b);
u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN);
- 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 ();
}
/**
* Send SHUTDOWN_ACK
*/
void
-sctp_send_shutdown_ack (sctp_connection_t * sctp_conn)
+sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
{
- vlib_buffer_t *b;
- u32 bi;
- sctp_main_t *tm = vnet_get_sctp_main ();
vlib_main_t *vm = vlib_get_main ();
if (sctp_check_outstanding_data_chunks (sctp_conn) > 0)
return;
- if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
- return;
+ sctp_reuse_buffer (vm, b);
- b = vlib_get_buffer (vm, bi);
- sctp_init_buffer (vm, b);
sctp_prepare_shutdown_ack_chunk (sctp_conn, b);
u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN_ACK);
- sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b);
- sctp_enqueue_to_ip_lookup (vm, b, bi,
- sctp_conn->sub_conn[idx].connection.is_ip4);
- /* Start the SCTP_TIMER_T2_SHUTDOWN timer */
- sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN, SCTP_RTO_INIT);
- sctp_conn->state = SCTP_STATE_SHUTDOWN_ACK_SENT;
+ /* Measure RTT with this */
+ sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
}
/**
sctp_conn->sub_conn[idx].connection.c_index;
}
+/**
+ * Convert buffer to HEARTBEAT_ACK
+ */
+void
+sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn,
+ vlib_buffer_t * b)
+{
+ vlib_main_t *vm = vlib_get_main ();
+
+ u8 idx = sctp_pick_conn_idx_on_chunk (HEARTBEAT_ACK);
+ u16 alloc_bytes = sizeof (sctp_hb_ack_chunk_t);
+
+ sctp_reuse_buffer (vm, b);
+
+ /* As per RFC 4960 the chunk_length value does NOT contemplate
+ * the size of the first header (see sctp_header_t) and any padding
+ */
+ u16 chunk_len = alloc_bytes - sizeof (sctp_header_t);
+
+ alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes);
+
+ sctp_hb_ack_chunk_t *hb_ack = vlib_buffer_push_uninit (b, alloc_bytes);
+
+ hb_ack->sctp_hdr.checksum = 0;
+ /* No need of host_to_net conversion, already in net-byte order */
+ hb_ack->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port;
+ hb_ack->sctp_hdr.dst_port = sctp_conn->sub_conn[idx].connection.rmt_port;
+ hb_ack->sctp_hdr.verification_tag = sctp_conn->remote_tag;
+ hb_ack->hb_info.param_hdr.type = clib_host_to_net_u16 (1);
+ hb_ack->hb_info.param_hdr.length =
+ clib_host_to_net_u16 (sizeof (hb_ack->hb_info.hb_info));
+
+ vnet_sctp_set_chunk_type (&hb_ack->chunk_hdr, HEARTBEAT_ACK);
+ vnet_sctp_set_chunk_length (&hb_ack->chunk_hdr, chunk_len);
+
+ vnet_buffer (b)->sctp.connection_index =
+ sctp_conn->sub_conn[idx].connection.c_index;
+}
+
/**
* Convert buffer to HEARTBEAT
*/
sctp_prepare_heartbeat_chunk (sctp_connection_t * sctp_conn,
vlib_buffer_t * b)
{
- vlib_main_t *vm = vlib_get_main ();
u8 idx = sctp_pick_conn_idx_on_chunk (HEARTBEAT);
u16 alloc_bytes = sizeof (sctp_hb_req_chunk_t);
- b = sctp_reuse_buffer (vm, b);
-
/* As per RFC 4960 the chunk_length value does NOT contemplate
* the size of the first header (see sctp_header_t) and any padding
*/
sctp_prepare_heartbeat_chunk (sctp_conn, b);
u8 idx = sctp_pick_conn_idx_on_state (SCTP_STATE_ESTABLISHED);
- sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b);
- sctp_enqueue_to_ip_lookup (vm, b, bi,
- sctp_conn->sub_conn[idx].connection.is_ip4);
+ sctp_enqueue_to_output_now (vm, b, bi,
+ sctp_conn->sub_conn[idx].connection.is_ip4);
+
+ sctp_conn->sub_conn[idx].unacknowledged_hb += 1;
}
/**
sctp_prepare_shutdown_complete_chunk (sctp_conn, b);
u8 idx = sctp_pick_conn_idx_on_chunk (SHUTDOWN_COMPLETE);
- sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b);
- sctp_enqueue_to_ip_lookup (vm, b, bi,
- sctp_conn->sub_conn[idx].connection.is_ip4);
+ sctp_enqueue_to_output (vm, b, bi,
+ sctp_conn->sub_conn[idx].connection.is_ip4);
sctp_conn->state = SCTP_STATE_CLOSED;
}
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);
+ sctp_enqueue_to_ip_lookup (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
vnet_sctp_set_chunk_type (&data_chunk->chunk_hdr, DATA);
vnet_sctp_set_chunk_length (&data_chunk->chunk_hdr, chunk_length);
+ vnet_sctp_set_bbit (&data_chunk->chunk_hdr);
+ vnet_sctp_set_ebit (&data_chunk->chunk_hdr);
+
SCTP_ADV_DBG_OUTPUT ("POINTER_WITH_DATA = %p, DATA_OFFSET = %u",
b->data, b->current_data);
{
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);
+
+ if (sctp_conn->sub_conn[idx].unacknowledged_hb >
+ SCTP_ASSOCIATION_MAX_RETRANS)
{
- sctp_conn->rtt_ts = sctp_time_now ();
- sctp_conn->rtt_seq = sctp_conn->next_tsn;
+ // The remote-peer is considered to be unreachable hence shutting down
+
+ /* Start cleanup. App wasn't notified yet so use delete notify as
+ * opposed to delete to cleanup session layer state. */
+ stream_session_delete_notify (&sctp_conn->sub_conn
+ [MAIN_SCTP_SUB_CONN_IDX].connection);
+
+ sctp_connection_timers_reset (sctp_conn);
+
+ sctp_connection_cleanup (sctp_conn);
}
+
+ sctp_push_hdr_i (sctp_conn, idx, b, SCTP_STATE_ESTABLISHED);
+
+ if (sctp_conn->sub_conn[idx].RTO_pending == 0)
+ {
+ 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;
n_left_to_next -= 1;
b0 = vlib_get_buffer (vm, bi0);
+
sctp_conn =
sctp_connection_get (vnet_buffer (b0)->sctp.connection_index,
my_thread_index);
/* Change state */
sctp_conn->state = SCTP_STATE_COOKIE_ECHOED;
break;
+ case SCTP_STATE_SHUTDOWN_SENT:
+ if (chunk_type != SHUTDOWN_COMPLETE)
+ {
+ SCTP_DBG_STATE_MACHINE
+ ("Sending the wrong chunk (%s) based on state-machine status (%s)",
+ sctp_chunk_to_string (chunk_type),
+ sctp_state_to_string (sctp_conn->state));
+
+ error0 = SCTP_ERROR_UNKOWN_CHUNK;
+ next0 = SCTP_OUTPUT_NEXT_DROP;
+ goto done;
+ }
+ case SCTP_STATE_SHUTDOWN_RECEIVED:
+ if (chunk_type != SHUTDOWN_ACK)
+ {
+ SCTP_DBG_STATE_MACHINE
+ ("Sending the wrong chunk (%s) based on state-machine status (%s)",
+ sctp_chunk_to_string (chunk_type),
+ sctp_state_to_string (sctp_conn->state));
+
+ error0 = SCTP_ERROR_UNKOWN_CHUNK;
+ next0 = SCTP_OUTPUT_NEXT_DROP;
+ goto done;
+ }
default:
SCTP_DBG_STATE_MACHINE
("Sending chunk (%s) based on state-machine status (%s)",
break;
}
- if (chunk_type == SHUTDOWN)
- {
- /* Start the SCTP_TIMER_T2_SHUTDOWN timer */
- sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN,
- SCTP_RTO_INIT);
- sctp_conn->state = SCTP_STATE_SHUTDOWN_SENT;
- }
-
- if (chunk_type == DATA)
+ switch (chunk_type)
{
- sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX,
- SCTP_RTO_INIT);
+ case DATA:
+ {
+ sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX,
+ sctp_conn->sub_conn[idx].RTO);
+ break;
+ }
+ case SHUTDOWN:
+ {
+ /* Start the SCTP_TIMER_T2_SHUTDOWN timer */
+ sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN,
+ sctp_conn->sub_conn[idx].RTO);
+ sctp_conn->state = SCTP_STATE_SHUTDOWN_SENT;
+ break;
+ }
+ case SHUTDOWN_ACK:
+ {
+ /* Start the SCTP_TIMER_T2_SHUTDOWN timer */
+ sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN,
+ sctp_conn->sub_conn[idx].RTO);
+ sctp_conn->state = SCTP_STATE_SHUTDOWN_ACK_SENT;
+ break;
+ }
}
vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;