tcp: fix v6 sessions
[vpp.git] / src / vnet / tcp / tcp.c
index 59b2074..d169002 100644 (file)
@@ -173,7 +173,7 @@ tcp_connection_cleanup (tcp_connection_t * tc)
 
   /* Cleanup local endpoint if this was an active connect */
   tepi = transport_endpoint_lookup (&tm->local_endpoints_table, &tc->c_lcl_ip,
-                                   tc->c_lcl_port);
+                                   clib_net_to_host_u16 (tc->c_lcl_port));
   if (tepi != TRANSPORT_ENDPOINT_INVALID_INDEX)
     {
       tep = pool_elt_at_index (tm->local_endpoints, tepi);
@@ -288,18 +288,31 @@ tcp_connection_close (tcp_connection_t * tc)
 {
   TCP_EVT_DBG (TCP_EVT_CLOSE, tc);
 
-  /* Send FIN if needed */
-  if (tc->state == TCP_STATE_ESTABLISHED
-      || tc->state == TCP_STATE_SYN_RCVD || tc->state == TCP_STATE_CLOSE_WAIT)
-    tcp_send_fin (tc);
-
-  /* Switch state */
-  if (tc->state == TCP_STATE_ESTABLISHED || tc->state == TCP_STATE_SYN_RCVD)
-    tc->state = TCP_STATE_FIN_WAIT_1;
-  else if (tc->state == TCP_STATE_SYN_SENT)
-    tc->state = TCP_STATE_CLOSED;
-  else if (tc->state == TCP_STATE_CLOSE_WAIT)
-    tc->state = TCP_STATE_LAST_ACK;
+  /* Send/Program FIN if needed and switch state */
+  switch (tc->state)
+    {
+    case TCP_STATE_SYN_SENT:
+      tc->state = TCP_STATE_CLOSED;
+      break;
+    case TCP_STATE_SYN_RCVD:
+      tcp_send_fin (tc);
+      tc->state = TCP_STATE_FIN_WAIT_1;
+      break;
+    case TCP_STATE_ESTABLISHED:
+      if (!stream_session_tx_fifo_max_dequeue (&tc->connection))
+       tcp_send_fin (tc);
+      else
+       tc->flags |= TCP_CONN_FINPNDG;
+      tc->state = TCP_STATE_FIN_WAIT_1;
+      break;
+    case TCP_STATE_CLOSE_WAIT:
+      tcp_send_fin (tc);
+      tc->state = TCP_STATE_LAST_ACK;
+      break;
+    default:
+      clib_warning ("shouldn't be here");
+    }
+
   TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc);
 
   /* If in CLOSED and WAITCLOSE timer is not set, delete connection now */
@@ -349,7 +362,11 @@ ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4)
       /* *INDENT-OFF* */
       foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ ,
       ({
-        return ip_interface_address_get_address (lm6, ia);
+        ip6_address_t *rv;
+        rv = ip_interface_address_get_address (lm6, ia);
+        /* Trying to use a link-local ip6 src address is a fool's errand */
+        if (!ip6_address_is_link_local_unicast (rv))
+          return rv;
       }));
       /* *INDENT-ON* */
     }
@@ -367,25 +384,24 @@ tcp_allocate_local_port (ip46_address_t * ip)
 {
   tcp_main_t *tm = vnet_get_tcp_main ();
   transport_endpoint_t *tep;
-  u32 time_now, tei;
+  u32 tei;
   u16 min = 1024, max = 65535; /* XXX configurable ? */
-  int tries;
+  int tries, limit;
 
-  tries = max - min;
-  time_now = tcp_time_now ();
+  limit = max - min;
 
   /* Only support active opens from thread 0 */
   ASSERT (vlib_get_thread_index () == 0);
 
   /* Search for first free slot */
-  for (; tries >= 0; tries--)
+  for (tries = 0; tries < limit; tries++)
     {
       u16 port = 0;
 
       /* Find a port in the specified range */
       while (1)
        {
-         port = random_u32 (&time_now) & PORT_MASK;
+         port = random_u32 (&tm->port_allocator_seed) & PORT_MASK;
          if (PREDICT_TRUE (port >= min && port < max))
            break;
        }
@@ -623,6 +639,14 @@ tcp_connection_open (transport_endpoint_t * rmt)
       else
        {
          ip6 = ip_interface_get_first_ip (sw_if_index, 0);
+         if (ip6 == 0)
+           {
+             clib_warning ("no routable ip6 addresses on %U",
+                           format_vnet_sw_if_index_name, vnet_get_main (),
+                           sw_if_index);
+             return -1;
+           }
+
          clib_memcpy (&lcl_addr.ip6, ip6, sizeof (*ip6));
        }
     }
@@ -1094,10 +1118,17 @@ tcp_timer_establish_handler (u32 conn_index)
   tcp_connection_t *tc;
 
   tc = tcp_half_open_connection_get (conn_index);
+  if (tc)
+    {
+      ASSERT (tc->state == TCP_STATE_SYN_SENT);
+      stream_session_connect_notify (&tc->connection, 1 /* fail */ );
+    }
+  else
+    {
+      tc = tcp_connection_get (conn_index, vlib_get_thread_index ());
+      ASSERT (tc->state == TCP_STATE_SYN_RCVD);
+    }
   tc->timers[TCP_TIMER_ESTABLISH] = TCP_TIMER_HANDLE_INVALID;
-
-  ASSERT (tc->state == TCP_STATE_SYN_SENT);
-  stream_session_connect_notify (&tc->connection, 1 /* fail */ );
   tcp_connection_cleanup (tc);
 }
 
@@ -1189,8 +1220,9 @@ tcp_main_enable (vlib_main_t * vm)
   vlib_thread_main_t *vtm = vlib_get_thread_main ();
   clib_error_t *error = 0;
   u32 num_threads;
-  int thread, i;
+  int i, thread;
   tcp_connection_t *tc __attribute__ ((unused));
+  u32 preallocated_connections_per_thread;
 
   if ((error = vlib_call_init_function (vm, ip_main_init)))
     return error;
@@ -1211,6 +1243,7 @@ tcp_main_enable (vlib_main_t * vm)
   pi->unformat_pg_edit = unformat_pg_tcp_header;
 
   ip4_register_protocol (IP_PROTOCOL_TCP, tcp4_input_node.index);
+  ip6_register_protocol (IP_PROTOCOL_TCP, tcp6_input_node.index);
 
   /* Register as transport with session layer */
   session_register_transport (TRANSPORT_PROTO_TCP, 1, &tcp_proto);
@@ -1224,14 +1257,26 @@ tcp_main_enable (vlib_main_t * vm)
   vec_validate (tm->connections, num_threads - 1);
 
   /*
-   * Preallocate connections
+   * Preallocate connections. Assume that thread 0 won't
+   * use preallocated threads when running multi-core
    */
-  for (thread = 0; thread < num_threads; thread++)
+  if (num_threads == 1)
+    {
+      thread = 0;
+      preallocated_connections_per_thread = tm->preallocated_connections;
+    }
+  else
     {
-      for (i = 0; i < tm->preallocated_connections; i++)
+      thread = 1;
+      preallocated_connections_per_thread =
+       tm->preallocated_connections / (num_threads - 1);
+    }
+  for (; thread < num_threads; thread++)
+    {
+      for (i = 0; i < preallocated_connections_per_thread; i++)
        pool_get (tm->connections[thread], tc);
 
-      for (i = 0; i < tm->preallocated_connections; i++)
+      for (i = 0; i < preallocated_connections_per_thread; i++)
        pool_put_index (tm->connections[thread], i);
     }
 
@@ -1257,13 +1302,25 @@ tcp_main_enable (vlib_main_t * vm)
     / TCP_TSTAMP_RESOLUTION;
 
   clib_bihash_init_24_8 (&tm->local_endpoints_table, "local endpoint table",
-                        200000 /* $$$$ config parameter nbuckets */ ,
-                        (64 << 20) /*$$$ config parameter table size */ );
+                        1000000 /* $$$$ config parameter nbuckets */ ,
+                        (512 << 20) /*$$$ config parameter table size */ );
+
+  /* Initialize [port-allocator] random number seed */
+  tm->port_allocator_seed = (u32) clib_cpu_time_now ();
+
   if (num_threads > 1)
     {
       clib_spinlock_init (&tm->half_open_lock);
       clib_spinlock_init (&tm->local_endpoints_lock);
     }
+
+  vec_validate (tm->tx_frames[0], num_threads - 1);
+  vec_validate (tm->tx_frames[1], num_threads - 1);
+
+  tm->bytes_per_buffer = vlib_buffer_free_list_buffer_size
+    (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
+
+  vec_validate (tm->time_now, num_threads - 1);
   return error;
 }
 
@@ -1289,16 +1346,12 @@ clib_error_t *
 tcp_init (vlib_main_t * vm)
 {
   tcp_main_t *tm = vnet_get_tcp_main ();
-
-  tm->vnet_main = vnet_get_main ();
   tm->is_enabled = 0;
-
   return 0;
 }
 
 VLIB_INIT_FUNCTION (tcp_init);
 
-
 static clib_error_t *
 tcp_config_fn (vlib_main_t * vm, unformat_input_t * input)
 {