// 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 ulaRange oncePrefix tsUlaRange oncePrefix ula4To6Range oncePrefix ulaEph6Range oncePrefix ) // TailscaleServiceIP returns the listen address of services // provided by Tailscale itself such as the MagicDNS proxy. func TailscaleServiceIP() netaddr.IP { serviceIP.Do(func() { mustIP(&serviceIP.v, "100.100.100.100") }) return serviceIP.v } var serviceIP onceIP // IsTailscaleIP reports whether ip is an IP address in a range that // Tailscale assigns from. func IsTailscaleIP(ip netaddr.IP) 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() netaddr.IPPrefix { tsUlaRange.Do(func() { mustPrefix(&tsUlaRange.v, "fd7a:115c:a1e0::/48") }) return tsUlaRange.v } // Tailscale4To6Range returns the subset of TailscaleULARange used for // auto-translated Tailscale ipv4 addresses. func Tailscale4To6Range() netaddr.IPPrefix { // 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() netaddr.IPPrefix { // 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() netaddr.IP { return Tailscale4To6Range().IP } // 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 netaddr.IP) netaddr.IP { if !ipv4.Is4() || !IsTailscaleIP(ipv4) { return netaddr.IP{} } ret := Tailscale4To6Range().IP.As16() v4 := ipv4.As4() copy(ret[13:], v4[1:]) 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) if err != nil { panic(err) } } type oncePrefix struct { sync.Once v netaddr.IPPrefix } func mustIP(v *netaddr.IP, ip string) { var err error *v, err = netaddr.ParseIP(ip) if err != nil { panic(err) } } type onceIP struct { sync.Once v netaddr.IP } // 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 []netaddr.IPPrefix) func(ip netaddr.IP) bool { // Specialize the three common cases: no address, just IPv4 // (or just IPv6), and both IPv4 and IPv6. if len(addrs) == 0 { return func(netaddr.IP) 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. for _, a := range addrs { if a.IsSingleIP() { continue } acopy := append([]netaddr.IPPrefix(nil), addrs...) return func(ip netaddr.IP) bool { for _, a := range acopy { if a.Contains(ip) { return true } } return false } } // Fast paths for 1 and 2 IPs: if len(addrs) == 1 { a := addrs[0] return func(ip netaddr.IP) bool { return ip == a.IP } } if len(addrs) == 2 { a, b := addrs[0], addrs[1] return func(ip netaddr.IP) bool { return ip == a.IP || ip == b.IP } } // General case: m := map[netaddr.IP]bool{} for _, a := range addrs { m[a.IP] = true } return func(ip netaddr.IP) bool { return m[ip] } }