MSS clamping is applied to every TCP SYN in the namespace, including traffic that never enters the tunnel
- Dominant language
- Go
- Stars
- 334
- Forks
- 33
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 7
Description
We run Pangolin with gerbil as the edge for about a hundred public HTTPS hostnames. Traefik shares gerbil's network namespace (`network_mode: service:gerbil`), so most of that traffic arrives from the internet on :443 and is proxied to a LAN backend, never going near WireGuard. I did not expect those connections to negotiate MSS 1228 when the path MTU is 1500 the whole way.
Setup: gerbil 1.4.2 on Docker/Debian, Traefik in the same netns, MTU 1500 on every hop between client and container. I read 1.5.1 and the code is the same there, but I have not deployed it.
### What happens
Every TCP socket in the namespace is clamped, not just tunnel traffic. On a plain public HTTPS connection:
```
$ ss -tin dst
ESTAB 0 0 :443 :
mss:1228 pmtu:1500 advmss:1448 ...
```
and inside the container:
```
$ iptables-save -t mangle | grep TCPMSS
-A INPUT -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1240
-A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1240
-A OUTPUT -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1240
```
I expected the clamp to cover traffic entering or leaving the WireGuard interface, not every SYN the namespace sees. The practical effect is about 18% more packets per byte served (1448/1228) on connections with no tunnel in their path.
### What I ruled out
Nothing in our compose or config sets an MSS. The rules come from gerbil itself — `ensureMSSClamping()` in `main.go` iterates `chains := []string{"INPUT", "OUTPUT", "FORWARD"}` and adds each rule with no `-i`/`-o` match, using `mssValue := mtuInt - 40` derived from the WireGuard MTU.
To be straight about impact: I found this while chasing a throughput shortfall and it turned out not to be the cause. I narrowed the rules to the wg interface for ten minutes and put them back, and measured no difference either way. So this is a correctness report, not a performance one, and low priority is fair.
Is the namespace-wide scope deliberate — some deployments routing traffic through wg that an interface match would miss — or would you expect `-o wg0` style scoping here? Happy to send a patch if it is the latter.
Measurements and the code reading above were done with an agent I have wired up to run tests for me; the numbers are from our own hosts.
Contributor guide
Research direction
Read ensureMSSClamping() in main.go and trace how the WireGuard MTU produces the TCPMSS rules for INPUT, OUTPUT, and FORWARD. Compare the resulting rules and negotiated MSS for tunnel traffic versus public HTTPS traffic; done means the intended tunnel traffic remains clamped without unnecessarily changing connections that never enter the tunnel.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100