From 946dfec98a365029e20fbcde00e327c3ccebd186 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 19 Nov 2021 11:17:03 -0800 Subject: [PATCH] wgengine/router: fix checkIPRuleSupportsV6 to actually use IPv6 Updates #3358 (should fix it) Updates #391 Change-Id: Ia62437dfa81247b0b5994d554cf279c3d540e4e7 Signed-off-by: Brad Fitzpatrick --- wgengine/router/router_linux.go | 23 ++++++++++++++++++----- wgengine/router/router_linux_test.go | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/wgengine/router/router_linux.go b/wgengine/router/router_linux.go index 593975878..79d11b7c6 100644 --- a/wgengine/router/router_linux.go +++ b/wgengine/router/router_linux.go @@ -119,7 +119,7 @@ func newUserspaceRouter(logf logger.Logf, tunDev tun.Device, linkMon *monitor.Mo return nil, err } - v6err := checkIPv6() + v6err := checkIPv6(logf) if v6err != nil { logf("disabling tunneled IPv6 due to system IPv6 config: %v", v6err) } @@ -169,7 +169,7 @@ func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, linkMon *monit r.logf("error querying IP rules (does kernel have IP_MULTIPLE_TABLES?): %v", err) r.logf("warning: running without policy routing") } else { - r.logf("policy routing available; found %d rules", len(rules)) + r.logf("[v1] policy routing available; found %d rules", len(rules)) r.ipRuleAvailable = true } } @@ -1492,7 +1492,7 @@ func cleanup(logf logger.Logf, interfaceName string) { // missing. It does not check that IPv6 is currently functional or // that there's a global address, just that the system would support // IPv6 if it were on an IPv6 network. -func checkIPv6() error { +func checkIPv6(logf logger.Logf) error { _, err := os.Stat("/proc/sys/net/ipv6") if os.IsNotExist(err) { return err @@ -1524,7 +1524,7 @@ func checkIPv6() error { } } - if err := checkIPRuleSupportsV6(); err != nil { + if err := checkIPRuleSupportsV6(logf); err != nil { return fmt.Errorf("kernel doesn't support IPv6 policy routing: %w", err) } @@ -1552,11 +1552,24 @@ func supportsV6NAT() bool { return bytes.Contains(bs, []byte("nat\n")) } -func checkIPRuleSupportsV6() error { +func checkIPRuleSupportsV6(logf logger.Logf) error { + // First try just a read-only operation to ideally avoid + // having to modify any state. + if rules, err := netlink.RuleList(netlink.FAMILY_V6); err != nil { + return fmt.Errorf("querying IPv6 policy routing rules: %w", err) + } else { + if len(rules) > 0 { + logf("[v1] kernel supports IPv6 policy routing (found %d rules)", len(rules)) + return nil + } + } + + // Try to actually create & delete one as a test. rule := netlink.NewRule() rule.Priority = 1234 rule.Mark = tailscaleBypassMarkNum rule.Table = tailscaleRouteTable.num + rule.Family = netlink.FAMILY_V6 // First delete the rule unconditionally, and don't check for // errors. This is just cleaning up anything that might be already // there. diff --git a/wgengine/router/router_linux_test.go b/wgengine/router/router_linux_test.go index 186be209f..32dca7937 100644 --- a/wgengine/router/router_linux_test.go +++ b/wgengine/router/router_linux_test.go @@ -803,7 +803,7 @@ func TestDebugListRules(t *testing.T) { } func TestCheckIPRuleSupportsV6(t *testing.T) { - err := checkIPRuleSupportsV6() + err := checkIPRuleSupportsV6(t.Logf) if err != nil && os.Getuid() != 0 { t.Skipf("skipping, error when not root: %v", err) }