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 <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2024-05-29 20:46:33 -07:00 committed by Brad Fitzpatrick
parent 909a292a8d
commit 2d2b62c400

View File

@ -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 <mark>/<mask>'
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 <mark>/<mask>'; 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 <mark>/<mask>'.
// 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))