google / google/gvisor

forwarded TCP packets larger than the outgoing MTU are dropped instead of segmented

Open
#14,011 2 comments 0 reactions 0 assignees View on GitHub
type: bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.