SagerNet / SagerNet/sing-tun

Linux TUN GRO probe runs before link is up and returns EIO on OpenWrt

Open
#81 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
237
Forks
228
PR merge metrics
No merged PRs in 30d

Description

Summary

On OpenWrt 25.12.5 (mediatek/filogic, aarch64_cortex-a53), the Linux TUN GRO capability probe fails with EIO, causing sing-tun to disable both TCP and UDP GRO:

WARN inbound/tun[tun-in]: disabled TUN TCP & UDP GRO due to GRO probe error: batch write: write tun: input/output error

Normal connectivity still works, but sing-tun falls back without TUN TCP/UDP GRO, which may increase CPU usage or reduce throughput under load.

Environment

DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='25.12.5'
DISTRIB_REVISION='r33051-f5dae5ece4'
DISTRIB_TARGET='mediatek/filogic'
DISTRIB_ARCH='aarch64_cortex-a53'
DISTRIB_DESCRIPTION='OpenWrt 25.12.5 r33051-f5dae5ece4'
DISTRIB_TAINTS=''
sing-box version 1.14.0-alpha.43

Environment: go1.25.11 linux/arm64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_naive_outbound,with_usbip,badlinkname,tfogo_checklinkname0,with_musl
Revision: f025e6f163a2f438c8dad2402fb723c255521ce5
CGO: enabled

The warning was observed with a TUN inbound and a Tailscale endpoint. No explicit stack value was configured.

TUN features reported by the kernel

ethtool -k tun0 reports the relevant offload features as enabled:

tx-checksumming: on
  tx-checksum-ip-generic: on
scatter-gather: on
tcp-segmentation-offload: on
  tx-tcp-segmentation: on
  tx-tcp6-segmentation: on
generic-segmentation-offload: on
generic-receive-offload: on
tx-udp-segmentation: on
rx-gro-list: on

This suggests that the kernel advertises support for the TUN offload path, but the actual probe write fails.

Suspected cause

The current Linux startup order appears to run the GRO probe before bringing the TUN link up:

  1. NativeTun.Start() calls probeTCPGRO() when vnetHdr is enabled.
  2. Only afterwards does it call t.start().
  3. t.start() is where netlink.LinkSetUp(tunLink) is performed.

Relevant code:

Tailscale also disables GRO only when the probe returns EINVAL, while sing-tun currently disables it for any error, including EIO.

On this OpenWrt system, writing the probe packet while the link is still down may be the reason for:

write tun: input/output error

This is a hypothesis based on the startup order and kernel feature output; it has not yet been confirmed with a patched build.

Expected behavior

If the kernel and TUN device support the required offloads, the probe should pass and TCP/UDP GRO should remain enabled.

If the probe is performed before the interface is ready, a transient readiness error should not necessarily be treated as proof that GRO is unsupported.

Possible fix

Run probeTCPGRO() only after the TUN interface has been brought up, similar to Tailscale's documented requirement.

Alternatively or additionally, consider distinguishing unsupported-feature errors such as EINVAL from transient or ordering-related errors such as EIO before permanently disabling TCP and UDP GRO for the process lifetime.

I can test a patch that moves the probe after LinkSetUp if helpful.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read tun_linux.go, starting with NativeTun.Start(), probeTCPGRO(), and t.start() where netlink.LinkSetUp(tunLink) is called. Compare this order with the linked Tailscale implementation and investigate whether probing before the link is up explains EIO on the reported OpenWrt environment. Done means the probe behavior is confirmed and GRO is not disabled incorrectly when the device supports it.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, linux
Domain
networking, operating-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.