tcp: allow local port sharing if 5-tuple available
[vpp.git] / src / vnet / tcp / tcp.c
index 2ac938a..e4335e0 100644 (file)
@@ -208,10 +208,14 @@ tcp_half_open_connection_del (tcp_connection_t * tc)
 int
 tcp_half_open_connection_cleanup (tcp_connection_t * tc)
 {
+  tcp_worker_ctx_t *wrk;
+
   /* Make sure this is the owning thread */
   if (tc->c_thread_index != vlib_get_thread_index ())
     return 1;
-  tcp_timer_reset (tc, TCP_TIMER_RETRANSMIT_SYN);
+
+  wrk = tcp_get_worker (tc->c_thread_index);
+  tcp_timer_reset (&wrk->timer_wheel, tc, TCP_TIMER_RETRANSMIT_SYN);
   tcp_half_open_connection_del (tc);
   return 0;
 }
@@ -354,6 +358,8 @@ tcp_program_cleanup (tcp_worker_ctx_t * wrk, tcp_connection_t * tc)
 void
 tcp_connection_close (tcp_connection_t * tc)
 {
+  tcp_worker_ctx_t *wrk = tcp_get_worker (tc->c_thread_index);
+
   TCP_EVT (TCP_EVT_CLOSE, tc);
 
   /* Send/Program FIN if needed and switch state */
@@ -368,7 +374,8 @@ tcp_connection_close (tcp_connection_t * tc)
       tcp_connection_timers_reset (tc);
       tcp_send_fin (tc);
       tcp_connection_set_state (tc, TCP_STATE_FIN_WAIT_1);
-      tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.finwait1_time);
+      tcp_timer_update (&wrk->timer_wheel, tc, TCP_TIMER_WAITCLOSE,
+                       tcp_cfg.finwait1_time);
       break;
     case TCP_STATE_ESTABLISHED:
       /* If closing with unread data, reset the connection */
@@ -379,7 +386,7 @@ tcp_connection_close (tcp_connection_t * tc)
          tcp_connection_set_state (tc, TCP_STATE_CLOSED);
          session_transport_closed_notify (&tc->connection);
          tcp_program_cleanup (tcp_get_worker (tc->c_thread_index), tc);
-         tcp_worker_stats_inc (tc->c_thread_index, rst_unread, 1);
+         tcp_worker_stats_inc (wrk, rst_unread, 1);
          break;
        }
       if (!transport_max_tx_dequeue (&tc->connection))
@@ -390,7 +397,8 @@ tcp_connection_close (tcp_connection_t * tc)
       /* Set a timer in case the peer stops responding. Otherwise the
        * connection will be stuck here forever. */
       ASSERT (tc->timers[TCP_TIMER_WAITCLOSE] == TCP_TIMER_HANDLE_INVALID);
-      tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.finwait1_time);
+      tcp_timer_set (&wrk->timer_wheel, tc, TCP_TIMER_WAITCLOSE,
+                    tcp_cfg.finwait1_time);
       break;
     case TCP_STATE_CLOSE_WAIT:
       if (!transport_max_tx_dequeue (&tc->connection))
@@ -398,13 +406,15 @@ tcp_connection_close (tcp_connection_t * tc)
          tcp_send_fin (tc);
          tcp_connection_timers_reset (tc);
          tcp_connection_set_state (tc, TCP_STATE_LAST_ACK);
-         tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.lastack_time);
+         tcp_timer_update (&wrk->timer_wheel, tc, TCP_TIMER_WAITCLOSE,
+                           tcp_cfg.lastack_time);
        }
       else
        tc->flags |= TCP_CONN_FINPNDG;
       break;
     case TCP_STATE_FIN_WAIT_1:
-      tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.finwait1_time);
+      tcp_timer_update (&wrk->timer_wheel, tc, TCP_TIMER_WAITCLOSE,
+                       tcp_cfg.finwait1_time);
       break;
     case TCP_STATE_CLOSED:
       /* Cleanup should've been programmed already */
@@ -469,11 +479,11 @@ tcp_connection_timers_init (tcp_connection_t * tc)
 void
 tcp_connection_timers_reset (tcp_connection_t * tc)
 {
+  tcp_worker_ctx_t *wrk = tcp_get_worker (tc->c_thread_index);
   int i;
+
   for (i = 0; i < TCP_N_TIMERS; i++)
-    {
-      tcp_timer_reset (tc, i);
-    }
+    tcp_timer_reset (&wrk->timer_wheel, tc, i);
 }
 
 #if 0
@@ -728,10 +738,7 @@ tcp_alloc_custom_local_endpoint (tcp_main_t * tm, ip46_address_t * lcl_addr,
     }
   port = transport_alloc_local_port (TRANSPORT_PROTO_TCP, lcl_addr);
   if (port < 1)
-    {
-      clib_warning ("Failed to allocate src port");
-      return -1;
-    }
+    return SESSION_E_NOPORT;
   *lcl_port = port;
   return 0;
 }
@@ -757,7 +764,20 @@ tcp_session_open (transport_endpoint_cfg_t * rmt)
                                         rmt, &lcl_addr, &lcl_port);
 
   if (rv)
-    return -1;
+    {
+      if (rv != SESSION_E_PORTINUSE)
+       return rv;
+
+      if (session_lookup_connection (rmt->fib_index, &lcl_addr, &rmt->ip,
+                                    lcl_port, rmt->port, TRANSPORT_PROTO_UDP,
+                                    rmt->is_ip4))
+       return SESSION_E_PORTINUSE;
+
+      /* 5-tuple is available so increase lcl endpoint refcount and proceed
+       * with connection allocation */
+      transport_share_local_endpoint (TRANSPORT_PROTO_UDP, &lcl_addr,
+                                     lcl_port);
+    }
 
   /*
    * Create connection and send SYN
@@ -956,7 +976,7 @@ tcp_timer_waitclose_handler (tcp_connection_t * tc)
          tcp_connection_set_state (tc, TCP_STATE_CLOSED);
          session_transport_closed_notify (&tc->connection);
          tcp_program_cleanup (wrk, tc);
-         tcp_workerp_stats_inc (wrk, to_closewait, 1);
+         tcp_worker_stats_inc (wrk, to_closewait, 1);
          break;
        }
 
@@ -969,8 +989,9 @@ tcp_timer_waitclose_handler (tcp_connection_t * tc)
       session_transport_closed_notify (&tc->connection);
 
       /* Make sure we don't wait in LAST ACK forever */
-      tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, tcp_cfg.lastack_time);
-      tcp_workerp_stats_inc (wrk, to_closewait2, 1);
+      tcp_timer_set (&wrk->timer_wheel, tc, TCP_TIMER_WAITCLOSE,
+                    tcp_cfg.lastack_time);
+      tcp_worker_stats_inc (wrk, to_closewait2, 1);
 
       /* Don't delete the connection yet */
       break;
@@ -992,21 +1013,21 @@ tcp_timer_waitclose_handler (tcp_connection_t * tc)
          tcp_program_cleanup (wrk, tc);
        }
       session_transport_closed_notify (&tc->connection);
-      tcp_workerp_stats_inc (wrk, to_finwait1, 1);
+      tcp_worker_stats_inc (wrk, to_finwait1, 1);
       break;
     case TCP_STATE_LAST_ACK:
       tcp_connection_timers_reset (tc);
       tcp_connection_set_state (tc, TCP_STATE_CLOSED);
       session_transport_closed_notify (&tc->connection);
       tcp_program_cleanup (wrk, tc);
-      tcp_workerp_stats_inc (wrk, to_lastack, 1);
+      tcp_worker_stats_inc (wrk, to_lastack, 1);
       break;
     case TCP_STATE_CLOSING:
       tcp_connection_timers_reset (tc);
       tcp_connection_set_state (tc, TCP_STATE_CLOSED);
       session_transport_closed_notify (&tc->connection);
       tcp_program_cleanup (wrk, tc);
-      tcp_workerp_stats_inc (wrk, to_closing, 1);
+      tcp_worker_stats_inc (wrk, to_closing, 1);
       break;
     case TCP_STATE_FIN_WAIT_2:
       tcp_send_reset (tc);
@@ -1014,7 +1035,7 @@ tcp_timer_waitclose_handler (tcp_connection_t * tc)
       tcp_connection_set_state (tc, TCP_STATE_CLOSED);
       session_transport_closed_notify (&tc->connection);
       tcp_program_cleanup (wrk, tc);
-      tcp_workerp_stats_inc (wrk, to_finwait2, 1);
+      tcp_worker_stats_inc (wrk, to_finwait2, 1);
       break;
     case TCP_STATE_TIME_WAIT:
       tcp_connection_set_state (tc, TCP_STATE_CLOSED);
@@ -1186,7 +1207,7 @@ tcp_expired_timers_dispatch (u32 * expired_timers)
 
   wrk = tcp_get_worker (thread_index);
   n_expired = vec_len (expired_timers);
-  tcp_workerp_stats_inc (wrk, timer_expirations, n_expired);
+  tcp_worker_stats_inc (wrk, timer_expirations, n_expired);
   n_left = clib_fifo_elts (wrk->pending_timers);
 
   /*