mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-30 05:25:35 +00:00
0d991249e1
And convert all callers over to the methods that check SelfNode. Now we don't have multiple ways to express things in tests (setting fields on SelfNode vs NetworkMap, sometimes inconsistently) and don't have multiple ways to check those two fields (often only checking one or the other). Updates #9443 Change-Id: I2d7ba1cf6556142d219fae2be6f484f528756e3c Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
317 lines
9.4 KiB
Go
317 lines
9.4 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package tsaddr handles Tailscale-specific IPs and ranges.
|
|
package tsaddr
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"net/netip"
|
|
"slices"
|
|
"sync"
|
|
|
|
"go4.org/netipx"
|
|
"tailscale.com/net/netaddr"
|
|
"tailscale.com/types/views"
|
|
)
|
|
|
|
// 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() netip.Prefix {
|
|
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/s/cgnat
|
|
// Note that Tailscale does not assign out of the ChromeOSVMRange.
|
|
func CGNATRange() netip.Prefix {
|
|
cgnatRange.Do(func() { mustPrefix(&cgnatRange.v, "100.64.0.0/10") })
|
|
return cgnatRange.v
|
|
}
|
|
|
|
var (
|
|
cgnatRange oncePrefix
|
|
ulaRange oncePrefix
|
|
tsUlaRange oncePrefix
|
|
tsViaRange oncePrefix
|
|
ula4To6Range oncePrefix
|
|
ulaEph6Range oncePrefix
|
|
serviceIPv6 oncePrefix
|
|
)
|
|
|
|
// TailscaleServiceIP returns the IPv4 listen address of services
|
|
// provided by Tailscale itself such as the MagicDNS proxy.
|
|
//
|
|
// For IPv6, use TailscaleServiceIPv6.
|
|
func TailscaleServiceIP() netip.Addr {
|
|
return netaddr.IPv4(100, 100, 100, 100) // "100.100.100.100" for those grepping
|
|
}
|
|
|
|
// TailscaleServiceIPv6 returns the IPv6 listen address of the services
|
|
// provided by Tailscale itself such as the MagicDNS proxy.
|
|
//
|
|
// For IPv4, use TailscaleServiceIP.
|
|
func TailscaleServiceIPv6() netip.Addr {
|
|
serviceIPv6.Do(func() { mustPrefix(&serviceIPv6.v, TailscaleServiceIPv6String+"/128") })
|
|
return serviceIPv6.v.Addr()
|
|
}
|
|
|
|
const (
|
|
TailscaleServiceIPString = "100.100.100.100"
|
|
TailscaleServiceIPv6String = "fd7a:115c:a1e0::53"
|
|
)
|
|
|
|
// IsTailscaleIP reports whether ip is an IP address in a range that
|
|
// Tailscale assigns from.
|
|
func IsTailscaleIP(ip netip.Addr) bool {
|
|
if ip.Is4() {
|
|
return CGNATRange().Contains(ip) && !ChromeOSVMRange().Contains(ip)
|
|
}
|
|
return TailscaleULARange().Contains(ip)
|
|
}
|
|
|
|
// TailscaleULARange returns the IPv6 Unique Local Address range that
|
|
// is the superset range that Tailscale assigns out of.
|
|
func TailscaleULARange() netip.Prefix {
|
|
tsUlaRange.Do(func() { mustPrefix(&tsUlaRange.v, "fd7a:115c:a1e0::/48") })
|
|
return tsUlaRange.v
|
|
}
|
|
|
|
// TailscaleViaRange returns the IPv6 Unique Local Address subset range
|
|
// TailscaleULARange that's used for IPv4 tunneling via IPv6.
|
|
func TailscaleViaRange() netip.Prefix {
|
|
// Mnemonic: "b1a" sounds like "via".
|
|
tsViaRange.Do(func() { mustPrefix(&tsViaRange.v, "fd7a:115c:a1e0:b1a::/64") })
|
|
return tsViaRange.v
|
|
}
|
|
|
|
// Tailscale4To6Range returns the subset of TailscaleULARange used for
|
|
// auto-translated Tailscale ipv4 addresses.
|
|
func Tailscale4To6Range() netip.Prefix {
|
|
// This IP range has no significance, beyond being a subset of
|
|
// TailscaleULARange. The bits from /48 to /104 were picked at
|
|
// random.
|
|
ula4To6Range.Do(func() { mustPrefix(&ula4To6Range.v, "fd7a:115c:a1e0:ab12:4843:cd96:6200::/104") })
|
|
return ula4To6Range.v
|
|
}
|
|
|
|
// TailscaleEphemeral6Range returns the subset of TailscaleULARange
|
|
// used for ephemeral IPv6-only Tailscale nodes.
|
|
func TailscaleEphemeral6Range() netip.Prefix {
|
|
// This IP range has no significance, beyond being a subset of
|
|
// TailscaleULARange. The bits from /48 to /64 were picked at
|
|
// random, with the only criterion being to not be the conflict
|
|
// with the Tailscale4To6Range above.
|
|
ulaEph6Range.Do(func() { mustPrefix(&ulaEph6Range.v, "fd7a:115c:a1e0:efe3::/64") })
|
|
return ulaEph6Range.v
|
|
}
|
|
|
|
// Tailscale4To6Placeholder returns an IP address that can be used as
|
|
// a source IP when one is required, but a netmap didn't provide
|
|
// any. This address never gets allocated by the 4-to-6 algorithm in
|
|
// control.
|
|
//
|
|
// Currently used to work around a Windows limitation when programming
|
|
// IPv6 routes in corner cases.
|
|
func Tailscale4To6Placeholder() netip.Addr {
|
|
return Tailscale4To6Range().Addr()
|
|
}
|
|
|
|
// Tailscale4To6 returns a Tailscale IPv6 address that maps 1:1 to the
|
|
// given Tailscale IPv4 address. Returns a zero IP if ipv4 isn't a
|
|
// Tailscale IPv4 address.
|
|
func Tailscale4To6(ipv4 netip.Addr) netip.Addr {
|
|
if !ipv4.Is4() || !IsTailscaleIP(ipv4) {
|
|
return netip.Addr{}
|
|
}
|
|
ret := Tailscale4To6Range().Addr().As16()
|
|
v4 := ipv4.As4()
|
|
copy(ret[13:], v4[1:])
|
|
return netip.AddrFrom16(ret)
|
|
}
|
|
|
|
// Tailscale6to4 returns the IPv4 address corresponding to the given
|
|
// tailscale IPv6 address within the 4To6 range. The IPv4 address
|
|
// and true are returned if the given address was in the correct range,
|
|
// false if not.
|
|
func Tailscale6to4(ipv6 netip.Addr) (netip.Addr, bool) {
|
|
if !ipv6.Is6() || !Tailscale4To6Range().Contains(ipv6) {
|
|
return netip.Addr{}, false
|
|
}
|
|
v6 := ipv6.As16()
|
|
return netip.AddrFrom4([4]byte{100, v6[13], v6[14], v6[15]}), true
|
|
}
|
|
|
|
func mustPrefix(v *netip.Prefix, prefix string) {
|
|
var err error
|
|
*v, err = netip.ParsePrefix(prefix)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
type oncePrefix struct {
|
|
sync.Once
|
|
v netip.Prefix
|
|
}
|
|
|
|
// FalseContainsIPFunc is shorthand for NewContainsIPFunc(views.Slice[netip.Prefix]{}).
|
|
func FalseContainsIPFunc() func(ip netip.Addr) bool {
|
|
return func(ip netip.Addr) bool { return false }
|
|
}
|
|
|
|
// NewContainsIPFunc returns a func that reports whether ip is in addrs.
|
|
//
|
|
// It's optimized for the cases of addrs being empty and addrs
|
|
// containing 1 or 2 single-IP prefixes (such as one IPv4 address and
|
|
// one IPv6 address).
|
|
//
|
|
// Otherwise the implementation is somewhat slow.
|
|
func NewContainsIPFunc(addrs views.Slice[netip.Prefix]) func(ip netip.Addr) bool {
|
|
// Specialize the three common cases: no address, just IPv4
|
|
// (or just IPv6), and both IPv4 and IPv6.
|
|
if addrs.Len() == 0 {
|
|
return func(netip.Addr) bool { return false }
|
|
}
|
|
// If any addr is more than a single IP, then just do the slow
|
|
// linear thing until
|
|
// https://github.com/inetaf/netaddr/issues/139 is done.
|
|
if views.SliceContainsFunc(addrs, func(p netip.Prefix) bool { return !p.IsSingleIP() }) {
|
|
acopy := addrs.AsSlice()
|
|
return func(ip netip.Addr) bool {
|
|
for _, a := range acopy {
|
|
if a.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
// Fast paths for 1 and 2 IPs:
|
|
if addrs.Len() == 1 {
|
|
a := addrs.At(0)
|
|
return func(ip netip.Addr) bool { return ip == a.Addr() }
|
|
}
|
|
if addrs.Len() == 2 {
|
|
a, b := addrs.At(0), addrs.At(1)
|
|
return func(ip netip.Addr) bool { return ip == a.Addr() || ip == b.Addr() }
|
|
}
|
|
// General case:
|
|
m := map[netip.Addr]bool{}
|
|
for i := range addrs.LenIter() {
|
|
m[addrs.At(i).Addr()] = true
|
|
}
|
|
return func(ip netip.Addr) bool { return m[ip] }
|
|
}
|
|
|
|
// PrefixesContainsIP reports whether any prefix in ipp contains ip.
|
|
func PrefixesContainsIP(ipp []netip.Prefix, ip netip.Addr) bool {
|
|
for _, r := range ipp {
|
|
if r.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// PrefixIs4 reports whether p is an IPv4 prefix.
|
|
func PrefixIs4(p netip.Prefix) bool { return p.Addr().Is4() }
|
|
|
|
// PrefixIs6 reports whether p is an IPv6 prefix.
|
|
func PrefixIs6(p netip.Prefix) bool { return p.Addr().Is6() }
|
|
|
|
// ContainsExitRoutes reports whether rr contains both the IPv4 and
|
|
// IPv6 /0 route.
|
|
func ContainsExitRoutes(rr views.Slice[netip.Prefix]) bool {
|
|
var v4, v6 bool
|
|
for i := range rr.LenIter() {
|
|
r := rr.At(i)
|
|
if r == allIPv4 {
|
|
v4 = true
|
|
} else if r == allIPv6 {
|
|
v6 = true
|
|
}
|
|
}
|
|
return v4 && v6
|
|
}
|
|
|
|
// ContainsNonExitSubnetRoutes reports whether v contains Subnet
|
|
// Routes other than ExitNode Routes.
|
|
func ContainsNonExitSubnetRoutes(rr views.Slice[netip.Prefix]) bool {
|
|
for i := range rr.LenIter() {
|
|
if rr.At(i).Bits() != 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
var (
|
|
allIPv4 = netip.MustParsePrefix("0.0.0.0/0")
|
|
allIPv6 = netip.MustParsePrefix("::/0")
|
|
)
|
|
|
|
// AllIPv4 returns 0.0.0.0/0.
|
|
func AllIPv4() netip.Prefix { return allIPv4 }
|
|
|
|
// AllIPv6 returns ::/0.
|
|
func AllIPv6() netip.Prefix { return allIPv6 }
|
|
|
|
// ExitRoutes returns a slice containing AllIPv4 and AllIPv6.
|
|
func ExitRoutes() []netip.Prefix { return []netip.Prefix{allIPv4, allIPv6} }
|
|
|
|
// SortPrefixes sorts the prefixes in place.
|
|
func SortPrefixes(p []netip.Prefix) {
|
|
slices.SortFunc(p, netipx.ComparePrefix)
|
|
}
|
|
|
|
// FilterPrefixes returns a new slice, not aliasing in, containing elements of
|
|
// in that match f.
|
|
func FilterPrefixesCopy(in views.Slice[netip.Prefix], f func(netip.Prefix) bool) []netip.Prefix {
|
|
var out []netip.Prefix
|
|
for i := range in.LenIter() {
|
|
if v := in.At(i); f(v) {
|
|
out = append(out, v)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// IsViaPrefix reports whether p is a CIDR in the Tailscale "via" range.
|
|
// See TailscaleViaRange.
|
|
func IsViaPrefix(p netip.Prefix) bool {
|
|
return TailscaleViaRange().Contains(p.Addr())
|
|
}
|
|
|
|
// UnmapVia returns the IPv4 address that corresponds to the provided Tailscale
|
|
// "via" IPv4-in-IPv6 address.
|
|
//
|
|
// If ip is not a via address, it returns ip unchanged.
|
|
func UnmapVia(ip netip.Addr) netip.Addr {
|
|
if TailscaleViaRange().Contains(ip) {
|
|
a := ip.As16()
|
|
return netip.AddrFrom4(*(*[4]byte)(a[12:16]))
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// MapVia returns an IPv6 "via" route for an IPv4 CIDR in a given siteID.
|
|
func MapVia(siteID uint32, v4 netip.Prefix) (via netip.Prefix, err error) {
|
|
if !v4.Addr().Is4() {
|
|
return via, errors.New("want IPv4 CIDR with a site ID")
|
|
}
|
|
viaRange16 := TailscaleViaRange().Addr().As16()
|
|
var a [16]byte
|
|
copy(a[:], viaRange16[:8])
|
|
binary.BigEndian.PutUint32(a[8:], siteID)
|
|
ip4a := v4.Addr().As4()
|
|
copy(a[12:], ip4a[:])
|
|
return netip.PrefixFrom(netip.AddrFrom16(a), v4.Bits()+64+32), nil
|
|
}
|