diff --git a/net/interfaces/interfaces.go b/net/interfaces/interfaces.go index 3fcecfe40..17b1db0c2 100644 --- a/net/interfaces/interfaces.go +++ b/net/interfaces/interfaces.go @@ -12,6 +12,7 @@ "strings" "inet.af/netaddr" + "tailscale.com/net/tsaddr" ) // Tailscale returns the current machine's Tailscale interface, if any. @@ -52,7 +53,7 @@ func maybeTailscaleInterfaceName(s string) bool { // Tailscale virtual network interfaces. func IsTailscaleIP(ip net.IP) bool { nip, _ := netaddr.FromStdIP(ip) // TODO: push this up to caller, change func signature - return cgNAT.Contains(nip) + return tsaddr.IsTailscaleIP(nip) } func isUp(nif *net.Interface) bool { return nif.Flags&net.FlagUp != 0 } @@ -95,7 +96,7 @@ func LocalAddresses() (regular, loopback []string, err error) { // very well be something we can route to // directly, because both nodes are // behind the same CGNAT router. - if cgNAT.Contains(ip) { + if tsaddr.IsTailscaleIP(ip) { continue } if linkLocalIPv4.Contains(ip) { @@ -283,7 +284,6 @@ func mustCIDR(s string) netaddr.IPPrefix { private2 = mustCIDR("172.16.0.0/12") private3 = mustCIDR("192.168.0.0/16") privatev4s = []netaddr.IPPrefix{private1, private2, private3} - cgNAT = mustCIDR("100.64.0.0/10") linkLocalIPv4 = mustCIDR("169.254.0.0/16") v6Global1 = mustCIDR("2000::/3") ) diff --git a/net/tsaddr/tsaddr.go b/net/tsaddr/tsaddr.go new file mode 100644 index 000000000..3b929e8fe --- /dev/null +++ b/net/tsaddr/tsaddr.go @@ -0,0 +1,52 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tsaddr handles Tailscale-specific IPs and ranges. +package tsaddr + +import ( + "sync" + + "inet.af/netaddr" +) + +// ChromeOSVMRange returns the subset of the CGNAT IPv4 range used by +// ChromeOS to interconnect the host OS to containers and VMs. We +// avoid allocating Tailscale IPs from it, to avoid conflicts. +func ChromeOSVMRange() netaddr.IPPrefix { + chromeOSRange.Do(func() { mustPrefix(&chromeOSRange.v, "100.115.92.0/23") }) + return chromeOSRange.v +} + +var chromeOSRange oncePrefix + +// CGNATRange returns the Carrier Grade NAT address range that +// is the superset range that Tailscale assigns out of. +// See https://tailscale.com/kb/1015/100.x-addresses. +// Note that Tailscale does not assign out of the ChromeOSVMRange. +func CGNATRange() netaddr.IPPrefix { + cgnatRange.Do(func() { mustPrefix(&cgnatRange.v, "100.64.0.0/10") }) + return cgnatRange.v +} + +var cgnatRange oncePrefix + +// IsTailscaleIP reports whether ip is an IP address in a range that +// Tailscale assigns from. +func IsTailscaleIP(ip netaddr.IP) bool { + return CGNATRange().Contains(ip) && !ChromeOSVMRange().Contains(ip) +} + +func mustPrefix(v *netaddr.IPPrefix, prefix string) { + var err error + *v, err = netaddr.ParseIPPrefix(prefix) + if err != nil { + panic(err) + } +} + +type oncePrefix struct { + sync.Once + v netaddr.IPPrefix +} diff --git a/net/tsaddr/tsaddr_test.go b/net/tsaddr/tsaddr_test.go new file mode 100644 index 000000000..bc7317688 --- /dev/null +++ b/net/tsaddr/tsaddr_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tsaddr + +import "testing" + +func TestChromeOSVMRange(t *testing.T) { + if got, want := ChromeOSVMRange().String(), "100.115.92.0/23"; got != want { + t.Errorf("got %q; want %q", got, want) + } +} + +func TestCGNATRange(t *testing.T) { + if got, want := CGNATRange().String(), "100.64.0.0/10"; got != want { + t.Errorf("got %q; want %q", got, want) + } +} diff --git a/wgengine/router/router_linux.go b/wgengine/router/router_linux.go index 5287e7fcc..7bb80a170 100644 --- a/wgengine/router/router_linux.go +++ b/wgengine/router/router_linux.go @@ -18,6 +18,7 @@ "github.com/tailscale/wireguard-go/tun" "inet.af/netaddr" "tailscale.com/atomicfile" + "tailscale.com/net/tsaddr" "tailscale.com/types/logger" ) @@ -50,11 +51,6 @@ tailscaleBypassMark = "0x20000" ) -// chromeOSVMRange is the subset of the CGNAT IPv4 range used by -// ChromeOS to interconnect the host OS to containers and VMs. We -// avoid allocating Tailscale IPs from it, to avoid conflicts. -const chromeOSVMRange = "100.115.92.0/23" - // netfilterRunner abstracts helpers to run netfilter commands. It // exists purely to swap out go-iptables for a fake implementation in // tests. @@ -666,11 +662,11 @@ func (r *linuxRouter) addNetfilterBase() error { // // Note, this will definitely break nodes that end up using the // CGNAT range for other purposes :(. - args := []string{"!", "-i", r.tunname, "-s", chromeOSVMRange, "-j", "RETURN"} + args := []string{"!", "-i", r.tunname, "-s", tsaddr.ChromeOSVMRange().String(), "-j", "RETURN"} if err := r.ipt4.Append("filter", "ts-input", args...); err != nil { return fmt.Errorf("adding %v in filter/ts-input: %w", args, err) } - args = []string{"!", "-i", r.tunname, "-s", "100.64.0.0/10", "-j", "DROP"} + args = []string{"!", "-i", r.tunname, "-s", tsaddr.CGNATRange().String(), "-j", "DROP"} if err := r.ipt4.Append("filter", "ts-input", args...); err != nil { return fmt.Errorf("adding %v in filter/ts-input: %w", args, err) } @@ -694,7 +690,7 @@ func (r *linuxRouter) addNetfilterBase() error { if err := r.ipt4.Append("filter", "ts-forward", args...); err != nil { return fmt.Errorf("adding %v in filter/ts-forward: %w", args, err) } - args = []string{"-o", r.tunname, "-s", "100.64.0.0/10", "-j", "DROP"} + args = []string{"-o", r.tunname, "-s", tsaddr.CGNATRange().String(), "-j", "DROP"} if err := r.ipt4.Append("filter", "ts-forward", args...); err != nil { return fmt.Errorf("adding %v in filter/ts-forward: %w", args, err) }