On Fri, Oct 10, 2025 at 03:47:00PM +0800, Yumei Huang wrote:
Use an exponential backoff timeout with the initial timeout 1s and total timeout 60s.
The commit message needs more information on why making this change to behaviour is desirable (e.g. referencing RFCs).
Signed-off-by: Yumei Huang
--- tcp.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tcp.c b/tcp.c index 85bbdac..0db7f30 100644 --- a/tcp.c +++ b/tcp.c @@ -185,10 +185,15 @@ * for more than TCP_MAX_RETRIES or (tcp_syn_retries + * tcp_syn_linear_timeouts) times in a row, reset the connection * - * - ACK_TIMEOUT: if no ACK segment was received from tap/guest, after sending - * data (flag ACK_FROM_TAP_DUE with ESTABLISHED event), re-send data from the - * socket and reset sequence to what was acknowledged. If this persists for - * more than TCP_MAX_RETRIES times in a row, reset the connection + * - ACK_TIMEOUT_INIT: if no ACK segment was received from tap/guest, after + * sending data (flag ACK_FROM_TAP_DUE with ESTABLISHED event), re-send data + * from the socket and reset sequence to what was acknowledged. It's the + * starting timeout for the first retransmission. If this persists for more + * than TCP_MAX_RETRIES times in a row, reset the connection + * + * - ACK_TIMEOUT_TOTAL: if no ACK segment was received from tap/guest after + * retransmitting data repeatedly from the socket within this time, reset + * the connection * * - FIN_TIMEOUT: if a FIN segment was sent to tap/guest (flag ACK_FROM_TAP_DUE * with TAP_FIN_SENT event), and no ACK is received within this time, reset @@ -344,7 +349,8 @@ enum {
#define ACK_INTERVAL 10 /* ms */ #define SYN_TIMEOUT_INIT 1 /* s */ -#define ACK_TIMEOUT 2 +#define ACK_TIMEOUT_INIT 1 +#define ACK_TIMEOUT_TOTAL 60 #define FIN_TIMEOUT 60 #define ACT_TIMEOUT 7200
@@ -358,6 +364,9 @@ enum { ((conn)->events & (SOCK_FIN_RCVD | TAP_FIN_RCVD))) #define CONN_HAS(conn, set) (((conn)->events & (set)) == (set))
+#define RETRY_ELAPSED(timeout_init, retries) \ + ((timeout_init) * ((1 << ((retries) + 1)) - 2)) + /* Buffers to migrate pending data from send and receive queues. No, they don't * use memory if we don't use them. And we're going away after this, so splurge. */ @@ -596,7 +605,7 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn) (conn->retries - c->tcp.syn_linear_timeouts); } else - it.it_value.tv_sec = ACK_TIMEOUT; + it.it_value.tv_sec = ACK_TIMEOUT_INIT << conn->retries; } else if (CONN_HAS(conn, SOCK_FIN_SENT | TAP_FIN_ACKED)) { it.it_value.tv_sec = FIN_TIMEOUT; } else { @@ -2438,6 +2447,10 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref) } else if (conn->retries == TCP_MAX_RETRIES) { flow_dbg(conn, "retransmissions count exceeded"); tcp_rst(c, conn); + } else if(RETRY_ELAPSED(ACK_TIMEOUT_INIT, conn->retries) >= + ACK_TIMEOUT_TOTAL) { + flow_dbg(conn, "retransmissions timeout exceeded"); + tcp_rst(c, conn);
Having a test both for number of retries and time elapsed seems redundant. RETRY_ELAPSED is pure function of the number of retries, so it should be possible to just have a threshold on the number of retries, which can be calculated from the target total timeout.
} else { flow_dbg(conn, "ACK timeout, retry");
-- 2.47.0
-- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson