ipn/ipnlocal: make IPv6 OS routes be a single /48 for our ULA space

And if we have over 10,000 CGNAT routes, just route the entire
CGNAT range. (for the hello test server)

Fixes #1450

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/1461/head
Brad Fitzpatrick 4 years ago committed by Brad Fitzpatrick
parent a6d098c750
commit 82edf94df7

@ -1362,6 +1362,41 @@ var (
ipv6Default = netaddr.MustParseIPPrefix("::/0") ipv6Default = netaddr.MustParseIPPrefix("::/0")
) )
// peerRoutes returns the routerConfig.Routes to access peers.
// If there are over cgnatThreshold CGNAT routes, one big CGNAT route
// is used instead.
func peerRoutes(peers []wgcfg.Peer, cgnatThreshold int) (routes []netaddr.IPPrefix) {
tsULA := tsaddr.TailscaleULARange()
cgNAT := tsaddr.CGNATRange()
var didULA bool
var cgNATIPs []netaddr.IPPrefix
for _, peer := range peers {
for _, aip := range peer.AllowedIPs {
aip = unmapIPPrefix(aip)
// Only add the Tailscale IPv6 ULA once, if we see anybody using part of it.
if aip.IP.Is6() && aip.IsSingleIP() && tsULA.Contains(aip.IP) {
if !didULA {
didULA = true
routes = append(routes, tsULA)
}
continue
}
if aip.IsSingleIP() && cgNAT.Contains(aip.IP) {
cgNATIPs = append(cgNATIPs, aip)
} else {
routes = append(routes, aip)
}
}
}
if len(cgNATIPs) > cgnatThreshold {
// Probably the hello server. Just append one big route.
routes = append(routes, cgNAT)
} else {
routes = append(routes, cgNATIPs...)
}
return routes
}
// routerConfig produces a router.Config from a wireguard config and IPN prefs. // routerConfig produces a router.Config from a wireguard config and IPN prefs.
func routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router.Config { func routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router.Config {
rs := &router.Config{ rs := &router.Config{
@ -1369,10 +1404,7 @@ func routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router.Config {
SubnetRoutes: unmapIPPrefixes(prefs.AdvertiseRoutes), SubnetRoutes: unmapIPPrefixes(prefs.AdvertiseRoutes),
SNATSubnetRoutes: !prefs.NoSNAT, SNATSubnetRoutes: !prefs.NoSNAT,
NetfilterMode: prefs.NetfilterMode, NetfilterMode: prefs.NetfilterMode,
} Routes: peerRoutes(cfg.Peers, 10_000),
for _, peer := range cfg.Peers {
rs.Routes = append(rs.Routes, unmapIPPrefixes(peer.AllowedIPs)...)
} }
// Sanity check: we expect the control server to program both a v4 // Sanity check: we expect the control server to program both a v4
@ -1409,10 +1441,14 @@ func routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router.Config {
return rs return rs
} }
func unmapIPPrefix(ipp netaddr.IPPrefix) netaddr.IPPrefix {
return netaddr.IPPrefix{IP: ipp.IP.Unmap(), Bits: ipp.Bits}
}
func unmapIPPrefixes(ippsList ...[]netaddr.IPPrefix) (ret []netaddr.IPPrefix) { func unmapIPPrefixes(ippsList ...[]netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
for _, ipps := range ippsList { for _, ipps := range ippsList {
for _, ipp := range ipps { for _, ipp := range ipps {
ret = append(ret, netaddr.IPPrefix{IP: ipp.IP.Unmap(), Bits: ipp.Bits}) ret = append(ret, unmapIPPrefix(ipp))
} }
} }
return ret return ret

@ -5,12 +5,14 @@
package ipnlocal package ipnlocal
import ( import (
"reflect"
"testing" "testing"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/wgengine/wgcfg"
) )
func TestNetworkMapCompare(t *testing.T) { func TestNetworkMapCompare(t *testing.T) {
@ -171,3 +173,95 @@ func TestShrinkDefaultRoute(t *testing.T) {
} }
} }
} }
func TestPeerRoutes(t *testing.T) {
pp := netaddr.MustParseIPPrefix
tests := []struct {
name string
peers []wgcfg.Peer
want []netaddr.IPPrefix
}{
{
name: "small_v4",
peers: []wgcfg.Peer{
{
AllowedIPs: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
},
},
},
want: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
},
},
{
name: "big_v4",
peers: []wgcfg.Peer{
{
AllowedIPs: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
pp("100.101.102.104/32"),
pp("100.101.102.105/32"),
},
},
},
want: []netaddr.IPPrefix{
pp("100.64.0.0/10"),
},
},
{
name: "has_1_v6",
peers: []wgcfg.Peer{
{
AllowedIPs: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b240/128"),
},
},
},
want: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0::/48"),
},
},
{
name: "has_2_v6",
peers: []wgcfg.Peer{
{
AllowedIPs: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b240/128"),
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b241/128"),
},
},
},
want: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0::/48"),
},
},
{
name: "big_v4_big_v6",
peers: []wgcfg.Peer{
{
AllowedIPs: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
pp("100.101.102.104/32"),
pp("100.101.102.105/32"),
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b240/128"),
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b241/128"),
},
},
},
want: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0::/48"),
pp("100.64.0.0/10"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := peerRoutes(tt.peers, 2)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got = %v; want %v", got, tt.want)
}
})
}
}

Loading…
Cancel
Save