2023-11-09 19:34:41 +00:00
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package netkernelconf
import (
"fmt"
"github.com/safchain/ethtool"
)
2024-06-10 18:19:03 +00:00
const (
rxWantFeature = "rx-udp-gro-forwarding"
rxDoNotWantFeature = "rx-gro-list"
txFeature = "tx-udp-segmentation"
)
2023-11-09 19:34:41 +00:00
// 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 nonexistant 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 ) , nil
}
return nil , nil
}
2024-06-10 18:19:03 +00:00
// SetUDPGROForwarding enables UDP GRO forwarding for the provided default
// interface. It validates if the provided tun interface has UDP segmentation
// enabled and, if not, returns an error. See
// https://tailscale.com/kb/1320/performance-best-practices#linux-optimizations-for-subnet-routers-and-exit-nodes
func SetUDPGROForwarding ( tunInterface , defaultInterface string ) error {
e , err := ethtool . NewEthtool ( )
if err != nil {
return fmt . Errorf ( "failed to init ethtool: %w" , err )
}
defer e . Close ( )
tunFeatures , err := e . Features ( tunInterface )
if err != nil {
return fmt . Errorf ( "failed to retrieve TUN device features: %w" , 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 fmt . Errorf ( "Not enabling UDP GRO forwarding as UDP segmentation is disabled for Tailscale interface" )
}
if err := e . Change ( defaultInterface , map [ string ] bool { rxWantFeature : true , rxDoNotWantFeature : false } ) ; err != nil {
return fmt . Errorf ( "error enabling UDP GRO forwarding: %w" , err )
}
return nil
}