From 2d2b62c4000d7c62845ad32cfc90d5f5f0cfcf76 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 29 May 2024 20:46:33 -0700 Subject: [PATCH] wgengine/router: probe generally-unused "ip" command style lazily This busybox fwmaskWorks check was added before we moved away from using the "ip" command to using netlink directly. So it's now just wasted work (and log spam on Gokrazy) to check the "ip" command capabilities if we're never going to use it. Do it lazily instead. Updates #12277 Change-Id: I8ab9acf64f9c0d8240ce068cb9ec8c0f6b1ecee7 Signed-off-by: Brad Fitzpatrick --- wgengine/router/router_linux.go | 48 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/wgengine/router/router_linux.go b/wgengine/router/router_linux.go index 41c5b128c..a328075d9 100644 --- a/wgengine/router/router_linux.go +++ b/wgengine/router/router_linux.go @@ -25,6 +25,7 @@ "tailscale.com/health" "tailscale.com/net/netmon" "tailscale.com/types/logger" + "tailscale.com/types/opt" "tailscale.com/types/preftype" "tailscale.com/util/linuxfw" "tailscale.com/util/multierr" @@ -58,9 +59,9 @@ type linuxRouter struct { ipRuleFixLimiter *rate.Limiter // Various feature checks for the network stack. - ipRuleAvailable bool // whether kernel was built with IP_MULTIPLE_TABLES - v6Available bool // whether the kernel supports IPv6 - fwmaskWorks bool // whether we can use 'ip rule...fwmark /' + ipRuleAvailable bool // whether kernel was built with IP_MULTIPLE_TABLES + v6Available bool // whether the kernel supports IPv6 + fwmaskWorksLazy opt.Bool // whether we can use 'ip rule...fwmark /'; set lazily // ipPolicyPrefBase is the base priority at which ip rules are installed. ipPolicyPrefBase int @@ -110,20 +111,6 @@ func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, netMon *netmon } } - // To be a good denizen of the 4-byte 'fwmark' bitspace on every packet, we try to - // only use the third byte. However, support for masking to part of the fwmark bitspace - // was only added to busybox in 1.33.0. As such, we want to detect older versions and - // not issue such a stanza. - var err error - if r.fwmaskWorks, err = ipCmdSupportsFwmask(); err != nil { - r.logf("failed to determine ip command fwmask support: %v", err) - } - if r.fwmaskWorks { - r.logf("[v1] ip command supports fwmark masks") - } else { - r.logf("[v1] ip command does NOT support fwmark masks") - } - // A common installation of OpenWRT involves use of the 'mwan3' package. // This package installs ip-tables rules like: // -A mwan3_fallback_policy -m mark --mark 0x0/0x3f00 -j MARK --set-xmark 0x100/0x3f00 @@ -260,6 +247,31 @@ func (r *linuxRouter) useIPCommand() bool { return !ok } +// fwmaskWorks reports whether we can use 'ip rule...fwmark /'. +// This is computed lazily on first use. By default, we don't run the "ip" +// command, so never actually runs this. But the "ip" command is used in tests +// and can be forced. (see useIPCommand) +func (r *linuxRouter) fwmaskWorks() bool { + if v, ok := r.fwmaskWorksLazy.Get(); ok { + return v + } + // To be a good denizen of the 4-byte 'fwmark' bitspace on every packet, we try to + // only use the third byte. However, support for masking to part of the fwmark bitspace + // was only added to busybox in 1.33.0. As such, we want to detect older versions and + // not issue such a stanza. + v, err := ipCmdSupportsFwmask() + if err != nil { + r.logf("failed to determine ip command fwmask support: %v", err) + } + r.fwmaskWorksLazy.Set(v) + if v { + r.logf("[v1] ip command supports fwmark masks") + } else { + r.logf("[v1] ip command does NOT support fwmark masks") + } + return v +} + // onIPRuleDeleted is the callback from the network monitor for when an IP // policy rule is deleted. See Issue 1591. // @@ -1266,7 +1278,7 @@ func (r *linuxRouter) addIPRulesWithIPCommand() error { "pref", strconv.Itoa(rule.Priority + r.ipPolicyPrefBase), } if rule.Mark != 0 { - if r.fwmaskWorks { + if r.fwmaskWorks() { args = append(args, "fwmark", fmt.Sprintf("0x%x/%s", rule.Mark, linuxfw.TailscaleFwmarkMask)) } else { args = append(args, "fwmark", fmt.Sprintf("0x%x", rule.Mark))