tcp: improve waitclose in closing states 91/16591/4
authorFlorin Coras <fcoras@cisco.com>
Fri, 21 Dec 2018 21:54:09 +0000 (13:54 -0800)
committerFlorin Coras <fcoras@cisco.com>
Sat, 22 Dec 2018 18:34:45 +0000 (10:34 -0800)
Change-Id: I90056176194cb2a144d49a3cb283653d8d30f051
Signed-off-by: Florin Coras <fcoras@cisco.com>
src/vnet/session/session.c
src/vnet/session/session.h
src/vnet/tcp/tcp.c
src/vnet/tcp/tcp.h
src/vnet/tcp/tcp_input.c
src/vnet/tcp/tcp_output.c

index 4081f90..6492ce7 100644 (file)
@@ -829,6 +829,24 @@ stream_session_delete_notify (transport_connection_t * tc)
     }
 }
 
+/**
+ * Notification from transport that session can be closed
+ *
+ * Should be called by transport only if it was closed with non-empty
+ * tx fifo and once it decides to begin the closing procedure prior to
+ * issuing a delete notify. This gives the chance to the session layer
+ * to cleanup any outstanding events.
+ */
+void
+session_stream_close_notify (transport_connection_t * tc)
+{
+  stream_session_t *s;
+
+  if (!(s = session_get_if_valid (tc->s_index, tc->thread_index)))
+    return;
+  s->session_state = SESSION_STATE_CLOSED;
+}
+
 /**
  * Notify application that connection has been reset.
  */
index 6c1bdb6..3b72923 100644 (file)
@@ -613,6 +613,7 @@ void stream_session_init_fifos_pointers (transport_connection_t * tc,
 int stream_session_accept_notify (transport_connection_t * tc);
 void stream_session_disconnect_notify (transport_connection_t * tc);
 void stream_session_delete_notify (transport_connection_t * tc);
+void session_stream_close_notify (transport_connection_t * tc);
 void stream_session_reset_notify (transport_connection_t * tc);
 int stream_session_accept (transport_connection_t * tc, u32 listener_index,
                           u8 notify);
index cbf570f..b0c4463 100644 (file)
@@ -299,6 +299,7 @@ tcp_connection_reset (tcp_connection_t * tc)
     case TCP_STATE_FIN_WAIT_1:
     case TCP_STATE_FIN_WAIT_2:
     case TCP_STATE_CLOSING:
+    case TCP_STATE_LAST_ACK:
       tcp_connection_timers_reset (tc);
       tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
       tc->state = TCP_STATE_CLOSED;
@@ -1249,38 +1250,65 @@ tcp_timer_waitclose_handler (u32 conn_index)
   tc = tcp_connection_get (conn_index, thread_index);
   if (!tc)
     return;
+
   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 && (tc->flags & TCP_CONN_FINPNDG))
+  switch (tc->state)
     {
-      /* Make sure we don't try to send unsent data */
+    case TCP_STATE_CLOSE_WAIT:
       tcp_connection_timers_reset (tc);
+
+      if (!(tc->flags & TCP_CONN_FINPNDG))
+       {
+         tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+         tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
+         break;
+       }
+
+      /* Session didn't come back with a close. Send FIN either way
+       * and switch to LAST_ACK. */
       tcp_cong_recovery_off (tc);
+      /* Make sure we don't try to send unsent data */
       tc->snd_una_max = tc->snd_nxt = tc->snd_una;
       tcp_send_fin (tc);
-      tc->state = TCP_STATE_LAST_ACK;
+      tcp_connection_set_state (tc, 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;
-    }
-  else if (tc->state == TCP_STATE_FIN_WAIT_1)
-    {
+      break;
+    case TCP_STATE_FIN_WAIT_1:
       tcp_connection_timers_reset (tc);
-      /* If FIN pending send it before closing */
       if (tc->flags & TCP_CONN_FINPNDG)
-       tcp_send_fin (tc);
-      tc->state = TCP_STATE_CLOSED;
-      /* Wait for session layer to clean up tx events */
+       {
+         /* If FIN pending send it before closing and wait as long as
+          * the rto timeout would wait. Notify session layer that transport
+          * is closed. We haven't sent everything but we did try. */
+         tcp_cong_recovery_off (tc);
+         tcp_send_fin (tc);
+         tcp_timer_set (tc, TCP_TIMER_WAITCLOSE,
+                        clib_max (tc->rto * TCP_TO_TIMER_TICK, 1));
+         session_stream_close_notify (&tc->connection);
+       }
+      else
+       {
+         /* We've sent the fin but no progress. Close the connection and
+          * to make sure everything is flushed, setup a cleanup timer */
+         tcp_connection_set_state (tc, TCP_STATE_CLOSED);
+         tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
+       }
+      break;
+    case TCP_STATE_LAST_ACK:
+    case TCP_STATE_CLOSING:
+      tcp_connection_timers_reset (tc);
+      tcp_connection_set_state (tc, TCP_STATE_CLOSED);
       tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME);
-      return;
+      break;
+    default:
+      tcp_connection_del (tc);
+      break;
     }
-
-  tcp_connection_del (tc);
 }
 
 /* *INDENT-OFF* */
index 2327b9a..bd0fa9a 100644 (file)
@@ -562,6 +562,13 @@ tcp_get_connection_from_transport (transport_connection_t * tconn)
   return (tcp_connection_t *) tconn;
 }
 
+always_inline void
+tcp_connection_set_state (tcp_connection_t * tc, tcp_state_t state)
+{
+  tc->state = state;
+  TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc);
+}
+
 void tcp_connection_close (tcp_connection_t * tc);
 void tcp_connection_cleanup (tcp_connection_t * tc);
 void tcp_connection_del (tcp_connection_t * tc);
index 32de369..b5e3ce7 100644 (file)
@@ -509,6 +509,7 @@ tcp_estimate_initial_rtt (tcp_connection_t * tc)
 
   if (mrtt > 0 && mrtt < TCP_RTT_MAX)
     tcp_estimate_rtt (tc, mrtt);
+  tcp_update_rto (tc);
 }
 
 /**
@@ -3710,6 +3711,10 @@ do {                                                             \
   _(FIN_WAIT_1, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
     TCP_ERROR_NONE);
   _(CLOSING, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_SYN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+  _(CLOSING, TCP_FLAG_RST | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
+    TCP_ERROR_NONE);
   /* FIN confirming that the peer (app) has closed */
   _(FIN_WAIT_2, TCP_FLAG_FIN, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
   _(FIN_WAIT_2, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
index fc4bceb..5f48d41 100644 (file)
@@ -392,9 +392,13 @@ tcp_make_options (tcp_connection_t * tc, tcp_options_t * opts,
   switch (state)
     {
     case TCP_STATE_ESTABLISHED:
+    case TCP_STATE_CLOSE_WAIT:
     case TCP_STATE_FIN_WAIT_1:
+    case TCP_STATE_LAST_ACK:
+    case TCP_STATE_CLOSING:
+    case TCP_STATE_FIN_WAIT_2:
+    case TCP_STATE_TIME_WAIT:
     case TCP_STATE_CLOSED:
-    case TCP_STATE_CLOSE_WAIT:
       return tcp_make_established_options (tc, opts);
     case TCP_STATE_SYN_RCVD:
       return tcp_make_synack_options (tc, opts);
@@ -1124,6 +1128,8 @@ tcp_make_state_flags (tcp_connection_t * tc, tcp_state_t next_state)
     {
     case TCP_STATE_ESTABLISHED:
     case TCP_STATE_CLOSE_WAIT:
+    case TCP_STATE_TIME_WAIT:
+    case TCP_STATE_FIN_WAIT_2:
       return TCP_FLAG_ACK;
     case TCP_STATE_SYN_RCVD:
       return TCP_FLAG_SYN | TCP_FLAG_ACK;
@@ -1131,6 +1137,7 @@ tcp_make_state_flags (tcp_connection_t * tc, tcp_state_t next_state)
       return TCP_FLAG_SYN;
     case TCP_STATE_LAST_ACK:
     case TCP_STATE_FIN_WAIT_1:
+    case TCP_STATE_CLOSING:
       if (tc->snd_nxt + 1 < tc->snd_una_max)
        return TCP_FLAG_ACK;
       else