mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-10 06:36:14 +02:00
netns: add Android callback to bind socket to network (#18915)
After switching from cellular to wifi without ipv6, ForeachInterface still sees rmnet prefixes, so HaveV6 stays true, and magicsock keeps attempting ipv6 connections that either route through cellular or time out for users on wifi without ipv6 This: -Adds SetAndroidBindToNetworkFunc, a callback to bind the socket to the selected Android Network object Updates tailscale/tailscale#6152 Signed-off-by: kari-ts <kari@tailscale.com>
This commit is contained in:
parent
dd1da0b389
commit
4c7c1091ba
@ -6299,6 +6299,9 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
|
||||
|
||||
// See the netns package for documentation on what these capability do.
|
||||
netns.SetBindToInterfaceByRoute(b.logf, nm.HasCap(tailcfg.CapabilityBindToInterfaceByRoute))
|
||||
if runtime.GOOS == "android" {
|
||||
netns.SetDisableAndroidBindToActiveNetwork(b.logf, nm.HasCap(tailcfg.NodeAttrDisableAndroidBindToActiveNetwork))
|
||||
}
|
||||
netns.SetDisableBindConnToInterface(b.logf, nm.HasCap(tailcfg.CapabilityDebugDisableBindConnToInterface))
|
||||
netns.SetDisableBindConnToInterfaceAppleExt(b.logf, nm.HasCap(tailcfg.CapabilityDebugDisableBindConnToInterfaceAppleExt))
|
||||
|
||||
|
||||
@ -46,6 +46,18 @@ func SetBindToInterfaceByRoute(logf logger.Logf, v bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// When true, disableAndroidBindToActiveNetwork skips binding sockets to the currently
|
||||
// active network on Android.
|
||||
var disableAndroidBindToActiveNetwork atomic.Bool
|
||||
|
||||
// SetDisableAndroidBindToActiveNetwork disables the default behavior of binding
|
||||
// sockets to the currently active network on Android.
|
||||
func SetDisableAndroidBindToActiveNetwork(logf logger.Logf, v bool) {
|
||||
if runtime.GOOS == "android" && disableAndroidBindToActiveNetwork.Swap(v) != v {
|
||||
logf("netns: disableAndroidBindToActiveNetwork changed to %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
var disableBindConnToInterface atomic.Bool
|
||||
|
||||
// SetDisableBindConnToInterface disables the (normal) behavior of binding
|
||||
|
||||
@ -17,6 +17,9 @@ import (
|
||||
var (
|
||||
androidProtectFuncMu sync.Mutex
|
||||
androidProtectFunc func(fd int) error
|
||||
|
||||
androidBindToNetworkFuncMu sync.Mutex
|
||||
androidBindToNetworkFunc func(fd int) error
|
||||
)
|
||||
|
||||
// UseSocketMark reports whether SO_MARK is in use. Android does not use SO_MARK.
|
||||
@ -50,6 +53,14 @@ func SetAndroidProtectFunc(f func(fd int) error) {
|
||||
androidProtectFunc = f
|
||||
}
|
||||
|
||||
// SetAndroidBindToNetworkFunc registers a func provided by Android that binds
|
||||
// the socket FD to the currently selected underlying network.
|
||||
func SetAndroidBindToNetworkFunc(f func(fd int) error) {
|
||||
androidBindToNetworkFuncMu.Lock()
|
||||
defer androidBindToNetworkFuncMu.Unlock()
|
||||
androidBindToNetworkFunc = f
|
||||
}
|
||||
|
||||
func control(logger.Logf, *netmon.Monitor) func(network, address string, c syscall.RawConn) error {
|
||||
return controlC
|
||||
}
|
||||
@ -60,14 +71,36 @@ func control(logger.Logf, *netmon.Monitor) func(network, address string, c sysca
|
||||
// and net.ListenConfig.Control.
|
||||
func controlC(network, address string, c syscall.RawConn) error {
|
||||
var sockErr error
|
||||
|
||||
err := c.Control(func(fd uintptr) {
|
||||
fdInt := int(fd)
|
||||
|
||||
// Protect from VPN loops
|
||||
androidProtectFuncMu.Lock()
|
||||
f := androidProtectFunc
|
||||
pf := androidProtectFunc
|
||||
androidProtectFuncMu.Unlock()
|
||||
if f != nil {
|
||||
sockErr = f(int(fd))
|
||||
if pf != nil {
|
||||
if err := pf(fdInt); err != nil {
|
||||
sockErr = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if disableAndroidBindToActiveNetwork.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
androidBindToNetworkFuncMu.Lock()
|
||||
bf := androidBindToNetworkFunc
|
||||
androidBindToNetworkFuncMu.Unlock()
|
||||
if bf != nil {
|
||||
if err := bf(fdInt); err != nil {
|
||||
sockErr = err
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("RawConn.Control on %T: %w", c, err)
|
||||
}
|
||||
|
||||
@ -180,7 +180,8 @@ type CapabilityVersion int
|
||||
// - 131: 2025-11-25: client respects [NodeAttrDefaultAutoUpdate]
|
||||
// - 132: 2026-02-13: client respects [NodeAttrDisableHostsFileUpdates]
|
||||
// - 133: 2026-02-17: client understands [NodeAttrForceRegisterMagicDNSIPv4Only]; MagicDNS IPv6 registered w/ OS by default
|
||||
const CurrentCapabilityVersion CapabilityVersion = 133
|
||||
// - 134: 2026-03-09: Client understands [NodeAttrDisableAndroidBindToActiveNetwork]
|
||||
const CurrentCapabilityVersion CapabilityVersion = 134
|
||||
|
||||
// ID is an integer ID for a user, node, or login allocated by the
|
||||
// control plane.
|
||||
@ -2463,6 +2464,12 @@ const (
|
||||
// details on the behaviour of this capability.
|
||||
CapabilityBindToInterfaceByRoute NodeCapability = "https://tailscale.com/cap/bind-to-interface-by-route"
|
||||
|
||||
// NodeAttrDisableAndroidBindToActiveNetwork disables binding sockets to the
|
||||
// currently active network on Android, which is enabled by default.
|
||||
// This allows the control plane to turn off the behavior if it causes
|
||||
// problems.
|
||||
NodeAttrDisableAndroidBindToActiveNetwork NodeCapability = "disable-android-bind-to-active-network"
|
||||
|
||||
// CapabilityDebugDisableAlternateDefaultRouteInterface changes how Darwin
|
||||
// nodes get the default interface. There is an optional hook (used by the
|
||||
// macOS and iOS clients) to override the default interface, this capability
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user