wgengine/router: provide explicit hook to signal Android when VPN needs to be reconfigured

This allows clients to avoid establishing their VPN multiple times when
both routes and DNS are changing in rapid succession.

Updates tailscale/corp#18928

Signed-off-by: Percy Wegmann <percy@tailscale.com>
pull/11625/head
Percy Wegmann 8 months ago committed by Percy Wegmann
parent 1a38d2a3b4
commit 853e3e29a0

@ -126,6 +126,7 @@ type userspaceEngine struct {
sentActivityAt map[netip.Addr]*mono.Time // value is accessed atomically sentActivityAt map[netip.Addr]*mono.Time // value is accessed atomically
destIPActivityFuncs map[netip.Addr]func() destIPActivityFuncs map[netip.Addr]func()
lastStatusPollTime mono.Time // last time we polled the engine status lastStatusPollTime mono.Time // last time we polled the engine status
reconfigureVPN func() error // or nil
mu sync.Mutex // guards following; see lock order comment below mu sync.Mutex // guards following; see lock order comment below
netMap *netmap.NetworkMap // or nil netMap *netmap.NetworkMap // or nil
@ -175,6 +176,13 @@ type Config struct {
// If nil, a fake OSConfigurator that does nothing is used. // If nil, a fake OSConfigurator that does nothing is used.
DNS dns.OSConfigurator DNS dns.OSConfigurator
// ReconfigureVPN provides an optional hook for platforms like Android to
// know when it's time to reconfigure their VPN implementation. Such
// platforms can only set their entire VPN configuration (routes, DNS, etc)
// at all once and can't make piecemeal incremental changes, so this
// provides a hook to "flush" a batch of Router and/or DNS changes.
ReconfigureVPN func() error
// NetMon optionally provides an existing network monitor to re-use. // NetMon optionally provides an existing network monitor to re-use.
// If nil, a new network monitor is created. // If nil, a new network monitor is created.
NetMon *netmon.Monitor NetMon *netmon.Monitor
@ -283,6 +291,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
confListenPort: conf.ListenPort, confListenPort: conf.ListenPort,
birdClient: conf.BIRDClient, birdClient: conf.BIRDClient,
controlKnobs: conf.ControlKnobs, controlKnobs: conf.ControlKnobs,
reconfigureVPN: conf.ReconfigureVPN,
} }
if e.birdClient != nil { if e.birdClient != nil {
@ -956,6 +965,9 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
if err != nil { if err != nil {
return err return err
} }
if err := e.reconfigureVPNIfNecessary(); err != nil {
return err
}
} }
// Shutdown the network logger. // Shutdown the network logger.
@ -1161,10 +1173,12 @@ func (e *userspaceEngine) linkChange(delta *netmon.ChangeDelta) {
} }
} }
// Hacky workaround for Linux DNS issue 2458: on // Hacky workaround for Unix DNS issue 2458: on
// suspend/resume or whenever NetworkManager is started, it // suspend/resume or whenever NetworkManager is started, it
// nukes all systemd-resolved configs. So reapply our DNS // nukes all systemd-resolved configs. So reapply our DNS
// config on major link change. // config on major link change.
// TODO: explain why this is ncessary not just on Linux but also android
// and Apple platforms.
if changed { if changed {
switch runtime.GOOS { switch runtime.GOOS {
case "linux", "android", "ios", "darwin": case "linux", "android", "ios", "darwin":
@ -1174,6 +1188,8 @@ func (e *userspaceEngine) linkChange(delta *netmon.ChangeDelta) {
if dnsCfg != nil { if dnsCfg != nil {
if err := e.dns.Set(*dnsCfg); err != nil { if err := e.dns.Set(*dnsCfg); err != nil {
e.logf("wgengine: error setting DNS config after major link change: %v", err) e.logf("wgengine: error setting DNS config after major link change: %v", err)
} else if err := e.reconfigureVPNIfNecessary(); err != nil {
e.logf("wgengine: error reconfiguring VPN after major link change: %v", err)
} else { } else {
e.logf("wgengine: set DNS config again after major link change") e.logf("wgengine: set DNS config again after major link change")
} }
@ -1528,3 +1544,10 @@ func (e *userspaceEngine) InstallCaptureHook(cb capture.Callback) {
e.tundev.InstallCaptureHook(cb) e.tundev.InstallCaptureHook(cb)
e.magicConn.InstallCaptureHook(cb) e.magicConn.InstallCaptureHook(cb)
} }
func (e *userspaceEngine) reconfigureVPNIfNecessary() error {
if e.reconfigureVPN == nil {
return nil
}
return e.reconfigureVPN()
}

Loading…
Cancel
Save