X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Ftcp%2Ftcp.c;h=8c8df00f098fb6ef97370d76e4ea876810f96547;hb=3c514d5516498c67f313d3bfe62288d4f36fba54;hp=a3cd1f3e1bb1d5b888c5d0338a293158fe370264;hpb=3c7d4f9e1f54ec6627795b64525f182e2cda7490;p=vpp.git diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c index a3cd1f3e1bb..8c8df00f098 100644 --- a/src/vnet/tcp/tcp.c +++ b/src/vnet/tcp/tcp.c @@ -246,7 +246,7 @@ tcp_connection_del (tcp_connection_t * tc) } tcp_connection_t * -tcp_connection_new (u8 thread_index) +tcp_connection_alloc (u8 thread_index) { tcp_main_t *tm = vnet_get_tcp_main (); tcp_connection_t *tc; @@ -258,6 +258,15 @@ tcp_connection_new (u8 thread_index) return tc; } +void +tcp_connection_free (tcp_connection_t * tc) +{ + tcp_main_t *tm = &tcp_main; + pool_put (tm->connections[tc->c_thread_index], tc); + if (CLIB_DEBUG > 0) + clib_memset (tc, 0xFA, sizeof (*tc)); +} + /** Notify session that connection has been reset. * * Switch state to closed and wait for session to call cleanup. @@ -281,22 +290,25 @@ tcp_connection_reset (tcp_connection_t * tc) tcp_connection_timers_reset (tc); /* Set the cleanup timer, in case the session layer/app don't * cleanly close the connection */ - tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); + tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME); stream_session_reset_notify (&tc->connection); + tcp_connection_set_state (tc, TCP_STATE_CLOSED); break; case TCP_STATE_CLOSE_WAIT: case TCP_STATE_FIN_WAIT_1: case TCP_STATE_FIN_WAIT_2: case TCP_STATE_CLOSING: - tc->state = TCP_STATE_CLOSED; - TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc); + case TCP_STATE_LAST_ACK: tcp_connection_timers_reset (tc); - tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); + tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME); + /* Make sure we mark the session as closed. In some states we may + * be still trying to send data */ + session_stream_close_notify (&tc->connection); + tcp_connection_set_state (tc, TCP_STATE_CLOSED); break; case TCP_STATE_CLOSED: - return; + break; } - tc->state = TCP_STATE_CLOSED; } /** @@ -321,27 +333,30 @@ tcp_connection_close (tcp_connection_t * tc) switch (tc->state) { case TCP_STATE_SYN_SENT: - tc->state = TCP_STATE_CLOSED; + /* Do nothing. Establish timer will pop and cleanup the connection */ break; case TCP_STATE_SYN_RCVD: tcp_connection_timers_reset (tc); tcp_send_fin (tc); - tc->state = TCP_STATE_FIN_WAIT_1; - tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); + tcp_connection_set_state (tc, TCP_STATE_FIN_WAIT_1); + tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_FINWAIT1_TIME); break; case TCP_STATE_ESTABLISHED: if (!session_tx_fifo_max_dequeue (&tc->connection)) tcp_send_fin (tc); else tc->flags |= TCP_CONN_FINPNDG; - tc->state = TCP_STATE_FIN_WAIT_1; + tcp_connection_set_state (tc, TCP_STATE_FIN_WAIT_1); + /* Set a timer in case the peer stops responding. Otherwise the + * connection will be stuck here forever. */ + tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_FINWAIT1_TIME); break; case TCP_STATE_CLOSE_WAIT: if (!session_tx_fifo_max_dequeue (&tc->connection)) { tcp_send_fin (tc); tcp_connection_timers_reset (tc); - tc->state = TCP_STATE_LAST_ACK; + tcp_connection_set_state (tc, TCP_STATE_LAST_ACK); tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_2MSL_TIME); } else @@ -357,14 +372,12 @@ tcp_connection_close (tcp_connection_t * tc) TCP_DBG ("state: %u", tc->state); } - TCP_EVT_DBG (TCP_EVT_STATE_CHANGE, tc); - /* If in CLOSED and WAITCLOSE timer is not set, delete connection. * But instead of doing it now wait until next dispatch cycle to give * the session layer a chance to clear unhandled events */ if (!tcp_timer_is_active (tc, TCP_TIMER_WAITCLOSE) && tc->state == TCP_STATE_CLOSED) - tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, 1); + tcp_timer_update (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); } static void @@ -1208,8 +1221,9 @@ tcp_timer_establish_handler (u32 conn_index) if (tc) { ASSERT (tc->state == TCP_STATE_SYN_SENT); - session_stream_connect_notify (&tc->connection, 1 /* fail */ ); - TCP_DBG ("establish pop: %U", format_tcp_connection, tc, 2); + /* Notify app if we haven't tried to clean this up already */ + if (!(tc->flags & TCP_CONN_HALF_OPEN_DONE)) + session_stream_connect_notify (&tc->connection, 1 /* fail */ ); } else { @@ -1217,7 +1231,6 @@ tcp_timer_establish_handler (u32 conn_index) /* note: the connection may have already disappeared */ if (PREDICT_FALSE (tc == 0)) return; - TCP_DBG ("establish pop: %U", format_tcp_connection, tc, 2); ASSERT (tc->state == TCP_STATE_SYN_RCVD); /* Start cleanup. App wasn't notified yet so use delete notify as * opposed to delete to cleanup session layer state. */ @@ -1236,32 +1249,67 @@ 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) + switch (tc->state) { - if (tc->flags & TCP_CONN_FINSNT) + case TCP_STATE_CLOSE_WAIT: + tcp_connection_timers_reset (tc); + session_stream_close_notify (&tc->connection); + + if (!(tc->flags & TCP_CONN_FINPNDG)) { - clib_warning ("FIN was sent and still in CLOSE WAIT. Weird!"); + tcp_connection_set_state (tc, TCP_STATE_CLOSED); + tcp_timer_set (tc, TCP_TIMER_WAITCLOSE, TCP_CLEANUP_TIME); + break; } - /* Make sure we don't try to send unsent data */ - tcp_connection_timers_reset (tc); + /* 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; + break; + case TCP_STATE_FIN_WAIT_1: + tcp_connection_timers_reset (tc); + if (tc->flags & TCP_CONN_FINPNDG) + { + /* 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); + session_stream_close_notify (&tc->connection); + break; + default: + tcp_connection_del (tc); + break; } - - tcp_connection_del (tc); } /* *INDENT-OFF* */