From ad91d1aa2099d028a96a4e2ac216c30aefd22f2c Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 28 Aug 2022 14:31:34 -0700 Subject: [PATCH] wgengine/router: allow disco packets through iptables on Linux Fixes #3824 Change-Id: I9d0a86bae7f6ca3b4588a0b621dc434b1611f414 Signed-off-by: Brad Fitzpatrick --- wgengine/router/router_linux.go | 33 ++++++++++++++++++++++++++++ wgengine/router/router_linux_test.go | 16 +++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/wgengine/router/router_linux.go b/wgengine/router/router_linux.go index 3a95c00ca..a4c9782c9 100644 --- a/wgengine/router/router_linux.go +++ b/wgengine/router/router_linux.go @@ -1112,6 +1112,30 @@ func (r *linuxRouter) addNetfilterBase() error { return nil } +// allowDiscoRule is the iptables rule to allow all UDP discovery packets in. +// See https://github.com/tailscale/tailscale/issues/3824. +var allowDiscoRule = []string{ + "-p", "udp", // UDP + "!", "-f", // not fragmented parts of UDP packets + "-m", "u32", "--u32", + "0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac", // check first 6 bytes of UDP payload is disco packet magic + "-m", "comment", "--comment", "Allow Tailscale NAT traversal", + "-j", "ACCEPT", +} + +// allowDiscoRule6 is allowDiscoRule without the fragment check that doesn't +// apply to IPv6, and with the right offsets for the IPv6 UDP payload. +// +// Note: this doesn't work if IPv6 extension headers are present. Oh well. +// It'll help most users. +var allowDiscoRule6 = []string{ + "-p", "udp", // UDP + "-m", "u32", "--u32", + "0x30=0x5453f09f && 0x32&0xffff=0x92ac", // check first 6 bytes of UDP payload is disco packet magic + "-m", "comment", "--comment", "Allow Tailscale NAT traversal", + "-j", "ACCEPT", +} + // addNetfilterBase4 adds some basic IPv4 processing rules to be // supplemented by later calls to other helpers. func (r *linuxRouter) addNetfilterBase4() error { @@ -1129,6 +1153,10 @@ func (r *linuxRouter) addNetfilterBase4() error { if err := r.ipt4.Append("filter", "ts-input", args...); err != nil { return fmt.Errorf("adding %v in v4/filter/ts-input: %w", args, err) } + if err := r.ipt4.Append("filter", "ts-input", allowDiscoRule...); err != nil { + // Not worth returning an error about. (Maybe they lack u32 kernel match support.) + r.logf("ignoring error adding allow-disco in v4/filter/ts-input: %v", err) + } // Forward all traffic from the Tailscale interface, and drop // traffic to the tailscale interface by default. We use packet @@ -1167,6 +1195,11 @@ func (r *linuxRouter) addNetfilterBase6() error { // TODO: only allow traffic from Tailscale's ULA range to come // from tailscale0. + if err := r.ipt6.Append("filter", "ts-input", allowDiscoRule6...); err != nil { + // Not worth returning an error about. (Maybe they lack u32 kernel match support.) + r.logf("ignoring error adding allow-disco in v6/filter/ts-input: %v", err) + } + args := []string{"-i", r.tunname, "-j", "MARK", "--set-mark", tailscaleSubnetRouteMark} if err := r.ipt6.Append("filter", "ts-forward", args...); err != nil { return fmt.Errorf("adding %v in v6/filter/ts-forward: %w", args, err) diff --git a/wgengine/router/router_linux_test.go b/wgengine/router/router_linux_test.go index 01191315c..bfbddb899 100644 --- a/wgengine/router/router_linux_test.go +++ b/wgengine/router/router_linux_test.go @@ -108,6 +108,7 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v4/nat/POSTROUTING -j ts-postrouting v4/nat/ts-postrouting -m mark --mark 0x40000 -j MASQUERADE v6/filter/FORWARD -j ts-forward @@ -115,6 +116,7 @@ func TestRouterStates(t *testing.T) { v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/nat/POSTROUTING -j ts-postrouting v6/nat/ts-postrouting -m mark --mark 0x40000 -j MASQUERADE `, @@ -140,12 +142,14 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v4/nat/POSTROUTING -j ts-postrouting v6/filter/FORWARD -j ts-forward v6/filter/INPUT -j ts-input v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/nat/POSTROUTING -j ts-postrouting `, }, @@ -173,17 +177,19 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v4/nat/POSTROUTING -j ts-postrouting v6/filter/FORWARD -j ts-forward v6/filter/INPUT -j ts-input v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/nat/POSTROUTING -j ts-postrouting `, }, { - name: "addr and routes with netfilter", + name: "addr and routes with netfilter v2", in: &Config{ LocalAddrs: mustCIDRs("100.101.102.104/10"), Routes: mustCIDRs("100.100.100.100/32", "10.0.0.0/8"), @@ -203,12 +209,14 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v4/nat/POSTROUTING -j ts-postrouting v6/filter/FORWARD -j ts-forward v6/filter/INPUT -j ts-input v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/nat/POSTROUTING -j ts-postrouting `, }, @@ -232,9 +240,11 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT `, }, { @@ -258,12 +268,14 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v4/nat/POSTROUTING -j ts-postrouting v6/filter/FORWARD -j ts-forward v6/filter/INPUT -j ts-input v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/nat/POSTROUTING -j ts-postrouting `, }, @@ -290,12 +302,14 @@ func TestRouterStates(t *testing.T) { v4/filter/ts-input -i lo -s 100.101.102.104 -j ACCEPT v4/filter/ts-input ! -i tailscale0 -s 100.115.92.0/23 -j RETURN v4/filter/ts-input ! -i tailscale0 -s 100.64.0.0/10 -j DROP +v4/filter/ts-input -p udp ! -f -m u32 --u32 0>>22&0x3C@8=0x5453f09f && 0>>22&0x3C@10&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v4/nat/POSTROUTING -j ts-postrouting v6/filter/FORWARD -j ts-forward v6/filter/INPUT -j ts-input v6/filter/ts-forward -i tailscale0 -j MARK --set-mark 0x40000 v6/filter/ts-forward -m mark --mark 0x40000 -j ACCEPT v6/filter/ts-forward -o tailscale0 -j ACCEPT +v6/filter/ts-input -p udp -m u32 --u32 0x30=0x5453f09f && 0x32&0xffff=0x92ac -m comment --comment Allow Tailscale NAT traversal -j ACCEPT v6/nat/POSTROUTING -j ts-postrouting `, },