feat(wireguard): WIREGUARD_GSO option to disable TUN vnet hdr batching - #3424
Merged
Conversation
…tching
Since wireguard-go introduced vectorized TUN I/O, it unconditionally
opens the TUN device with IFF_VNET_HDR and uses GRO/GSO batched reads
and writes, with no way to opt out. Some kernels misbehave on this
path: notably vendor NAS kernels such as ASUSTOR ADM (Linux 6.6.x),
which advertise IFF_VNET_HDR support but reject GRO-coalesced writes
with EINVAL. When that happens, wireguard-go repeatedly logs
Failed to write packets to TUN device: write /dev/net/tun: invalid argument
under load and ends up retrying packets individually, wasting up to
half of the available bandwidth.
The same regression hit mainline LTS kernels (~6.6.44) for a window
and is documented with a kernel bisection in
tailscale/tailscale#13041 - but vendor
kernels can carry it indefinitely, and there is no way to detect the
breakage at TUN creation time since the kernel accepts the flags and
only fails on write.
Setting WIREGUARD_DISABLE_GSO=on (default off, no behavior change
unless opted in) creates the TUN device without IFF_VNET_HDR, so
wireguard-go detects no vnet hdr support and falls back to simple
single-packet reads and writes - the same behavior all versions had
before vectorized I/O. The AmneziaWG userspace path honors the same
option.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
qdm12
reviewed
Aug 4, 2026
qdm12
left a comment
Member
There was a problem hiding this comment.
Nice PR 💪
I appreciate the AI+Human effort 💯
and the fully/partly human written PR description too!
What a relief in this day and age 😸
- Flip WIREGUARD_DISABLE_GSO (default off) to WIREGUARD_GSO (default on), and DisableGSO to GSO throughout - Warn once, suggesting WIREGUARD_GSO=off, when wireguard-go emits the TUN write EINVAL error characteristic of kernels with broken IFF_VNET_HDR support - Wrap the tun.CreateTUNFromFile error - List WIREGUARD_GSO=on in the Dockerfile ENV defaults Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WIREGUARD_GSO option to disable TUN vnet hdr batching
Member
|
Sorry to be annoying on the x-compilation compatibility, can you fix: Thanks! |
Move the TUN creation to a Linux build-tagged createTUN function mirroring the wireguard package, since amneziatun.CreateTUNFromFile only exists on Linux. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Member
|
merged, thanks! 👍 |
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.
Description
Since wireguard-go introduced vectorized TUN I/O, it unconditionally opens the TUN device with
IFF_VNET_HDRand uses GRO/GSO batched reads and writes, with no way to opt out. Some kernels misbehave on this path: notably vendor NAS kernels such as ASUSTOR ADM (Linux "6.6.x" — the vendor scrubs the exact version string), which advertiseIFF_VNET_HDRsupport but reject GRO-coalesced writes withEINVAL. When that happens, wireguard-go repeatedly logsunder load and ends up retrying packets individually, wasting up to half of the available bandwidth. The breakage is undetectable at TUN creation time: the kernel accepts the flags and only fails on write.
This PR adds
WIREGUARD_DISABLE_GSO(defaultoff, no behavior change unless opted in). Whenon, the TUN device is created withoutIFF_VNET_HDR, so wireguard-go detects no vnet hdr support and falls back to simple single-packet reads and writes — the same behavior all versions had before vectorized I/O. The AmneziaWG userspace path honors the same option.Testing
WIREGUARD_DISABLE_GSO=onsustained ~82 Mbit/s (indicative only — VPN server assignment may differ between connections).EINVALlog spam that motivated this change (spring 2026) no longer reproduces on the current ADM kernel, suggesting ASUSTOR has since shipped the mainline fix — but vendor kernels can carry the regression indefinitely, and the same regression hit mainline LTS (~6.6.44) for a window: kernel bisection documented in Tailscale is slow:wg: Failed to write packets to TUN device: write /dev/net/tun: invalid argumenttailscale/tailscale#13041, which shows the identical symptom in Tailscale's embedded wireguard-go.go build ./...,golangci-lint run(v2.4.0, 0 issues) andgo test ./...pass; new table-test case coversDisableGSO=trueand the default-MTU fallback inbuildWireguardSettings.A note on how this was developed
This was developed and iterated with substantial help from Anthropic Claude (implementation, AGENTS.md conformance pass, and testing against a live ASUSTOR deployment), with all changes reviewed and validated by a human. Happy to walk through any part of the design.
Issue
No matching gluetun issue found (searched for the error string, GSO, vnet_hdr, ASUSTOR). Closest ecosystem reference: tailscale/tailscale#13041 — same wireguard-go error and slowdown, bisected to a mainline kernel regression in the
virtio_net_hdrGSO path.Assertions
WIREGUARD_DISABLE_GSOonce this is reviewed.🤖 Generated with Claude Code