wgengine/router: probe better for v6 policy routing support.

Previously we disabled v6 support if the disable_policy knob was
missing in /proc, but some kernels support policy routing without
exposing the toggle. So instead, treat disable_policy absence as a
"maybe", and make the direct `ip -6 rule` probing a bit more
elaborate to compensate.

Fixes #1241.

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/1253/head
David Anderson 2021-02-01 14:32:50 -08:00 committed by Dave Anderson
parent 717c715c96
commit 267531e4f8
1 changed files with 28 additions and 21 deletions

View File

@ -1045,18 +1045,22 @@ func checkIPv6() error {
return errors.New("disable_ipv6 is set") return errors.New("disable_ipv6 is set")
} }
// Older kernels don't support IPv6 policy routing. // Older kernels don't support IPv6 policy routing. Some kernels
// support policy routing but don't have this knob, so absence of
// the knob is not fatal.
bs, err = ioutil.ReadFile("/proc/sys/net/ipv6/conf/all/disable_policy") bs, err = ioutil.ReadFile("/proc/sys/net/ipv6/conf/all/disable_policy")
if err != nil { if err == nil {
// Absent knob means policy routing is unsupported. disabled, err = strconv.ParseBool(strings.TrimSpace(string(bs)))
return err if err != nil {
return errors.New("disable_policy has invalid bool")
}
if disabled {
return errors.New("disable_policy is set")
}
} }
disabled, err = strconv.ParseBool(strings.TrimSpace(string(bs)))
if err != nil { if err := checkIPRuleSupportsV6(); err != nil {
return errors.New("disable_policy has invalid bool") return fmt.Errorf("kernel doesn't support IPv6 policy routing: %w", err)
}
if disabled {
return errors.New("disable_policy is set")
} }
// Some distros ship ip6tables separately from iptables. // Some distros ship ip6tables separately from iptables.
@ -1064,10 +1068,6 @@ func checkIPv6() error {
return err return err
} }
if err := checkIPRuleSupportsV6(); err != nil {
return err
}
return nil return nil
} }
@ -1088,13 +1088,17 @@ func supportsV6NAT() bool {
} }
func checkIPRuleSupportsV6() error { func checkIPRuleSupportsV6() error {
// First add a rule for "ip rule del" to delete. add := []string{"-6", "rule", "add", "pref", "1234", "fwmark", tailscaleBypassMark, "table", tailscaleRouteTable}
// We ignore the "add" operation's error because it can also del := []string{"-6", "rule", "del", "pref", "1234", "fwmark", tailscaleBypassMark, "table", tailscaleRouteTable}
// fail if the rule already exists.
exec.Command("ip", "-6", "rule", "add", // First delete the rule unconditionally, and don't check for
"pref", "123", "fwmark", tailscaleBypassMark, "table", fmt.Sprint(tailscaleRouteTable)).Run() // errors. This is just cleaning up anything that might be already
out, err := exec.Command("ip", "-6", "rule", "del", // there.
"pref", "123", "fwmark", tailscaleBypassMark, "table", fmt.Sprint(tailscaleRouteTable)).CombinedOutput() exec.Command("ip", del...).Run()
// Try adding the rule. This will fail on systems that support
// IPv6, but not IPv6 policy routing.
out, err := exec.Command("ip", add...).CombinedOutput()
if err != nil { if err != nil {
out = bytes.TrimSpace(out) out = bytes.TrimSpace(out)
var detail interface{} = out var detail interface{} = out
@ -1103,5 +1107,8 @@ func checkIPRuleSupportsV6() error {
} }
return fmt.Errorf("ip -6 rule failed: %s", detail) return fmt.Errorf("ip -6 rule failed: %s", detail)
} }
// Delete again.
exec.Command("ip", del...).Run()
return nil return nil
} }