SCTP: Handle a COOKIE ECHO/ACK when a TCB Exists
[vpp.git] / src / vnet / sctp / sctp_output.c
index fba06d9..5e64ca7 100644 (file)
@@ -261,7 +261,7 @@ sctp_reuse_buffer (vlib_main_t * vm, vlib_buffer_t * b)
   b->current_length = 0;
   b->total_length_not_including_first_buffer = 0;
   vnet_buffer (b)->sctp.flags = 0;
-  vnet_buffer (b)->sctp.conn_idx = MAX_SCTP_CONNECTIONS;
+  vnet_buffer (b)->sctp.subconn_idx = MAX_SCTP_CONNECTIONS;
 
   /* Leave enough space for headers */
   return vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
@@ -275,7 +275,7 @@ sctp_init_buffer (vlib_main_t * vm, vlib_buffer_t * b)
   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
   b->total_length_not_including_first_buffer = 0;
   vnet_buffer (b)->sctp.flags = 0;
-  vnet_buffer (b)->sctp.conn_idx = MAX_SCTP_CONNECTIONS;
+  vnet_buffer (b)->sctp.subconn_idx = MAX_SCTP_CONNECTIONS;
   VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b);
   /* Leave enough space for headers */
   return vlib_buffer_make_headroom (b, MAX_HDRS_LEN);
@@ -478,7 +478,7 @@ sctp_prepare_init_chunk (sctp_connection_t * sctp_conn, u8 idx,
   sctp_conn->local_tag = init_chunk->initiate_tag;
 
   vnet_buffer (b)->sctp.connection_index = sub_conn->c_c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 
   SCTP_DBG_STATE_MACHINE ("CONN_INDEX = %u, CURR_CONN_STATE = %u (%s), "
                          "CHUNK_TYPE = %s, "
@@ -551,7 +551,7 @@ sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 void
@@ -585,7 +585,230 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
+}
+
+/**
+ * Convert buffer to ABORT
+ */
+/*
+void
+sctp_prepare_operation_error (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_operation_error_t
+  u16 alloc_bytes = sizeof (sctp_operation_error_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;
+
+  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_u32 (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;
 }
 
 /**
@@ -728,7 +951,7 @@ sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 /**
@@ -764,7 +987,7 @@ sctp_prepare_shutdown_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 /*
@@ -822,7 +1045,7 @@ sctp_prepare_shutdown_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 /*
@@ -877,7 +1100,7 @@ sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 /**
@@ -916,7 +1139,7 @@ sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 /**
@@ -951,7 +1174,7 @@ sctp_prepare_heartbeat_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 void
@@ -1016,7 +1239,7 @@ sctp_prepare_shutdown_complete_chunk (sctp_connection_t * sctp_conn, u8 idx,
 
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 void
@@ -1093,7 +1316,12 @@ sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
     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, 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);
   data_chunk->sctp_hdr.checksum = 0;
   data_chunk->sctp_hdr.src_port =
     sctp_conn->sub_conn[idx].connection.lcl_port;
@@ -1133,7 +1361,7 @@ sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b,
   vnet_buffer (b)->sctp.connection_index =
     sctp_conn->sub_conn[idx].connection.c_index;
 
-  vnet_buffer (b)->sctp.conn_idx = idx;
+  vnet_buffer (b)->sctp.subconn_idx = idx;
 }
 
 u32
@@ -1142,6 +1370,15 @@ 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_DBG_OUTPUT ("TRANS_CONN = %p, SCTP_CONN = %p, "
+                  "S_INDEX = %u, C_INDEX = %u,"
+                  "trans_conn->LCL_PORT = %u, trans_conn->RMT_PORT = %u",
+                  trans_conn,
+                  sctp_conn,
+                  trans_conn->s_index,
+                  trans_conn->c_index,
+                  trans_conn->lcl_port, trans_conn->rmt_port);
+
   sctp_push_hdr_i (sctp_conn, b, SCTP_STATE_ESTABLISHED);
 
   sctp_trajectory_add_start (b0, 3);
@@ -1244,7 +1481,7 @@ sctp46_output_inline (vlib_main_t * vm,
              goto done;
            }
 
-         u8 idx = vnet_buffer (b0)->sctp.conn_idx;
+         u8 idx = vnet_buffer (b0)->sctp.subconn_idx;
 
          th0 = vlib_buffer_get_current (b0);