summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMihai Parparita <mihai@tailscale.com>2022-06-29 13:25:45 -0700
committerMihai Parparita <mihai.parparita@gmail.com>2022-06-30 13:18:52 -0700
commitec649e707f2f111fd01ca649f3e0b5fe867fde99 (patch)
treebc89c8918c463d190a77fe4400b6a21cb21001d8
parent3f4fd64311708ed0c0242a407ca0be9a833acb06 (diff)
downloadtailscale-ec649e707f2f111fd01ca649f3e0b5fe867fde99.tar.xz
tailscale-ec649e707f2f111fd01ca649f3e0b5fe867fde99.zip
ipn/ipnlocal: prefer to use one CGNAT route on the Mac
Together with 06aa1416325424ab2881a9e2442697f9707f363b this minimizes the number of NEPacketTunnelNetworkSettings updates that we have to do, and thus avoids Chrome interrupting outstanding requests due to (perceived) network changes. Updates #3102 Signed-off-by: Mihai Parparita <mihai@tailscale.com>
-rw-r--r--ipn/ipnlocal/local.go34
-rw-r--r--net/interfaces/interfaces.go22
2 files changed, 55 insertions, 1 deletions
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index e9431a39a..cabda6f47 100644
--- a/ipn/ipnlocal/local.go
+++ b/ipn/ipnlocal/local.go
@@ -2175,7 +2175,6 @@ func (b *LocalBackend) authReconfig() {
nm := b.netMap
hasPAC := b.prevIfState.HasPAC()
disableSubnetsIfPAC := nm != nil && nm.Debug != nil && nm.Debug.DisableSubnetsIfPAC.EqualBool(true)
- oneCGNATRoute := nm != nil && nm.Debug != nil && nm.Debug.OneCGNATRoute.EqualBool(true)
b.mu.Unlock()
if blocked {
@@ -2220,6 +2219,7 @@ func (b *LocalBackend) authReconfig() {
return
}
+ oneCGNATRoute := shouldUseOneCGNATRoute(nm, b.logf, version.OS())
rcfg := b.routerConfig(cfg, prefs, oneCGNATRoute)
dcfg := dnsConfigForNetmap(nm, prefs, b.logf, version.OS())
@@ -2232,6 +2232,38 @@ func (b *LocalBackend) authReconfig() {
b.initPeerAPIListener()
}
+// shouldUseOneCGNATRoute reports whether we should prefer to make one big
+// CGNAT /10 route rather than a /32 per peer.
+//
+// The versionOS is a Tailscale-style version ("iOS", "macOS") and not
+// a runtime.GOOS.
+func shouldUseOneCGNATRoute(nm *netmap.NetworkMap, logf logger.Logf, versionOS string) bool {
+ // Explicit enabling or disabling always take precedence.
+ if nm.Debug != nil {
+ if v, ok := nm.Debug.OneCGNATRoute.Get(); ok {
+ logf("[v1] shouldUseOneCGNATRoute: explicit=%v", v)
+ return v
+ }
+ }
+ // Also prefer to do this on the Mac, so that we don't need to constantly
+ // update the network extension configuration (which is disruptive to
+ // Chrome, see https://github.com/tailscale/tailscale/issues/3102). Only
+ // use fine-grained routes if another interfaces is also using the CGNAT
+ // IP range.
+ if versionOS == "macOS" {
+ hasCGNATInterface, err := interfaces.HasCGNATInterface()
+ if err != nil {
+ logf("shouldUseOneCGNATRoute: Could not determine if any interfaces use CGNAT: %v", err)
+ return false
+ }
+ logf("[v1] shouldUseOneCGNATRoute: macOS automatic=%v", !hasCGNATInterface)
+ if !hasCGNATInterface {
+ return true
+ }
+ }
+ return false
+}
+
// dnsConfigForNetmap returns a *dns.Config for the given netmap,
// prefs, client OS version, and cloud hosting environment.
//
diff --git a/net/interfaces/interfaces.go b/net/interfaces/interfaces.go
index 4f6c6e680..d724af602 100644
--- a/net/interfaces/interfaces.go
+++ b/net/interfaces/interfaces.go
@@ -720,3 +720,25 @@ func DefaultRouteInterface() (string, error) {
func DefaultRoute() (DefaultRouteDetails, error) {
return defaultRoute()
}
+
+// HasCGNATInterface reports whether there are any non-Tailscale interfaces that
+// use a CGNAT IP range.
+func HasCGNATInterface() (bool, error) {
+ hasCGNATInterface := false
+ cgnatRange := tsaddr.CGNATRange()
+ err := ForeachInterface(func(i Interface, pfxs []netaddr.IPPrefix) {
+ if hasCGNATInterface || !i.IsUp() || isTailscaleInterface(i.Name, pfxs) {
+ return
+ }
+ for _, pfx := range pfxs {
+ if cgnatRange.Overlaps(pfx) {
+ hasCGNATInterface = true
+ break
+ }
+ }
+ })
+ if err != nil {
+ return false, err
+ }
+ return hasCGNATInterface, nil
+}