Add optional TCP MSS clamp for tethering PMTUD failures (#478) - #603
Merged
Conversation
The native tunnel advertises an oversized tun MTU (get_mtu() == 10000),
so the MSS derived for the SYN/SYN-ACK it injects is ~9960. Combined with
handle_icmp() dropping every non-echo ICMP - including ICMPv4 type 3 code 4
("fragmentation needed") and ICMPv6 type 2 ("packet too big") - a peer that
is not re-originated over a real kernel socket (notably a tethered client)
never learns to shrink its segments, so large flows black-hole while small
ones and the phone's own apps work.
Clamp the advertised/used MSS to a conservative TCP_MSS_CLAMP (1360 bytes)
so every segment on the tun path fits typical constrained upstream paths
(cellular / WireGuard) without relying on PMTUD ICMP:
- netguard.h: add TCP_MSS_CLAMP with rationale.
- tcp.c (handle_tcp): clamp the effective MSS stored in the session (used to
size downstream segmentation into the tun) to min(mss, 1360), covering both
the oversized-tun default and any peer-advertised value.
- tcp.c (write_tcp): clamp the MSS advertised in the injected SYN/SYN-ACK, and
write it with htons(). The option value is read back with ntohs() in
handle_tcp, so it must be network byte order; the previous host-order write
was a latent byte-order bug that would also have defeated the clamp.
The global tun MTU (get_mtu/setMtu) is deliberately left unchanged. Passing
PMTUD ICMP through (translating type 3 code 4 / type 2 back into the tun) is a
possible follow-up but is out of scope here. MSS clamping is a heuristic; 1360
is chosen conservatively and needs on-device tethering validation.
Native lib build-checked: :app:externalNativeBuildGithubDebug BUILD SUCCESSFUL.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kasnder
marked this pull request as ready for review
July 11, 2026 01:33
This was referenced Jul 11, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Provides an opt-in workaround for the tethering stalls reported in #478.
Problem
TrackerControl's TUN uses an MTU of 10000, and the native TCP proxy can therefore use an MSS near 9960. When oversized packets are written back through the TUN toward a tethered client, the smaller downstream path may return ICMP "fragmentation needed" / "packet too big" messages. The native ICMP handler does not currently process those PMTUD messages.
A fixed global clamp would reduce TUN aggregation for every TCP connection, increasing native reads, allocations, checksums, and writes even for users who do not encounter the tethering problem.
Change
Adds an advanced, default-off Tethering compatibility mode setting.
When enabled:
The preference is read once when the VPN starts and stored in the native context. Native code checks it only during TCP SYN/session setup; there is no preference lookup or additional branch for established data packets.
The existing MSS option write is also corrected to use network byte order with
htons(). That correctness fix applies regardless of whether compatibility mode is enabled.The global TUN MTU remains unchanged.
Tradeoffs
1360 is a conservative heuristic, not a complete PMTUD implementation. Compatibility mode may reduce throughput and increase CPU/battery use because it produces more native socket reads and TUN writes. Keeping it opt-in limits that cost to affected users and enables direct A/B testing.
Validation
:app:externalNativeBuildGithubDebug -x :app:wgbridgeBuild: BUILD SUCCESSFUL for arm64-v8a, armeabi-v7a, x86, and x86_64.git diff --check: passed.:app:assembleGithubDebugreached and compiled the changed native code, but the repository currently fails resource linking on unrelated Ukrainian blocking-mode string references.Required device testing