forwarded TCP packets larger than the outgoing MTU are dropped instead of segmented
- Dominant language
- Go
- Stars
- 19.3k
- Forks
- 2k
- Avg merge
- 3d 5h
- Merged PRs (30d)
- 264
Description
### Description
When netstack forwards a TCP packet larger than the outgoing route's MTU with DF set, it drops the packet and increments Forwarding.PacketTooBig, with no ICMP frag-needed. ipv6.go does
the same for all forwarded oversized packets.
This is not an edge case. A host that coalesces on receive (GRO/TSO) routinely hands the sandbox frames far larger than any MTU — I saw 2868, 5736, 7170, 11472 and 20076 bytes against a
1466-byte outgoing MTU. Every one is dropped, so the sender only advances via single-MSS RTO retransmits: a flat ~35 KB/s.
Linux forwards these. ip_forward() exempts GSO skbs whose per-segment length fits the MTU (skb_gso_validate_network_len()), and segments at transmit. netstack asks "is this buffer bigger
than the MTU" instead of "would the segments fit", and drops.
Affected code: the IsForwardedPacket branch in writePacketPostRouting (pkg/tcpip/network/ipv4/ipv4.go) and the equivalent in pkg/tcpip/network/ipv6/ipv6.go. Fragmenting is not allowed
here, but re-segmenting is.
Impact: any workload where the sentry forwards between an external interface and an internal bridge. Docker-in-gVisor is the common case.
### Steps to reproduce
1. Start a privileged container under runsc, and inside it run dockerd --iptables=false --ip6tables=false with the SNAT rules from images/basic/docker/start-dockerd.sh.
2. Serve a large blob from outside the sandbox: nc -l -p 8099 < /blob
3. From a container on the inner bridge: timeout 10 nc 8099 | wc -c
Result: ~365 KB in 10s. Fetching to the sandbox itself, and the outbound direction, both run at full speed — only the forwarded direction is affected.
### runsc version
```shell
runsc release-20260727.0-85-g28cbfc7aa188
```
### docker version (if using docker)
```shell
```
### uname
_No response_
### kubectl (if using Kubernetes)
```shell
```
### repo state (if built from source)
_No response_
### runsc debug logs (if available)
```shell
```
Contributor guide
Research direction
Start in the IsForwardedPacket branches of writePacketPostRouting in pkg/tcpip/network/ipv4/ipv4.go and pkg/tcpip/network/ipv6/ipv6.go, then compare the existing MTU checks with Linux's GSO forwarding behavior. The change is complete when forwarded oversized TCP frames whose per-segment length fits the outgoing MTU are re-segmented rather than dropped; preserve the existing no-fragmentation behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100