From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: passt.top; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=gZVhE75Y; dkim-atps=neutral Received: from mail-ed2-x1d.google.com (mail-ed2-x1d.google.com [IPv6:2a00:1450:4864:33::1d]) by passt.top (Postfix) with ESMTPS id 21D3E5A0269 for ; Mon, 28 Sep 2026 12:31:58 +0200 (CEST) Received: by mail-ed2-x1d.google.com with SMTP id 4fb4d7f45d1cf-6aafc6ab6d1so1634217a12.0 for ; Mon, 28 Sep 2026 03:31:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1790591517; x=1791196317; darn=passt.top; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=AwmN/S94/TzmPlppDMUCmxivulk1DhKN1/ttWifgG3s=; b=gZVhE75Y9u+bEPD8g6nUzkbeNg5yO6wuRUznlZnx7wzZN2K0fNVHnvTgR7iJZ6T4tF X8r2sRtYkPuf/DZa7UmOP43dAywrJvtd0FoimmZNxAQxj3O4qJyMl2WHYAW7jwFG3Cw8 NrJgelcl2FeobTI6AuRYoJvt0pTeEZ/junZ6RS26aXDl40ZPvqtPzLXq/RlaPdhb0ymj LLn3vhB/H0GEFOgYx+iyyfZA3I7jBppiq77PAGuAE+zMBMm8YMRqECOjaUw0lw5iGRd4 +gcwxz4w0FQ/j9nSYCqEntmE9cd9+8SVdUnQOfeFLTJj4yZ6+OyVyj3od5plOVQa3UAh 4LBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790591517; x=1791196317; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=AwmN/S94/TzmPlppDMUCmxivulk1DhKN1/ttWifgG3s=; b=srHu6l0VaQZ1IROGxp3dU87QHcHjgaX9quUpUYTrf3wkIIdsIVfpN3fIG2CxI/bls4 nmlGyq2rAAhUgQM34bnfCKNFRCKjULI9nzkXxFkHD59aSneKEAYPe/HEQlUr+7W7Wetp TLVq3l+u6p8n6gzGPzPeJWxpiJ3j4RJDcL4AI8IEGesNWCd7d9ypbKE9qTXIGianz5Jn 73tLrnK9RmBe6ab4b3RiZZ6fD3Qjb2eQDb8M+Rsl1lW4sCG1YI4GmkyqigM23EibpCRw SiQguS6xUXec2vXtGRPyaB4l23067ZhQ+YfUbxmaFZLetHE6TcKlWnwD/Rn1KIRJ140a EMnw== X-Gm-Message-State: AFuF++nqLicJp0ShzJNQjKtOjUKyuOYD3terp5sxmIlW8RBXMZrjrGef /5MgxQRpwn+3teVmt2FacJNjY3yX7XGPvKkYPJlp2beQZPaDqHWv2RuuTf7tjG9zXPSFLQ== X-Gm-Gg: AYBFou065myrkEn+5Bu2GFjgmHR1DLaTCV4cvfLq4/FXmfmx1yQsj+mvUSfFbHLhtY6 fD1qGUsgL+b+WLNYzK6rZ9zwOOIFtvi2/qbYaMnsKGS3vKPUTAnVyicMrhgdx9a1VjU45CIyuJb Wk6phVS8ap6UYY/XaVdIp32tjKd9oh5ediy3Hkrzng2/gzUT9kcAiHuhs9byJ87jgz7kzstNsWr 2FZQTYOQz+LeFW0m3Ns2WBRQLmI8e26oeoAXLKSn6NaVqvadx9FSXQ4vWJwbejn/VsXJ+1sxxJk nV6cI9WZzIwj1laPuypyNX6u7gRjGckjomU7xVzq1wkH8ddE37zlp3+2f93iqp/yiutQIW7Zini Pda6eutjl9xdrj9c3f14BVSML1MlLft9MoX6pQczOervmvZ/M7mKlaGqvzyldI+MzzE0FhV2r4y GUmobTEnAYoLRB60sqyiepUHQfEnMUguR+wTrZ38hEkZM5FwwaCmdE7sRLuTMEfMUrDXkEe1S4/ iVHXES1xWY= X-Received: by 2002:a17:906:6a06:b0:c25:8b1e:3750 with SMTP id a640c23a62f3a-c2ac2334053mr1123030666b.12.1790591516834; Mon, 28 Sep 2026 03:31:56 -0700 (PDT) Received: from localhost.localdomain ([31.216.123.179]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c2ae678c90csm471470466b.6.2026.09.28.03.31.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 28 Sep 2026 03:31:55 -0700 (PDT) From: Aris Konstantoulas To: passt-dev@passt.top Subject: [PATCH] tcp: Don't fast re-transmit if only our FIN is outstanding Date: Mon, 28 Sep 2026 13:31:20 +0300 Message-ID: <20260928103120.233586-1-arist.kon@gmail.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-MailFrom: arist.kon@gmail.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation Message-ID-Hash: J7XZ7F6UQ5GKJTJZYPZVXHZCOK6FPKBZ X-Message-ID-Hash: J7XZ7F6UQ5GKJTJZYPZVXHZCOK6FPKBZ X-Mailman-Approved-At: Mon, 28 Sep 2026 13:38:19 +0200 CC: Stefano Brivio , David Gibson , Aris Konstantoulas X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Aris Konstantoulas In the TAP_FIN_RCVD path of tcp_tap_handler(), a bare segment from the guest acknowledging exactly seq_ack_from_tap, with an unchanged window, is taken as a duplicate ACK and triggers a fast re-transmit. If the only unacknowledged sequence number is our own FIN, that's harmful: tcp_rewind_seq() rewinds seq_to_tap and clears TAP_FIN_SENT, so tcp_data_from_sock() immediately sends the FIN again. If the guest answers that FIN with the same bare ACK, as a socket in TIME-WAIT will, we loop at packet rate: - conn->retries is never incremented on this path, so we never reach TCP_MAX_RETRIES and tcp_rst() - ACK_FROM_TAP_DUE is re-armed on every iteration, so the backed-off re-transmission in tcp_timer_handler() never fires - TAP_FIN_ACKED can't be set, as it requires TAP_FIN_SENT, which the rewind just cleared On an idle Podman host (rootless, pasta), this showed up as a single flow exchanging ~45,000 54-byte segments per second between pasta and a container whose socket was in TIME-WAIT, with pasta using ~75% of one core, until the socket was killed by hand. It recurred on the idle teardown of an HTTP/2 connection to an ACME server. With a raw-socket peer driving the same sequence against pasta at f8df3f1, pasta re-sent the FIN 727,509 times in 10 seconds. With this change it's re-transmitted by the timer at 1, 3, 7, 15, 31, 63 and 127 seconds, and the connection is reset once TCP_MAX_RETRIES is reached. Don't consider a duplicate ACK as a fast re-transmit trigger if the only outstanding sequence number is the FIN, and leave it to the timer. Fast re-transmit of data is unaffected, with or without a FIN queued after it. Fixes: bde1847960cf ("tcp: Fast re-transmit if half-closed, make TAP_FIN_RCVD path consistent") Link: https://bugs.passt.top/show_bug.cgi?id=125 Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Aris Konstantoulas --- Notes: Notes for reviewers, not for the commit message: - Reproducer: a raw AF_PACKET TCP peer in pasta's namespace completes a handshake, closes first, then answers each FIN from pasta with a bare ACK for seq_ack_from_tap, with an unchanged window. I can post it (Python, ~200 lines) if useful. - Open question: I haven't established how a real flow first reaches the state where the peer ACKs exactly up to, but not past, our FIN. The live capture showed the TIME-WAIT socket ACKing the sequence number of pasta's FIN itself. That would fit a FIN re-sent after the first one was already acknowledged, one sequence number further on, that is, tcp_rewind_seq() clearing TAP_FIN_SENT with nothing outstanding. Candidates are the zero-window rewind and the fast-retransmit check in tcp_data_from_tap(), neither of which checks that anything is outstanding. This patch breaks the loop in either case, but not that possible entry path. I'm happy to look into it further, possibly as part of bug 125. tcp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tcp.c b/tcp.c index 3b78d2e..e279f8a 100644 --- a/tcp.c +++ b/tcp.c @@ -2417,15 +2417,22 @@ int tcp_tap_handler(const struct ctx *c, uint8_t pif, sa_family_t af, /* Established connections not accepting data from tap */ if (conn->events & TAP_FIN_RCVD) { + bool fin_only, retr; size_t dlen; - bool retr; if ((dlen = tcp_packet_data_len(th, l4len))) { flow_dbg(conn, "data segment in CLOSE-WAIT (%zu B)", dlen); } - retr = th->ack && !th->fin && + /* If only our FIN is outstanding, rewinding on a duplicate ACK + * would re-send it on every ACK, bypassing the backoff and + * retry limit of tcp_timer_handler(): let the timer do it. + */ + fin_only = (conn->events & TAP_FIN_SENT) && + conn->seq_to_tap == conn->seq_ack_from_tap + 1; + + retr = th->ack && !th->fin && !fin_only && ntohl(th->ack_seq) == conn->seq_ack_from_tap && ntohs(th->window) == conn->wnd_from_tap; -- 2.43.0