[PATCH net-next v4 0/2] tcp: keep the route window metric when SO_RCVBUF is set before connect

From: Shashank Mohan Jain

Date: Wed Oct 21 2026 - 12:04:48 EST


Since commit b025461303d8 ("tcp: update window_clamp when SO_RCVBUF is
set"), SO_RCVBUF set before connect() or on a listener leaves a non-zero
window_clamp behind. The SYN, SYN-ACK and SYN cookie paths take that for
a TCP_WINDOW_CLAMP set by the user, so the route "window" metric
(RTAX_WINDOW) is skipped, and a real TCP_WINDOW_CLAMP set before
SO_RCVBUF is overwritten.

Patch 1 records the clamp the user set with TCP_WINDOW_CLAMP in a new
field and keys the SYN, SYN-ACK and SYN cookie paths off it. Patch 2
adds nine packetdrill tests (active open, passive open, SYN cookies over
IPv4 and IPv6, TCP_WINDOW_CLAMP reset, SO_RCVBUF raise on an established
socket). All nine fail on net-next without patch 1 and pass with it.

As Jakub asked, the commit message of patch 1 now says how the problem
was found and how it was tested.

Changes since v3, all from the AI review of v3 (each point was checked
against net-next before acting on it):

- SYN cookies: cookie_v4_check() and cookie_v6_check() still used
"window_clamp ?: RTAX_WINDOW". With SO_RCVBUF on the listener and a
route window metric, v3 sent a SYN-ACK with wscale 0 while the cookie
child used rcv_wscale 5 (reproduced on the wire with v3, and by the
new cookie tests). The SYN-ACK path and both cookie paths now use
one helper, tcp_req_window_clamp().

- A single bit could leave window_clamp too low: v3's tcp_set_rcvbuf()
returned early once TCP_WINDOW_CLAMP was set, so after
tcp_init_buffer_space() lowered window_clamp, a larger SO_RCVBUF
could not raise it again. v4 stores the requested clamp (u32
window_clamp_user) and lets SO_RCVBUF move window_clamp up to it.

- Lockless read of a bitfield: the listener side now reads a separate
u32 with READ_ONCE(), written with WRITE_ONCE(), instead of a bit in
a byte shared with nonagle and rate_app_limited.

- window_clamp_user is listed in net_cachelines/tcp_sock.rst. It sits
in the cold part of struct tcp_sock; pahole shows the size (2432
bytes on x86_64, with and without MPTCP) and the fast path groups
unchanged.

- Selftests: symbolic TCP_FASTOPEN_CONNECT and TCP_FASTOPEN_NO_COOKIE
(TCP_WINDOW_CLAMP stays numeric because packetdrill has no symbol for
it, and the comment no longer refers to a local build); new listener
tests for SO_RCVBUF alone and for TCP_WINDOW_CLAMP then SO_RCVBUF;
SYN cookie tests for IPv4 and IPv6 that check tcpi_rcv_wscale of the
child; a TCP_WINDOW_CLAMP=0 reset test; a test that SO_RCVBUF raises
window_clamp up to the user's clamp and not above it.

- Patch 1 changelog: how the bug was found and tested; the cookie paths
are named.

- Rebased on current net-next.

Link: https://lore.kernel.org/all/20261012132622.48550-1-jain.sm@xxxxxxxxx/ (v3)

Changes since v2:
- track a user TCP_WINDOW_CLAMP instead of checking the socket state,
so the TCP_SYN_SENT paths (TCP_FASTOPEN_CONNECT, sock_ops) are fixed
too; corrected the changelog; two more selftests.

Changes since v1 (single [PATCH net]):
- retargeted from net to net-next, added the packetdrill selftest, and
rewrote the changelog.

Shashank Mohan Jain (2):
tcp: keep the route window metric when SO_RCVBUF is set before connect
selftests/net/packetdrill: cover SO_RCVBUF vs the route window clamp

.../networking/net_cachelines/tcp_sock.rst | 1 +
include/linux/tcp.h | 1 +
include/net/tcp.h | 14 ++++++++++
net/ipv4/syncookies.c | 3 +--
net/ipv4/tcp.c | 14 +++++++++-
net/ipv4/tcp_minisocks.c | 4 +--
net/ipv4/tcp_output.c | 2 +-
net/ipv6/syncookies.c | 2 +-
.../tcp_fastopen_connect_rcvbuf_window.pkt | 21 +++++++++++++++
.../tcp_rcvbuf_before_connect_window.pkt | 27 +++++++++++++++++++
.../tcp_rcvbuf_listener_window.pkt | 20 ++++++++++++++
.../tcp_syncookies_rcvbuf_window_ip4.pkt | 22 +++++++++++++++
.../tcp_syncookies_rcvbuf_window_ip6.pkt | 22 +++++++++++++++
.../tcp_window_clamp_rcvbuf_raise.pkt | 27 +++++++++++++++++++
.../tcp_window_clamp_reset_rcvbuf.pkt | 22 +++++++++++++++
.../tcp_window_clamp_vs_rcvbuf.pkt | 23 ++++++++++++++++
.../tcp_window_clamp_vs_rcvbuf_listener.pkt | 25 +++++++++++++++++
17 files changed, 242 insertions(+), 8 deletions(-)
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_fastopen_connect_rcvbuf_window.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rcvbuf_before_connect_window.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rcvbuf_listener_window.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_syncookies_rcvbuf_window_ip4.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_syncookies_rcvbuf_window_ip6.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_window_clamp_rcvbuf_raise.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_window_clamp_reset_rcvbuf.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_window_clamp_vs_rcvbuf.pkt
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_window_clamp_vs_rcvbuf_listener.pkt


base-commit: 279d5c8afcd38bd841832a39bfe1aa4f8518d98e
--
2.43.0