net/tsaddr, wgengine/netstack: add IPv6 range that forwards to site-relative IPv4

This defines a new magic IPv6 prefix, fd7a:115c:a1e0:b1a::/64, a
subset of our existing /48, where the final 32 bits are an IPv4
address, and the middle 32 bits are a user-chosen "site ID". (which
must currently be 0000:00xx; the top 3 bytes must be zero for now)

e.g., I can say my home LAN's "site ID" is "0000:00bb" and then
advertise its 10.2.0.0/16 IPv4 range via IPv6, like:

    tailscale up --advertise-routes=fd7a:115c:a1e0:b1a::bb:10.2.0.0/112

(112 being /128 minuse the /96 v6 prefix length)

Then people in my tailnet can:

     $ curl '[fd7a:115c:a1e0:b1a::bb:10.2.0.230]'
     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ....

Updates #3616, etc

RELNOTE=initial support for TS IPv6 addresses to route v4 "via" specific nodes

Change-Id: I9b49b6ad10410a24b5866b9fbc69d3cae1f600ef
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-03-30 08:47:16 -07:00
committed by Brad Fitzpatrick
parent f992749b98
commit 3ae701f0eb
6 changed files with 156 additions and 4 deletions

View File

@@ -34,6 +34,7 @@ var (
cgnatRange oncePrefix
ulaRange oncePrefix
tsUlaRange oncePrefix
tsViaRange oncePrefix
ula4To6Range oncePrefix
ulaEph6Range oncePrefix
serviceIPv6 oncePrefix
@@ -72,6 +73,14 @@ func TailscaleULARange() netaddr.IPPrefix {
return tsUlaRange.v
}
// TailscaleViaRange returns the IPv6 Unique Local Address subset range
// TailscaleULARange that's used for IPv4 tunneling via IPv6.
func TailscaleViaRange() netaddr.IPPrefix {
// 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() netaddr.IPPrefix {
@@ -241,3 +250,33 @@ func AllIPv6() netaddr.IPPrefix { return allIPv6 }
// ExitRoutes returns a slice containing AllIPv4 and AllIPv6.
func ExitRoutes() []netaddr.IPPrefix { return []netaddr.IPPrefix{allIPv4, allIPv6} }
// FilterPrefixes returns a new slice, not aliasing in, containing elements of
// in that match f.
func FilterPrefixesCopy(in []netaddr.IPPrefix, f func(netaddr.IPPrefix) bool) []netaddr.IPPrefix {
var out []netaddr.IPPrefix
for _, v := range in {
if 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 netaddr.IPPrefix) bool {
return TailscaleViaRange().Contains(p.IP())
}
// 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 netaddr.IP) netaddr.IP {
if TailscaleViaRange().Contains(ip) {
a := ip.As16()
return netaddr.IPFrom4(*(*[4]byte)(a[12:16]))
}
return ip
}