client/tailscale,ipn/{ipnlocal,localapi}: check UDP GRO config (#10071)
Updates tailscale/corp#9990 Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
@@ -54,6 +54,7 @@ import (
|
||||
"tailscale.com/net/dnscache"
|
||||
"tailscale.com/net/dnsfallback"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/net/netkernelconf"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/netutil"
|
||||
@@ -4871,6 +4872,45 @@ func (b *LocalBackend) CheckIPForwarding() error {
|
||||
return warn
|
||||
}
|
||||
|
||||
// CheckUDPGROForwarding checks if the machine is optimally configured to
|
||||
// forward UDP packets between the default route and Tailscale TUN interfaces.
|
||||
// It returns an error if the check fails or if suboptimal configuration is
|
||||
// detected. No error is returned if we are unable to gather the interface
|
||||
// names from the relevant subsystems.
|
||||
func (b *LocalBackend) CheckUDPGROForwarding() error {
|
||||
if b.sys.IsNetstackRouter() {
|
||||
return nil
|
||||
}
|
||||
// We return nil when the interface name or subsystem it's tied to can't be
|
||||
// fetched. This is intentional as answering the question "are netdev
|
||||
// features optimal for performance?" is a low priority in that situation.
|
||||
tunSys, ok := b.sys.Tun.GetOK()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
tunInterface, err := tunSys.Name()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
netmonSys, ok := b.sys.NetMon.GetOK()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
state := netmonSys.InterfaceState()
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
// We return warn or err. If err is non-nil there was a problem
|
||||
// communicating with the kernel via ethtool semantics/ioctl. ethtool ioctl
|
||||
// errors are interesting for our future selves as we consider tweaking
|
||||
// netdev features automatically using similar API infra.
|
||||
warn, err := netkernelconf.CheckUDPGROForwarding(tunInterface, state.DefaultRouteInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return warn
|
||||
}
|
||||
|
||||
// DERPMap returns the current DERPMap in use, or nil if not connected.
|
||||
func (b *LocalBackend) DERPMap() *tailcfg.DERPMap {
|
||||
b.mu.Lock()
|
||||
|
||||
@@ -71,6 +71,7 @@ var handler = map[string]localAPIHandler{
|
||||
// without a trailing slash:
|
||||
"bugreport": (*Handler).serveBugReport,
|
||||
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
|
||||
"check-udp-gro-forwarding": (*Handler).serveCheckUDPGROForwarding,
|
||||
"check-prefs": (*Handler).serveCheckPrefs,
|
||||
"component-debug-logging": (*Handler).serveComponentDebugLogging,
|
||||
"debug": (*Handler).serveDebug,
|
||||
@@ -983,6 +984,23 @@ func (h *Handler) serveCheckIPForwarding(w http.ResponseWriter, r *http.Request)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) serveCheckUDPGROForwarding(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitRead {
|
||||
http.Error(w, "UDP GRO forwarding check access denied", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
var warning string
|
||||
if err := h.b.CheckUDPGROForwarding(); err != nil {
|
||||
warning = err.Error()
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(struct {
|
||||
Warning string
|
||||
}{
|
||||
Warning: warning,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) serveStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.PermitRead {
|
||||
http.Error(w, "status access denied", http.StatusForbidden)
|
||||
|
||||
Reference in New Issue
Block a user