tailscale/tailscale · error
couldn't check system's UDP GRO forwarding configuration, fa
Error message
couldn't check system's UDP GRO forwarding configuration, failed to retrieve TUN device features: %v
What it means
The GRO check reads the offload feature bitmap of the Tailscale TUN interface via ethtool.Features and this call failed: the interface name is wrong or absent, the TUN lives in another network namespace, or the TUN driver does not implement ethtool feature reporting.
Source
Thrown at net/netkernelconf/netkernelconf_linux.go:38
// CheckUDPGROForwarding checks if the machine is optimally configured to
// forward UDP packets between the default route and Tailscale TUN interfaces.
// It returns a non-nil warn in the case that the configuration is suboptimal.
// It returns a non-nil err in the case that an error is encountered while
// performing the check.
func CheckUDPGROForwarding(tunInterface, defaultRouteInterface string) (warn, err error) {
const kbLink = "\nSee https://tailscale.com/s/ethtool-config-udp-gro"
errWithPrefix := func(format string, a ...any) error {
const errPrefix = "couldn't check system's UDP GRO forwarding configuration, "
return fmt.Errorf(errPrefix+format, a...)
}
e, err := ethtool.NewEthtool()
if err != nil {
return nil, errWithPrefix("failed to init ethtool: %v", err)
}
defer e.Close()
tunFeatures, err := e.Features(tunInterface)
if err != nil {
return nil, errWithPrefix("failed to retrieve TUN device features: %v", err)
}
if !tunFeatures[txFeature] {
// if txFeature is disabled/nonexistent on the TUN then UDP GRO
// forwarding doesn't matter, we won't be taking advantage of it.
return nil, nil
}
defaultFeatures, err := e.Features(defaultRouteInterface)
if err != nil {
return nil, errWithPrefix("failed to retrieve default route interface features: %v", err)
}
defaultHasRxWant, ok := defaultFeatures[rxWantFeature]
if !ok {
// unlikely the feature is nonexistent with txFeature in the TUN driver
// being added to the kernel later than rxWantFeature, but let's be sure
return nil, nil
}
if !defaultHasRxWant || defaultFeatures[rxDoNotWantFeature] {
return fmt.Errorf("UDP GRO forwarding is suboptimally configured on %s, UDP forwarding throughput capability will increase with a configuration change.%s", defaultRouteInterface, kbLink), nilView on GitHub (pinned to 6e0912f979)
Solutions
- Verify the interface exists first: ip link show <tunInterface>
- Run the check after the TUN is up, or pass the actual interface name
- Run in the same network namespace as the TUN device
Defensive patterns
Strategy: validation
Validate before calling
if _, err := net.InterfaceByName(tunInterface); err != nil {
return fmt.Errorf("TUN interface %q not present: %w", tunInterface, err)
}
warn, err := netkernelconf.CheckUDPGROForwarding(tunInterface, defaultRouteInterface) Try / catch
warn, err := netkernelconf.CheckUDPGROForwarding(tun, def)
if err != nil && strings.Contains(err.Error(), "failed to retrieve TUN device features") {
// wrong interface name or netns: verify with `ip link` and retry after TUN creation
log.Printf("TUN feature query failed; retry after tailscaled is up: %v", err)
} Prevention
- Call NIC checks only after the TUN interface is confirmed to exist
- Pass interface names obtained from link monitor events, not hardcoded guesses
- Run the check in the network namespace that owns the TUN device
When it happens
Trigger: Passing a TUN interface name that does not exist (check runs before tailscale0 is created), a non-TUN interface name, containerized setups where the TUN device belongs to a different netns, or TUN drivers without ethtool ops.
Common situations: Health checks racing with tailscaled startup, custom TUN names (--tun=...), Dockerized tailscaled with mismatched namespaces, swapped argument order.
Related errors
- failed to retrieve TUN device features: %w
- Not enabling UDP GRO forwarding as UDP segmentation is disab
- couldn't check system's UDP GRO forwarding configuration, fa
- couldn't check system's UDP GRO forwarding configuration, fa
- UDP GRO forwarding is suboptimally configured on %s, UDP for
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/7119cc7ed70a4cee.
Report an issue: GitHub.