|
|
@@ -25,6 +25,7 @@ import (
|
|
|
"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))
|