ipn/ipnlocal: prefer one CGNAT route on Android (#19652)

Android rebuilds its VpnService interface when the VPN route
configuration changes, which tears down long lived TCP connections
through the tunnel. Use the same automatic OneCGNATRoute behavior as
macOS on Android, and prefer the single CGNAT route when no other
interface is using the CGNAT, falling back to fine grained peer routes
otherwise.

Updates tailscale/tailscale#19591

Signed-off-by: kari <kari@tailscale.com>
This commit is contained in:
kari-ts
2026-05-05 19:11:17 -07:00
committed by GitHub
parent f844c8bc32
commit c721189cef
2 changed files with 83 additions and 5 deletions
+9 -5
View File
@@ -5458,12 +5458,16 @@ func shouldUseOneCGNATRoute(logf logger.Logf, mon *netmon.Monitor, controlKnobs
return true
}
// 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
// Prefer a single CGNAT route on platforms where updateing the VPN
// configuration is espensive. On macOS, changing the network extension
// configuration can disrupt existing connections notably Chrome; see
// https://github.com/tailscale/tailscale/issues/3102). On Android, updating
// VpnService.Builder configuration requires establishing a new VPN interface,
// which tears down long lived TCP connections.
//
// Only use fine-grained routes if another interfaces is also using the CGNAT
// IP range.
if versionOS == "macOS" {
if versionOS == "macOS" || versionOS == "android" {
hasCGNATInterface, err := mon.HasCGNATInterface()
if err != nil {
logf("shouldUseOneCGNATRoute: Could not determine if any interfaces use CGNAT: %v", err)