aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vnet/session/session.c18
-rw-r--r--src/vnet/session/session.h1
-rw-r--r--src/vnet/tcp/tcp.c60
-rw-r--r--src/vnet/tcp/tcp.h7
-rw-r--r--src/vnet/tcp/tcp_input.c5
-rw-r--r--src/vnet/tcp/tcp_output.c9
6 files changed, 83 insertions, 17 deletions
diff --git a/src/vnet/session/session.c b/src/vnet/session/session.c
index 4081f909482..6492ce7089e 100644
--- a/src/vnet/session/session.c
+++ b/src/vnet/session/session.c
@@ -830,6 +830,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.
*/
void
diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h
index 6c1bdb639df..3b729234967 100644
--- a/src/vnet/session/session.h
+++ b/src/vnet/session/session.h
@@ -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);
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index cbf570f2dc7..b0c44638b36 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -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* */
diff --git a/src/vnet/tcp/tcp.h b/src/vnet/tcp/tcp.h
index 2327b9a6ad5..bd0fa9a7204 100644
--- a/src/vnet/tcp/tcp.h
+++ b/src/vnet/tcp/tcp.h
@@ -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);
diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c
index 32de369b4cc..b5e3ce74c38 100644
--- a/src/vnet/tcp/tcp_input.c
+++ b/src/vnet/tcp/tcp_input.c
@@ -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);
diff --git a/src/vnet/tcp/tcp_output.c b/src/vnet/tcp/tcp_output.c
index fc4bceb58cb..5f48d410126 100644
--- a/src/vnet/tcp/tcp_output.c
+++ b/src/vnet/tcp/tcp_output.c
@@ -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