diff --git a/net/tsaddr/tsaddr.go b/net/tsaddr/tsaddr.go index 9bf81326e..8c6417366 100644 --- a/net/tsaddr/tsaddr.go +++ b/net/tsaddr/tsaddr.go @@ -33,6 +33,7 @@ func CGNATRange() netaddr.IPPrefix { var ( cgnatRange oncePrefix ulaRange oncePrefix + tsUlaRange oncePrefix ula4To6Range oncePrefix ) @@ -57,8 +58,8 @@ func IsTailscaleIP(ip netaddr.IP) bool { // TailscaleULARange returns the IPv6 Unique Local Address range that // is the superset range that Tailscale assigns out of. func TailscaleULARange() netaddr.IPPrefix { - ulaRange.Do(func() { mustPrefix(&ulaRange.v, "fd7a:115c:a1e0::/48") }) - return ulaRange.v + tsUlaRange.Do(func() { mustPrefix(&tsUlaRange.v, "fd7a:115c:a1e0::/48") }) + return tsUlaRange.v } // Tailscale4To6Range returns the subset of TailscaleULARange used for @@ -95,6 +96,11 @@ func Tailscale4To6(ipv4 netaddr.IP) netaddr.IP { return netaddr.IPFrom16(ret) } +func IsULA(ip netaddr.IP) bool { + ulaRange.Do(func() { mustPrefix(&ulaRange.v, "fc00::/7") }) + return ulaRange.v.Contains(ip) +} + func mustPrefix(v *netaddr.IPPrefix, prefix string) { var err error *v, err = netaddr.ParseIPPrefix(prefix) diff --git a/net/tsaddr/tsaddr_test.go b/net/tsaddr/tsaddr_test.go index ad98a5160..e66aa8d8d 100644 --- a/net/tsaddr/tsaddr_test.go +++ b/net/tsaddr/tsaddr_test.go @@ -42,3 +42,25 @@ func TestCGNATRange(t *testing.T) { t.Errorf("got %q; want %q", got, want) } } + +func TestIsUla(t *testing.T) { + tests := []struct { + name string + ip string + want bool + }{ + {"first ULA", "fc00::1", true}, + {"not ULA", "fb00::1", false}, + {"Tailscale", "fd7a:115c:a1e0::1", true}, + {"Cloud Run", "fddf:3978:feb1:d745::1", true}, + {"zeros", "0000:0000:0000:0000:0000:0000:0000:0000", false}, + {"Link Local", "fe80::1", false}, + {"Global", "2602::1", false}, + } + + for _, test := range tests { + if got := IsULA(netaddr.MustParseIP(test.ip)); got != test.want { + t.Errorf("IsULA(%s) = %v, want %v", test.name, got, test.want) + } + } +}