Skip to content

tcp: register tcp.rcv_wnd_max and tcp.snd_mss_max as runtime-tunable sysctls - #46

Merged
ccie18643 merged 1 commit into
ccie18643:PyTCP_3_0_10from
doronz88:feature/tcp-throughput-sysctls
Sep 6, 2026
Merged

tcp: register tcp.rcv_wnd_max and tcp.snd_mss_max as runtime-tunable sysctls#46
ccie18643 merged 1 commit into
ccie18643:PyTCP_3_0_10from
doronz88:feature/tcp-throughput-sysctls

Conversation

@doronz88

Copy link
Copy Markdown

Two TCP policy values were effectively hard-coded, forcing applications that embed PyTCP to monkeypatch internals to tune throughput on fast or asymmetric paths:

  • The advertised receive-window ceiling was fixed at 65535 bytes (WindowState.rcv_wnd_max). A bulk inbound transfer is bound by window / RTT, so on a high bandwidth-delay-product path (fast link, tunnel) 64 KiB throttles the peer far below the link rate even though PyTCP already negotiates RFC 7323 window scaling.

  • The send-side MSS always tracked the egress interface MTU, with no way to emit smaller segments while still advertising a large receive MSS. Overlay/tunnel deployments whose host->peer path MTU is below the local interface MTU need exactly that asymmetry (and classical PMTUD cannot discover the smaller hop when it sits past a relay that drops ICMP PTB).

Expose both as registered sysctls, consistent with the existing 'tcp.base_mss' / 'tcp.mtu_probing' knobs:

  • 'tcp.rcv_wnd_max' (flat; Linux net.ipv4.tcp_rmem parity) — default 65535, seeded into WindowState.rcv_wnd_max at session creation, so behaviour is unchanged until an operator raises it.

  • 'tcp.snd_mss_max' (per-interface, like 'tcp.base_mss') — default 0 (uncapped); a non-zero value clamps _mss_ceiling() last, bounding the segments we EMIT without lowering the advertised receive MSS. Floor 88 (Linux TCP_MIN_MSS); 0 reserved for "off".

Both ride the 'sysctls={...}' bag in stack.init(); neither warrants an explicit kwarg yet.

Tests at tests/integration/protocols/tcp/test__tcp__sysctls.py pin registration, defaults, validator rejection (rcv_wnd_max != 0; snd_mss_max 0-or->=88) and the per-interface storage semantics; test__tcp__session__throughput_knobs.py pins the session behaviour (rcv_wnd_max seeds the window; snd_mss_max caps _mss_ceiling while leaving rcv_mss at the interface ceiling).

Reference: Linux net.ipv4.tcp_rmem (receive-window max).
Reference: Linux include/net/tcp.h TCP_MIN_MSS=88.

@doronz88
doronz88 force-pushed the feature/tcp-throughput-sysctls branch from 815fd47 to 85b2aec Compare June 20, 2026 16:20
The send-side MSS always tracked the egress interface MTU, with no way
to emit smaller segments while still advertising a large receive MSS.
Overlay/tunnel deployments whose host->peer path MTU is below the local
interface MTU need exactly that asymmetry (and classical PMTUD cannot
discover the smaller hop when it sits past a relay that drops ICMP PTB).

Expose it as a registered sysctl, consistent with the existing
'tcp.base_mss' / 'tcp.mtu_probing' knobs:

  * 'tcp.snd_mss_max' (per-interface, like 'tcp.base_mss') — default 0
    (uncapped); a non-zero value clamps '_mss_ceiling()' last, bounding
    the segments we EMIT without lowering the advertised receive MSS.
    Floor 88 (Linux 'TCP_MIN_MSS'); 0 reserved for "off".

It rides the 'sysctls={...}' bag in 'stack.init()'; it does not warrant
an explicit kwarg yet.

Tests at tests/integration/protocols/tcp/test__tcp__sysctls.py pin
registration, the default, validator rejection (0-or->=88) and the
per-interface storage semantics; test__tcp__session__throughput_knobs.py
pins the session behaviour (the cap bounds '_mss_ceiling' while leaving
'rcv_mss' at the interface ceiling).

The originally proposed companion knob 'tcp.rcv_wnd_max' is dropped as
superseded: 3.0.8 shipped the same capability by the Linux-faithful
route — 'tcp.rmem' (min/default/max) plus SO_RCVBUF seeding
'WindowState.rcv_wnd_max', with receive-buffer Dynamic Right-Sizing
growing it and the WSCALE shift sized for 'tcp.rmem.max' at SYN.

Reference: Linux include/net/tcp.h TCP_MIN_MSS=88.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzQ7Jyy9os1WhyPuFJezJU
@ccie18643
ccie18643 changed the base branch from master to PyTCP_3_0_10 September 6, 2026 13:28

@ccie18643 ccie18643 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, and apologies for the slow review.

Landing the tcp.snd_mss_max half. The motivating case is real and there is
no other way to express it today: _mss_ceiling() tracked the egress
interface MTU with no way to bound emitted segments independently of the
advertised receive MSS, and classical PMTUD cannot discover a smaller hop
sitting behind a relay that drops ICMP PTB.

Dropping the tcp.rcv_wnd_max half as superseded. 3.0.8 shipped the same
capability by the Linux-faithful route: tcp.rmem (min/default/max) plus
SO_RCVBUF seeding WindowState.rcv_wnd_max, receive-buffer Dynamic
Right-Sizing growing it, and the WSCALE shift sized from tcp.rmem.max at
SYN. Keeping the static 65535 seed would have regressed that.

Two mechanical fixes on rebase: the PR was opened against master, which is
still the 2.7.10 line, so I retargeted it to the active PyTCP_3_0_10 dev
branch; and pytcp.socket.tcp__socket moved to
pytcp.runtime.socket.tcp__socket in the directory restructure.

Your authorship is preserved on the commit. make lint clean, 13,862 tests
green (+7 from your new cases).

@ccie18643
ccie18643 force-pushed the feature/tcp-throughput-sysctls branch from 85b2aec to 1e7b9c9 Compare September 6, 2026 13:28
@ccie18643
ccie18643 merged commit 889cc7f into ccie18643:PyTCP_3_0_10 Sep 6, 2026
1 of 2 checks passed
ccie18643 added a commit that referenced this pull request Sep 6, 2026
…audit

The knob landed with PR #46 (commit 1e7b9c9) and is applied inside
'_mss_ceiling()', the helper this record already documents as the
PLPMTUD cold-start seed. The adherence record is updated in lockstep so
the operator-facing surface of that helper stays fully described: the
cap clamps the emitted MSS last, independently of the advertised receive
MSS, and is inert at its 0 default so no RFC 4821 behaviour changes.

Reference: RFC 4821 §7.2 (per-protocol operator override).
Reference: Linux include/net/tcp.h TCP_MIN_MSS=88.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzQ7Jyy9os1WhyPuFJezJU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants