From: Jeff Jo <jeffjo@openai.com>
To: netdev@vger.kernel.org
Cc: edumazet@google.com, ncardwell@google.com, kuniyu@google.com,
davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, shuah@kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org
Subject: [PATCH net 2/2] selftests: net: check timestamp echo after an old ACK
Date: Mon, 21 Sep 2026 15:26:12 -0700 [thread overview]
Message-ID: <20260921222609.50824-6-jeffjo@openai.com> (raw)
In-Reply-To: <20260921222609.50824-4-jeffjo@openai.com>
Add a regression test for a gap-filling packet whose acknowledgment has
become old. Linux first sends data to the peer. Deliver two peer data
packets out of order: the later packet acknowledges Linux's data, while
the delayed packet still carries the earlier acknowledgment. Require
the final reply to echo the delayed packet's timestamp.
Use a large jump in peer timestamps to represent the idle interval.
The test checks the echo directly, without waiting for that interval
and without packet loss or retransmission.
Packetdrill does not currently compare the timestamp-echo field (TSecr),
so the packet script alone can pass on a broken kernel. Add a shell
wrapper to check all four outgoing timestamp echoes in packetdrill's
output. Register the wrapper as the runnable test and keep the packet
script as a supporting file.
Run the test in a separate network namespace for each of IPv4, IPv6
and IPv4-mapped IPv6.
Assisted-by: LLM
Signed-off-by: Jeff Jo <jeffjo@openai.com>
---
.../selftests/net/packetdrill/Makefile | 5 +-
.../net/packetdrill/tcp_old_ack_ts.pkt | 23 ++++++++
.../net/packetdrill/tcp_old_ack_ts.sh | 57 +++++++++++++++++++
3 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt
create mode 100755 tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.sh
diff --git a/tools/testing/selftests/net/packetdrill/Makefile b/tools/testing/selftests/net/packetdrill/Makefile
index ff54641493e9..4695d8258aa1 100644
--- a/tools/testing/selftests/net/packetdrill/Makefile
+++ b/tools/testing/selftests/net/packetdrill/Makefile
@@ -7,6 +7,9 @@ TEST_INCLUDES := \
../../kselftest/ktap_helpers.sh \
# end of TEST_INCLUDES
-TEST_PROGS := $(wildcard *.pkt)
+# This fixture requires its wrapper to validate outbound timestamp echoes.
+TEST_PROGS := $(filter-out tcp_old_ack_ts.pkt,$(wildcard *.pkt))
+TEST_PROGS += tcp_old_ack_ts.sh
+TEST_FILES := tcp_old_ack_ts.pkt
include ../../lib.mk
diff --git a/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt b/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt
new file mode 100644
index 000000000000..d13f5ea4ac12
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+// Run through tcp_old_ack_ts.sh: packetdrill alone does not check TSecr.
+// An out-of-order segment advances SND.UNA. The gap filler still carries
+// its original, older ACK; accepting its data must refresh TS.Recent.
+--tcp_ts_tick_usecs=1000
+
+`./defaults.sh`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
++0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
++0 bind(3, ..., ...) = 0
++0 listen(3, 1) = 0
+0.100 < S 0:0(0) win 20000 <mss 1000,sackOK,TS val 900 ecr 0>
++0 > S. 0:0(0) ack 1 <mss 1460,sackOK,TS val 100 ecr 900>
+0.200 < . 1:1(0) ack 1 win 20000 <nop,nop,TS val 1000 ecr 100>
++0 accept(3, ..., ...) = 4
+0.300 write(4, ..., 17) = 17
++0 > P. 1:18(17) ack 1 <nop,nop,TS val 300 ecr 1000>
+0.310 < P. 18:35(17) ack 18 win 20000 <nop,nop,TS val 301001 ecr 300>
++0 > . 18:18(0) ack 1 <nop,nop,TS val 310 ecr 1000,nop,nop,sack 18:35>
+0.320 < P. 1:18(17) ack 1 win 20000 <nop,nop,TS val 301000 ecr 100>
++0 > . 18:18(0) ack 35 <nop,nop,TS val 320 ecr 301000>
++0 read(4, ..., 34) = 34
diff --git a/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.sh b/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.sh
new file mode 100755
index 000000000000..4c4534a81e78
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+cd "$(dirname "$(realpath "$0")")" || exit 1
+source ../../kselftest/ktap_helpers.sh
+
+if ! command -v packetdrill >/dev/null; then
+ ktap_skip_all "packetdrill not found in PATH"
+ exit "$KSFT_SKIP"
+fi
+
+log=$(mktemp) || exit "$KSFT_FAIL"
+trap 'rm -f "$log"' EXIT
+optargs=()
+if [[ -n "${KSFT_MACHINE_SLOW}" ]]; then
+ optargs+=(--tolerance_usecs=14000)
+fi
+
+ktap_print_header
+ktap_set_plan 3
+
+for family in ipv4 ipv6 ipv4-mapped-ipv6; do
+ mtu=1500
+ [[ "$family" == ipv6 ]] && mtu=1520
+ if ! unshare -n packetdrill --verbose --ip_version="$family" \
+ --mtu="$mtu" "${optargs[@]}" tcp_old_ack_ts.pkt >"$log" 2>&1; then
+ sed 's/^/# /' "$log"
+ ktap_test_fail "$family: packetdrill"
+ continue
+ fi
+
+ # Packetdrill checks packet structure and timing but ignores outbound
+ # TSecr. Require all four observed echoes, including the gap-filling ACK.
+ if awk '
+ /outbound sniffed packet:/ {
+ count++
+ if (!match($0, /ecr [0-9]+/)) {
+ bad = 1
+ next
+ }
+ echoes = echoes (count == 1 ? "" : ",") \
+ substr($0, RSTART + 4, RLENGTH - 4)
+ }
+ END {
+ if (bad || count != 4 || echoes != "900,1000,1000,301000") {
+ print "# unexpected timestamp echoes: " echoes
+ exit 1
+ }
+ }' "$log"; then
+ ktap_test_pass "$family"
+ else
+ sed 's/^/# /' "$log"
+ ktap_test_fail "$family: timestamp echo"
+ fi
+done
+
+ktap_finished
--
2.55.0
next prev parent reply other threads:[~2026-09-21 22:26 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 22:26 [PATCH net 0/2] tcp: correct timestamp echo for accepted old ACKs Jeff Jo
2026-09-21 22:26 ` [PATCH net 1/2] tcp: refresh TS.Recent " Jeff Jo
2026-09-22 0:22 ` Eric Dumazet
2026-09-21 22:26 ` Jeff Jo [this message]
2026-09-22 0:08 ` [PATCH net 2/2] selftests: net: check timestamp echo after an old ACK Eric Dumazet
2026-09-22 4:33 ` Jeff Jo
2026-09-22 8:09 ` Eric Dumazet
2026-09-22 17:01 ` Jeff Jo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260921222609.50824-6-jeffjo@openai.com \
--to=jeffjo@openai.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®