c11 safe string handling support
[vpp.git] / src / vnet / sctp / sctp_output.c
index 39e5e75..2b65d97 100644 (file)
@@ -362,7 +362,7 @@ sctp_enqueue_to_output_now (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
 
 always_inline void
 sctp_enqueue_to_ip_lookup_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
-                            u8 is_ip4, u8 flush)
+                            u8 is_ip4, u32 fib_index, u8 flush)
 {
   sctp_main_t *tm = vnet_get_sctp_main ();
   u32 thread_index = vlib_get_thread_index ();
@@ -372,8 +372,8 @@ sctp_enqueue_to_ip_lookup_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
   b->error = 0;
 
-  /* Default FIB for now */
-  vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;
+  vnet_buffer (b)->sw_if_index[VLIB_TX] = fib_index;
+  vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
 
   /* Send to IP lookup */
   next_index = is_ip4 ? ip4_lookup_node.index : ip6_lookup_node.index;
@@ -403,9 +403,11 @@ sctp_enqueue_to_ip_lookup_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
 
 always_inline void
 sctp_enqueue_to_ip_lookup (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
-                          u8 is_ip4)
+                          u8 is_ip4, u32 fib_index)
 {
-  sctp_enqueue_to_ip_lookup_i (vm, b, bi, is_ip4, 0);
+  sctp_enqueue_to_ip_lookup_i (vm, b, bi, is_ip4, fib_index, 0);
+  if (vm->thread_index == 0 && vlib_num_workers ())
+    session_flush_frames_main_thread (vm);
 }
 
 /**
@@ -463,6 +465,8 @@ sctp_prepare_init_chunk (sctp_connection_t * sctp_conn, u8 idx,
   vnet_sctp_set_chunk_length (&init_chunk->chunk_hdr, chunk_len);
   vnet_sctp_common_hdr_params_host_to_net (&init_chunk->chunk_hdr);
 
+  sctp_init_cwnd (sctp_conn);
+
   init_chunk->a_rwnd = clib_host_to_net_u32 (sctp_conn->sub_conn[idx].cwnd);
   init_chunk->initiate_tag = clib_host_to_net_u32 (random_u32 (&random_seed));
   init_chunk->inboud_streams_count =
@@ -556,12 +560,12 @@ sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
 void
 sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
-                               vlib_buffer_t * b,
-                               sctp_state_cookie_param_t * sc)
+                               vlib_buffer_t * b, u8 reuse_buffer)
 {
   vlib_main_t *vm = vlib_get_main ();
 
-  sctp_reuse_buffer (vm, b);
+  if (reuse_buffer)
+    sctp_reuse_buffer (vm, b);
 
   /* The minimum size of the message is given by the sctp_init_ack_chunk_t */
   u16 alloc_bytes = sizeof (sctp_cookie_echo_chunk_t);
@@ -580,7 +584,7 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
   cookie_echo_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag;
   vnet_sctp_set_chunk_type (&cookie_echo_chunk->chunk_hdr, COOKIE_ECHO);
   vnet_sctp_set_chunk_length (&cookie_echo_chunk->chunk_hdr, chunk_len);
-  clib_memcpy (&(cookie_echo_chunk->cookie), sc,
+  clib_memcpy (&(cookie_echo_chunk->cookie), &sctp_conn->cookie_param,
               sizeof (sctp_state_cookie_param_t));
 
   vnet_buffer (b)->sctp.connection_index =
@@ -588,13 +592,285 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
   vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
+
+/*
+ *  Send COOKIE_ECHO
+ */
+void
+sctp_send_cookie_echo (sctp_connection_t * sctp_conn)
+{
+  vlib_buffer_t *b;
+  u32 bi;
+  sctp_main_t *tm = vnet_get_sctp_main ();
+  vlib_main_t *vm = vlib_get_main ();
+
+  if (PREDICT_FALSE (sctp_conn->init_retransmit_err > SCTP_MAX_INIT_RETRANS))
+    {
+      clib_warning ("Reached MAX_INIT_RETRANS times. Aborting connection.");
+
+      session_stream_connect_notify (&sctp_conn->sub_conn
+                                    [SCTP_PRIMARY_PATH_IDX].connection, 1);
+
+      sctp_connection_timers_reset (sctp_conn);
+
+      sctp_connection_cleanup (sctp_conn);
+    }
+
+  if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
+    return;
+
+  b = vlib_get_buffer (vm, bi);
+  u8 idx = SCTP_PRIMARY_PATH_IDX;
+
+  sctp_init_buffer (vm, b);
+  sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b, 0);
+  sctp_enqueue_to_output_now (vm, b, bi, sctp_conn->sub_conn[idx].c_is_ip4);
+
+  /* Start the T1_INIT timer */
+  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;
+
+  /* Measure RTT with this */
+  sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now ();
+}
+
+
+/**
+ * Convert buffer to ERROR
+ */
+void
+sctp_prepare_operation_error (sctp_connection_t * sctp_conn, u8 idx,
+                             vlib_buffer_t * b, u8 err_cause)
+{
+  vlib_main_t *vm = vlib_get_main ();
+
+  sctp_reuse_buffer (vm, b);
+
+  /* The minimum size of the message is given by the sctp_operation_error_t */
+  u16 alloc_bytes =
+    sizeof (sctp_operation_error_t) + sizeof (sctp_err_cause_param_t);
+
+  /* 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_operation_error_t *err_chunk =
+    vlib_buffer_push_uninit (b, alloc_bytes);
+
+  /* src_port & dst_port are already in network byte-order */
+  err_chunk->sctp_hdr.checksum = 0;
+  err_chunk->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port;
+  err_chunk->sctp_hdr.dst_port = sctp_conn->sub_conn[idx].connection.rmt_port;
+  /* As per RFC4960 Section 5.2.2: copy the INITIATE_TAG into the VERIFICATION_TAG of the ABORT chunk */
+  err_chunk->sctp_hdr.verification_tag = sctp_conn->local_tag;
+
+  err_chunk->err_causes[0].param_hdr.length =
+    clib_host_to_net_u16 (sizeof (err_chunk->err_causes[0].param_hdr.type) +
+                         sizeof (err_chunk->err_causes[0].param_hdr.length));
+  err_chunk->err_causes[0].param_hdr.type = clib_host_to_net_u16 (err_cause);
+
+  vnet_sctp_set_chunk_type (&err_chunk->chunk_hdr, OPERATION_ERROR);
+  vnet_sctp_set_chunk_length (&err_chunk->chunk_hdr, chunk_len);
+
+  vnet_buffer (b)->sctp.connection_index =
+    sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
+}
+
+/**
+ * Convert buffer to ABORT
+ */
+void
+sctp_prepare_abort_for_collision (sctp_connection_t * sctp_conn, u8 idx,
+                                 vlib_buffer_t * b, ip4_address_t * ip4_addr,
+                                 ip6_address_t * ip6_addr)
+{
+  vlib_main_t *vm = vlib_get_main ();
+
+  sctp_reuse_buffer (vm, b);
+
+  /* The minimum size of the message is given by the sctp_abort_chunk_t */
+  u16 alloc_bytes = sizeof (sctp_abort_chunk_t);
+
+  /* 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_abort_chunk_t *abort_chunk = vlib_buffer_push_uninit (b, alloc_bytes);
+
+  /* src_port & dst_port are already in network byte-order */
+  abort_chunk->sctp_hdr.checksum = 0;
+  abort_chunk->sctp_hdr.src_port =
+    sctp_conn->sub_conn[idx].connection.lcl_port;
+  abort_chunk->sctp_hdr.dst_port =
+    sctp_conn->sub_conn[idx].connection.rmt_port;
+  /* As per RFC4960 Section 5.2.2: copy the INITIATE_TAG into the VERIFICATION_TAG of the ABORT chunk */
+  abort_chunk->sctp_hdr.verification_tag = sctp_conn->local_tag;
+
+  vnet_sctp_set_chunk_type (&abort_chunk->chunk_hdr, ABORT);
+  vnet_sctp_set_chunk_length (&abort_chunk->chunk_hdr, chunk_len);
+
+  vnet_buffer (b)->sctp.connection_index =
+    sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
+}
+
+/**
+ * Convert buffer to INIT-ACK
+ */
+void
+sctp_prepare_initack_chunk_for_collision (sctp_connection_t * sctp_conn,
+                                         u8 idx, vlib_buffer_t * b,
+                                         ip4_address_t * ip4_addr,
+                                         ip6_address_t * ip6_addr)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  sctp_ipv4_addr_param_t *ip4_param = 0;
+  sctp_ipv6_addr_param_t *ip6_param = 0;
+
+  sctp_reuse_buffer (vm, b);
+
+  /* The minimum size of the message is given by the sctp_init_ack_chunk_t */
+  u16 alloc_bytes =
+    sizeof (sctp_init_ack_chunk_t) + sizeof (sctp_state_cookie_param_t);
+
+  if (PREDICT_TRUE (ip4_addr != NULL))
+    {
+      /* Create room for variable-length fields in the INIT_ACK chunk */
+      alloc_bytes += SCTP_IPV4_ADDRESS_TYPE_LENGTH;
+    }
+  if (PREDICT_TRUE (ip6_addr != NULL))
+    {
+      /* Create room for variable-length fields in the INIT_ACK chunk */
+      alloc_bytes += SCTP_IPV6_ADDRESS_TYPE_LENGTH;
+    }
+
+  if (sctp_conn->sub_conn[idx].connection.is_ip4)
+    alloc_bytes += sizeof (sctp_ipv4_addr_param_t);
+  else
+    alloc_bytes += sizeof (sctp_ipv6_addr_param_t);
+
+  /* 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_init_ack_chunk_t *init_ack_chunk =
+    vlib_buffer_push_uninit (b, alloc_bytes);
+
+  u16 pointer_offset = sizeof (sctp_init_ack_chunk_t);
+
+  /* Create State Cookie parameter */
+  sctp_state_cookie_param_t *state_cookie_param =
+    (sctp_state_cookie_param_t *) ((char *) init_ack_chunk + pointer_offset);
+
+  state_cookie_param->param_hdr.type =
+    clib_host_to_net_u16 (SCTP_STATE_COOKIE_TYPE);
+  state_cookie_param->param_hdr.length =
+    clib_host_to_net_u16 (sizeof (sctp_state_cookie_param_t));
+  state_cookie_param->creation_time = clib_host_to_net_u64 (sctp_time_now ());
+  state_cookie_param->cookie_lifespan =
+    clib_host_to_net_u32 (SCTP_VALID_COOKIE_LIFE);
+
+  sctp_compute_mac (sctp_conn, state_cookie_param);
+
+  pointer_offset += sizeof (sctp_state_cookie_param_t);
+
+  if (PREDICT_TRUE (ip4_addr != NULL))
+    {
+      sctp_ipv4_addr_param_t *ipv4_addr =
+       (sctp_ipv4_addr_param_t *) init_ack_chunk + pointer_offset;
+
+      ipv4_addr->param_hdr.type =
+       clib_host_to_net_u16 (SCTP_IPV4_ADDRESS_TYPE);
+      ipv4_addr->param_hdr.length =
+       clib_host_to_net_u16 (SCTP_IPV4_ADDRESS_TYPE_LENGTH);
+      ipv4_addr->address.as_u32 = ip4_addr->as_u32;
+
+      pointer_offset += SCTP_IPV4_ADDRESS_TYPE_LENGTH;
+    }
+  if (PREDICT_TRUE (ip6_addr != NULL))
+    {
+      sctp_ipv6_addr_param_t *ipv6_addr =
+       (sctp_ipv6_addr_param_t *) init_ack_chunk + pointer_offset;
+
+      ipv6_addr->param_hdr.type =
+       clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE);
+      ipv6_addr->param_hdr.length =
+       clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE_LENGTH);
+      ipv6_addr->address.as_u64[0] = ip6_addr->as_u64[0];
+      ipv6_addr->address.as_u64[1] = ip6_addr->as_u64[1];
+
+      pointer_offset += SCTP_IPV6_ADDRESS_TYPE_LENGTH;
+    }
+
+  if (sctp_conn->sub_conn[idx].connection.is_ip4)
+    {
+      ip4_param = (sctp_ipv4_addr_param_t *) init_ack_chunk + pointer_offset;
+      ip4_param->address.as_u32 =
+       sctp_conn->sub_conn[idx].connection.lcl_ip.ip4.as_u32;
+
+      pointer_offset += sizeof (sctp_ipv4_addr_param_t);
+    }
+  else
+    {
+      ip6_param = (sctp_ipv6_addr_param_t *) init_ack_chunk + pointer_offset;
+      ip6_param->address.as_u64[0] =
+       sctp_conn->sub_conn[idx].connection.lcl_ip.ip6.as_u64[0];
+      ip6_param->address.as_u64[1] =
+       sctp_conn->sub_conn[idx].connection.lcl_ip.ip6.as_u64[1];
+
+      pointer_offset += sizeof (sctp_ipv6_addr_param_t);
+    }
+
+  /* src_port & dst_port are already in network byte-order */
+  init_ack_chunk->sctp_hdr.checksum = 0;
+  init_ack_chunk->sctp_hdr.src_port =
+    sctp_conn->sub_conn[idx].connection.lcl_port;
+  init_ack_chunk->sctp_hdr.dst_port =
+    sctp_conn->sub_conn[idx].connection.rmt_port;
+  /* the sctp_conn->verification_tag is already in network byte-order (being a copy of the init_tag coming with the INIT chunk) */
+  init_ack_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag;
+  init_ack_chunk->initial_tsn =
+    clib_host_to_net_u32 (sctp_conn->local_initial_tsn);
+  SCTP_CONN_TRACKING_DBG ("init_ack_chunk->initial_tsn = %u",
+                         init_ack_chunk->initial_tsn);
+
+  vnet_sctp_set_chunk_type (&init_ack_chunk->chunk_hdr, INIT_ACK);
+  vnet_sctp_set_chunk_length (&init_ack_chunk->chunk_hdr, chunk_len);
+
+  init_ack_chunk->initiate_tag = sctp_conn->local_tag;
+
+  init_ack_chunk->a_rwnd =
+    clib_host_to_net_u32 (sctp_conn->sub_conn[idx].cwnd);
+  init_ack_chunk->inboud_streams_count =
+    clib_host_to_net_u16 (INBOUND_STREAMS_COUNT);
+  init_ack_chunk->outbound_streams_count =
+    clib_host_to_net_u16 (OUTBOUND_STREAMS_COUNT);
+
+  vnet_buffer (b)->sctp.connection_index =
+    sctp_conn->sub_conn[idx].connection.c_index;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
+}
+
 /**
  * Convert buffer to INIT-ACK
  */
 void
 sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx,
                            vlib_buffer_t * b, ip4_address_t * ip4_addr,
-                           ip6_address_t * ip6_addr)
+                           u8 add_ip4, ip6_address_t * ip6_addr, u8 add_ip6)
 {
   vlib_main_t *vm = vlib_get_main ();
   sctp_ipv4_addr_param_t *ip4_param = 0;
@@ -607,12 +883,12 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx,
   u16 alloc_bytes =
     sizeof (sctp_init_ack_chunk_t) + sizeof (sctp_state_cookie_param_t);
 
-  if (PREDICT_TRUE (ip4_addr != NULL))
+  if (PREDICT_FALSE (add_ip4 == 1))
     {
       /* Create room for variable-length fields in the INIT_ACK chunk */
       alloc_bytes += SCTP_IPV4_ADDRESS_TYPE_LENGTH;
     }
-  if (PREDICT_TRUE (ip6_addr != NULL))
+  if (PREDICT_FALSE (add_ip6 == 1))
     {
       /* Create room for variable-length fields in the INIT_ACK chunk */
       alloc_bytes += SCTP_IPV6_ADDRESS_TYPE_LENGTH;
@@ -643,7 +919,7 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx,
     clib_host_to_net_u16 (SCTP_STATE_COOKIE_TYPE);
   state_cookie_param->param_hdr.length =
     clib_host_to_net_u16 (sizeof (sctp_state_cookie_param_t));
-  state_cookie_param->creation_time = clib_host_to_net_u32 (sctp_time_now ());
+  state_cookie_param->creation_time = clib_host_to_net_u64 (sctp_time_now ());
   state_cookie_param->cookie_lifespan =
     clib_host_to_net_u32 (SCTP_VALID_COOKIE_LIFE);
 
@@ -784,7 +1060,7 @@ sctp_send_shutdown (sctp_connection_t * sctp_conn)
   if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
     return;
 
-  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
+  u8 idx = SCTP_PRIMARY_PATH_IDX;
 
   b = vlib_get_buffer (vm, bi);
   sctp_init_buffer (vm, b);
@@ -963,7 +1239,7 @@ sctp_send_heartbeat (sctp_connection_t * sctp_conn)
   vlib_main_t *vm = vlib_get_main ();
 
   u8 i;
-  u32 now = sctp_time_now ();
+  u64 now = sctp_time_now ();
 
   for (i = 0; i < MAX_SCTP_CONNECTIONS; i++)
     {
@@ -1044,17 +1320,32 @@ sctp_send_init (sctp_connection_t * sctp_conn)
   sctp_main_t *tm = vnet_get_sctp_main ();
   vlib_main_t *vm = vlib_get_main ();
 
+  if (PREDICT_FALSE (sctp_conn->init_retransmit_err > SCTP_MAX_INIT_RETRANS))
+    {
+      clib_warning ("Reached MAX_INIT_RETRANS times. Aborting connection.");
+
+      session_stream_connect_notify (&sctp_conn->sub_conn
+                                    [SCTP_PRIMARY_PATH_IDX].connection, 1);
+
+      sctp_connection_timers_reset (sctp_conn);
+
+      sctp_connection_cleanup (sctp_conn);
+
+      return;
+    }
+
   if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi)))
     return;
 
   b = vlib_get_buffer (vm, bi);
-  u8 idx = MAIN_SCTP_SUB_CONN_IDX;
+  u8 idx = SCTP_PRIMARY_PATH_IDX;
 
   sctp_init_buffer (vm, b);
   sctp_prepare_init_chunk (sctp_conn, idx, b);
 
   sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b);
-  sctp_enqueue_to_ip_lookup (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,
+                            sctp_conn->sub_conn[idx].c_fib_index);
 
   /* Start the T1_INIT timer */
   sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_INIT,
@@ -1087,14 +1378,12 @@ sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
   u16 bytes_to_add = sizeof (sctp_payload_data_chunk_t);
   u16 chunk_length = data_len + bytes_to_add - sizeof (sctp_header_t);
 
-  bytes_to_add += vnet_sctp_calculate_padding (bytes_to_add + data_len);
-
   sctp_payload_data_chunk_t *data_chunk =
     vlib_buffer_push_uninit (b, bytes_to_add);
 
   u8 idx = sctp_data_subconn_select (sctp_conn);
   SCTP_DBG_OUTPUT
-    ("SCTP_CONN = %p, IDX = %u, S_INDEX = %u, C_INDEX = %u, LCL_PORT = %u, RMT_PORT = %u",
+    ("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,
      sctp_conn->sub_conn[idx].connection.c_index,
      sctp_conn->sub_conn[idx].connection.lcl_port,
@@ -1119,7 +1408,12 @@ sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
   SCTP_ADV_DBG_OUTPUT ("POINTER_WITH_DATA = %p, DATA_OFFSET = %u",
                       b->data, b->current_data);
 
-  sctp_conn->last_unacked_tsn = sctp_conn->next_tsn;
+  if (sctp_conn->sub_conn[idx].state != SCTP_SUBCONN_AWAITING_SACK)
+    {
+      sctp_conn->sub_conn[idx].state = SCTP_SUBCONN_AWAITING_SACK;
+      sctp_conn->last_unacked_tsn = sctp_conn->next_tsn;
+    }
+
   sctp_conn->next_tsn += data_len;
 
   u32 inflight = sctp_conn->next_tsn - sctp_conn->last_unacked_tsn;
@@ -1149,7 +1443,7 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b)
 
   SCTP_DBG_OUTPUT ("TRANS_CONN = %p, SCTP_CONN = %p, "
                   "S_INDEX = %u, C_INDEX = %u,"
-                  "LCL_PORT = %u, RMT_PORT = %u",
+                  "trans_conn->LCL_PORT = %u, trans_conn->RMT_PORT = %u",
                   trans_conn,
                   sctp_conn,
                   trans_conn->s_index,
@@ -1158,11 +1452,98 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b)
 
   sctp_push_hdr_i (sctp_conn, b, SCTP_STATE_ESTABLISHED);
 
-  sctp_trajectory_add_start (b0, 3);
+  sctp_trajectory_add_start (b, 3);
 
   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 =
+    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)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vlib_buffer_t *b = 0;
+  u32 bi, n_bytes = 0;
+
+  u8 idx = sctp_data_subconn_select (sctp_conn);
+
+  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,
+     sctp_conn->sub_conn[idx].connection.c_index,
+     sctp_conn->sub_conn[idx].connection.lcl_port,
+     sctp_conn->sub_conn[idx].connection.rmt_port);
+
+  if (sctp_conn->state >= SCTP_STATE_ESTABLISHED)
+    {
+      return;
+    }
+
+  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;
+}
+
 #if SCTP_DEBUG_STATE_MACHINE
 always_inline u8
 sctp_validate_output_state_machine (sctp_connection_t * sctp_conn,
@@ -1321,7 +1702,7 @@ sctp46_output_inline (vlib_main_t * vm,
            {
              clib_warning
                ("Trying to send an unrecognized chunk... something is really bad.");
-             error0 = SCTP_ERROR_UNKOWN_CHUNK;
+             error0 = SCTP_ERROR_UNKNOWN_CHUNK;
              next0 = SCTP_OUTPUT_NEXT_DROP;
              goto done;
            }
@@ -1354,7 +1735,7 @@ sctp46_output_inline (vlib_main_t * vm,
                                      connection.rmt_port,
                                      sctp_hdr->dst_port);
 
-             error0 = SCTP_ERROR_UNKOWN_CHUNK;
+             error0 = SCTP_ERROR_UNKNOWN_CHUNK;
              next0 = SCTP_OUTPUT_NEXT_DROP;
              goto done;
            }
@@ -1377,7 +1758,7 @@ sctp46_output_inline (vlib_main_t * vm,
                 sctp_chunk_to_string (chunk_type),
                 sctp_state_to_string (sctp_conn->state));
 
-             error0 = SCTP_ERROR_UNKOWN_CHUNK;
+             error0 = SCTP_ERROR_UNKNOWN_CHUNK;
              next0 = SCTP_OUTPUT_NEXT_DROP;
              goto done;
 
@@ -1440,7 +1821,8 @@ sctp46_output_inline (vlib_main_t * vm,
            }
 
          vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
-         vnet_buffer (b0)->sw_if_index[VLIB_TX] = ~0;
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] =
+           sctp_conn->sub_conn[idx].c_fib_index;
 
          b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
 
@@ -1465,7 +1847,7 @@ sctp46_output_inline (vlib_main_t * vm,
                }
              else
                {
-                 memset (&t0->sctp_header, 0, sizeof (t0->sctp_header));
+                 clib_memset (&t0->sctp_header, 0, sizeof (t0->sctp_header));
                }
              clib_memcpy (&t0->sctp_connection, sctp_conn,
                           sizeof (t0->sctp_connection));