mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 10:58:49 +08:00
tcp_close: Fix a race with passive close
tcp_close disposes the connection immediately if it's called in TCP_LAST_ACK. If it happens, we will end up with responding the last ACK with a RST. This commit fixes it by making tcp_close wait for the completion of the passive close.
This commit is contained in:
parent
50eee2f081
commit
669619a06a
1 changed files with 36 additions and 8 deletions
|
@ -118,10 +118,16 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
|
|||
goto end_wait;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
||||
/* Check if all outstanding bytes have been ACKed */
|
||||
/* Check if all outstanding bytes have been ACKed.
|
||||
*
|
||||
* Note: in case of passive close, this ensures our FIN is acked.
|
||||
*/
|
||||
|
||||
else if (conn->tx_unacked != 0 || !sq_empty(&conn->write_q))
|
||||
else if (conn->tx_unacked != 0
|
||||
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
|
||||
|| !sq_empty(&conn->write_q)
|
||||
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
|
||||
)
|
||||
{
|
||||
/* No... we are still waiting for ACKs. Drop any received data, but
|
||||
* do not yet report TCP_CLOSE in the response.
|
||||
|
@ -129,12 +135,27 @@ static uint16_t tcp_close_eventhandler(FAR struct net_driver_s *dev,
|
|||
|
||||
dev->d_len = 0;
|
||||
flags &= ~TCP_NEWDATA;
|
||||
ninfo("waiting for ack\n");
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
|
||||
|
||||
else
|
||||
{
|
||||
/* Note: the following state shouldn't reach here because
|
||||
*
|
||||
* FIN_WAIT_1, CLOSING, LAST_ACK
|
||||
* should have tx_unacked != 0, already handled above
|
||||
*
|
||||
* CLOSED, TIME_WAIT
|
||||
* a TCP_CLOSE callback should have already cleared this callback
|
||||
* when transitioning to these states.
|
||||
*
|
||||
* FIN_WAIT_2
|
||||
* new data is dropped by tcp_input without invoking tcp_callback.
|
||||
* timer is handled by tcp_timer without invoking tcp_callback.
|
||||
* TCP_CLOSE is handled above.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(conn->tcpstateflags == TCP_ESTABLISHED);
|
||||
|
||||
/* Drop data received in this state and make sure that TCP_CLOSE
|
||||
* is set in the response
|
||||
*/
|
||||
|
@ -278,9 +299,16 @@ static inline int tcp_close_disconnect(FAR struct socket *psock)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Check for the case where the host beat us and disconnected first */
|
||||
/* TCP_ESTABLISHED
|
||||
* We need to initiate an active close and wait for its completion.
|
||||
*
|
||||
* TCP_LAST_ACK
|
||||
* We still need to wait for the ACK for our FIN, possibly
|
||||
* retransmitting the FIN, before disposing the connection.
|
||||
*/
|
||||
|
||||
if (conn->tcpstateflags == TCP_ESTABLISHED &&
|
||||
if ((conn->tcpstateflags == TCP_ESTABLISHED ||
|
||||
conn->tcpstateflags == TCP_LAST_ACK) &&
|
||||
(state.cl_cb = tcp_callback_alloc(conn)) != NULL)
|
||||
{
|
||||
/* Set up to receive TCP data event callbacks */
|
||||
|
|
Loading…
Reference in a new issue