VPP-659 TCP improvements
[vpp.git] / src / vnet / tcp / tcp.c
index 0f9b709..d2df5c3 100644 (file)
@@ -139,6 +139,20 @@ tcp_connection_del (tcp_connection_t * tc)
   tcp_connection_cleanup (tc);
 }
 
+/** Notify session that connection has been reset.
+ *
+ * Switch state to closed and wait for session to call cleanup.
+ */
+void
+tcp_connection_reset (tcp_connection_t * tc)
+{
+  if (tc->state == TCP_STATE_CLOSED)
+    return;
+
+  tc->state = TCP_STATE_CLOSED;
+  stream_session_reset_notify (&tc->connection);
+}
+
 /**
  * Begin connection closing procedure.
  *
@@ -149,6 +163,8 @@ tcp_connection_del (tcp_connection_t * tc)
  * calls cleanup.
  * 2) TIME_WAIT (active close) whereby after 2MSL the 2MSL timer triggers
  * and cleanup is called.
+ *
+ * N.B. Half-close connections are not supported
  */
 void
 tcp_connection_close (tcp_connection_t * tc)
@@ -166,9 +182,9 @@ tcp_connection_close (tcp_connection_t * tc)
   else if (tc->state == TCP_STATE_CLOSE_WAIT)
     tc->state = TCP_STATE_LAST_ACK;
 
-  /* Half-close connections are not supported XXX */
-
-  if (tc->state == TCP_STATE_CLOSED)
+  /* If in CLOSED and WAITCLOSE timer is not set, delete connection now */
+  if (tc->timers[TCP_TIMER_WAITCLOSE] == TCP_TIMER_HANDLE_INVALID
+      && tc->state == TCP_STATE_CLOSED)
     tcp_connection_del (tc);
 }
 
@@ -185,7 +201,10 @@ tcp_session_cleanup (u32 conn_index, u32 thread_index)
 {
   tcp_connection_t *tc;
   tc = tcp_connection_get (conn_index, thread_index);
-  tcp_connection_cleanup (tc);
+
+  /* Wait for the session tx events to clear */
+  tc->state = TCP_STATE_CLOSED;
+  tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
 }
 
 void *
@@ -217,6 +236,7 @@ ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4)
   return 0;
 }
 
+#define PORT_MASK ((1 << 16)- 1)
 /**
  * Allocate local port and add if successful add entry to local endpoint
  * table to mark the pair as used.
@@ -224,10 +244,10 @@ ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4)
 u16
 tcp_allocate_local_port (tcp_main_t * tm, ip46_address_t * ip)
 {
-  u8 unique = 0;
   transport_endpoint_t *tep;
   u32 time_now, tei;
-  u16 min = 1024, max = 65535, tries;  /* XXX configurable ? */
+  u16 min = 1024, max = 65535; /* XXX configurable ? */
+  int tries;
 
   tries = max - min;
   time_now = tcp_time_now ();
@@ -235,37 +255,34 @@ tcp_allocate_local_port (tcp_main_t * tm, ip46_address_t * ip)
   /* Start at random point or max */
   pool_get (tm->local_endpoints, tep);
   clib_memcpy (&tep->ip, ip, sizeof (*ip));
-  tep->port = random_u32 (&time_now) << 16;
-  tep->port = tep->port < min ? max : tep->port;
 
   /* Search for first free slot */
-  while (tries)
+  for (; tries >= 0; tries--)
     {
+      u16 port = 0;
+
+      /* Find a port in the specified range */
+      while (1)
+       {
+         port = random_u32 (&time_now) & PORT_MASK;
+         if (PREDICT_TRUE (port >= min && port < max))
+           break;
+       }
+
+      tep->port = port;
+
+      /* Look it up */
       tei = transport_endpoint_lookup (&tm->local_endpoints_table, &tep->ip,
                                       tep->port);
+      /* If not found, we're done */
       if (tei == TRANSPORT_ENDPOINT_INVALID_INDEX)
        {
-         unique = 1;
-         break;
+         transport_endpoint_table_add (&tm->local_endpoints_table, tep,
+                                       tep - tm->local_endpoints);
+         return tep->port;
        }
-
-      tep->port--;
-
-      if (tep->port < min)
-       tep->port = max;
-
-      tries--;
     }
-
-  if (unique)
-    {
-      transport_endpoint_table_add (&tm->local_endpoints_table, tep,
-                                   tep - tm->local_endpoints);
-
-      return tep->port;
-    }
-
-  /* Failed */
+  /* No free ports */
   pool_put (tm->local_endpoints, tep);
   return -1;
 }
@@ -360,7 +377,10 @@ tcp_connection_open (ip46_address_t * rmt_addr, u16 rmt_port, u8 is_ip4)
   /* Allocate source port */
   lcl_port = tcp_allocate_local_port (tm, &lcl_addr);
   if (lcl_port < 1)
-    return -1;
+    {
+      clib_warning ("Failed to allocate src port");
+      return -1;
+    }
 
   /*
    * Create connection and send SYN
@@ -505,10 +525,10 @@ tcp_session_send_space (transport_connection_t * trans_conn)
 }
 
 u32
-tcp_session_rx_fifo_offset (transport_connection_t * trans_conn)
+tcp_session_tx_fifo_offset (transport_connection_t * trans_conn)
 {
   tcp_connection_t *tc = (tcp_connection_t *) trans_conn;
-  return (tc->snd_una_max - tc->snd_una);
+  return (tc->snd_nxt - tc->snd_una);
 }
 
 /* *INDENT-OFF* */
@@ -524,7 +544,7 @@ const static transport_proto_vft_t tcp4_proto = {
   .cleanup = tcp_session_cleanup,
   .send_mss = tcp_session_send_mss,
   .send_space = tcp_session_send_space,
-  .rx_fifo_offset = tcp_session_rx_fifo_offset,
+  .tx_fifo_offset = tcp_session_tx_fifo_offset,
   .format_connection = format_tcp_session_ip4,
   .format_listener = format_tcp_listener_session_ip4,
   .format_half_open = format_tcp_half_open_session_ip4
@@ -542,7 +562,7 @@ const static transport_proto_vft_t tcp6_proto = {
   .cleanup = tcp_session_cleanup,
   .send_mss = tcp_session_send_mss,
   .send_space = tcp_session_send_space,
-  .rx_fifo_offset = tcp_session_rx_fifo_offset,
+  .tx_fifo_offset = tcp_session_tx_fifo_offset,
   .format_connection = format_tcp_session_ip6,
   .format_listener = format_tcp_listener_session_ip6,
   .format_half_open = format_tcp_half_open_session_ip6
@@ -579,13 +599,32 @@ tcp_timer_establish_handler (u32 conn_index)
 }
 
 void
-tcp_timer_2msl_handler (u32 conn_index)
+tcp_timer_waitclose_handler (u32 conn_index)
 {
   u32 cpu_index = os_get_cpu_number ();
   tcp_connection_t *tc;
 
   tc = tcp_connection_get (conn_index, cpu_index);
-  tc->timers[TCP_TIMER_2MSL] = TCP_TIMER_HANDLE_INVALID;
+  tc->timers[TCP_TIMER_WAITCLOSE] = TCP_TIMER_HANDLE_INVALID;
+
+  /* Session didn't come back with a close(). Send FIN either way
+   * and switch to LAST_ACK. */
+  if (tc->state == TCP_STATE_CLOSE_WAIT)
+    {
+      if (tc->flags & TCP_CONN_FINSNT)
+       {
+         clib_warning ("FIN was sent and still in CLOSE WAIT. Weird!");
+       }
+
+      tcp_send_fin (tc);
+      tc->state = TCP_STATE_LAST_ACK;
+
+      /* Make sure we don't wait in LAST ACK forever */
+      tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME);
+
+      /* Don't delete the connection yet */
+      return;
+    }
 
   tcp_connection_del (tc);
 }
@@ -597,7 +636,7 @@ static timer_expiration_handler *timer_expiration_handlers[TCP_N_TIMERS] =
     tcp_timer_delack_handler,
     0,
     tcp_timer_keep_handler,
-    tcp_timer_2msl_handler,
+    tcp_timer_waitclose_handler,
     tcp_timer_retransmit_syn_handler,
     tcp_timer_establish_handler
 };
@@ -633,18 +672,15 @@ tcp_initialize_timer_wheels (tcp_main_t * tm)
 }
 
 clib_error_t *
-tcp_init (vlib_main_t * vm)
+tcp_main_enable (vlib_main_t * vm)
 {
-  ip_main_t *im = &ip_main;
-  ip_protocol_info_t *pi;
   tcp_main_t *tm = vnet_get_tcp_main ();
+  ip_protocol_info_t *pi;
+  ip_main_t *im = &ip_main;
   vlib_thread_main_t *vtm = vlib_get_thread_main ();
   clib_error_t *error = 0;
   u32 num_threads;
 
-  tm->vlib_main = vm;
-  tm->vnet_main = vnet_get_main ();
-
   if ((error = vlib_call_init_function (vm, ip_main_init)))
     return error;
   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
@@ -697,6 +733,36 @@ tcp_init (vlib_main_t * vm)
   return error;
 }
 
+clib_error_t *
+vnet_tcp_enable_disable (vlib_main_t * vm, u8 is_en)
+{
+  if (is_en)
+    {
+      if (tcp_main.is_enabled)
+       return 0;
+
+      return tcp_main_enable (vm);
+    }
+  else
+    {
+      tcp_main.is_enabled = 0;
+    }
+
+  return 0;
+}
+
+clib_error_t *
+tcp_init (vlib_main_t * vm)
+{
+  tcp_main_t *tm = vnet_get_tcp_main ();
+
+  tm->vlib_main = vm;
+  tm->vnet_main = vnet_get_main ();
+  tm->is_enabled = 0;
+
+  return 0;
+}
+
 VLIB_INIT_FUNCTION (tcp_init);
 
 /*