SCTP: fix build errors on ubuntu bionic
[vpp.git] / src / vnet / sctp / sctp_output.c
index 7b22cc5..7657d0c 100644 (file)
@@ -15,6 +15,7 @@
 #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;
@@ -269,7 +270,7 @@ always_inline void *
 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;
@@ -350,6 +351,13 @@ sctp_enqueue_to_output_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
     }
 }
 
+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)
@@ -405,13 +413,6 @@ sctp_enqueue_to_ip_lookup (vlib_main_t * vm, vlib_buffer_t * b, u32 bi,
   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
  */
@@ -494,10 +495,33 @@ sctp_prepare_init_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
                          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
@@ -530,6 +554,9 @@ sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn,
   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;
 }
@@ -563,6 +590,10 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn,
   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;
 }
@@ -626,7 +657,8 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
   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);
 
@@ -646,8 +678,7 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
   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);
@@ -705,6 +736,9 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
 
   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;
 }
@@ -715,12 +749,9 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
 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
    */
@@ -769,9 +800,11 @@ sctp_send_shutdown (sctp_connection_t * sctp_conn)
   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 ();
 }
 
 /**
@@ -809,31 +842,21 @@ sctp_prepare_shutdown_ack_chunk (sctp_connection_t * sctp_conn,
  * 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 ();
 }
 
 /**
@@ -873,6 +896,45 @@ sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, vlib_buffer_t * b)
     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
  */
@@ -880,12 +942,9 @@ void
 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
    */
@@ -927,9 +986,10 @@ sctp_send_heartbeat (sctp_connection_t * sctp_conn)
   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;
 }
 
 /**
@@ -979,9 +1039,8 @@ sctp_send_shutdown_complete (sctp_connection_t * sctp_conn)
   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;
 }
@@ -1007,35 +1066,27 @@ sctp_send_init (sctp_connection_t * sctp_conn)
   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
@@ -1068,6 +1119,9 @@ sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
   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);
 
@@ -1082,13 +1136,32 @@ sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b)
 {
   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;
@@ -1137,6 +1210,7 @@ sctp46_output_inline (vlib_main_t * vm,
          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);
@@ -1299,6 +1373,30 @@ sctp46_output_inline (vlib_main_t * vm,
              /* 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)",
@@ -1307,18 +1405,30 @@ sctp46_output_inline (vlib_main_t * vm,
              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;