net/netmon: move TailscaleInterfaceIndex out of netmon.State (#18428)

fixes tailscale/tailscale#18418

Both Serve and PeerAPI broke when we moved the TailscaleInterfaceName
into State, which is updated asynchronously and may not be
available when we configure the listeners.

This extracts the explicit interface name property from netmon.State
and adds as a static struct with getters that have proper error
handling.

The bug is only found in sandboxed Darwin clients, where we
need to know the Tailscale interface details in order to set up the
listeners correctly (they must bind to our interface explicitly to escape
the network sandboxing that is applied by NECP).

Currently set only sandboxed macOS and Plan9 set this but it will
also be useful on Windows to simplify interface filtering in netns.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
Jonathan Nobels
2026-01-16 14:53:23 -05:00
committed by GitHub
parent 1478028591
commit 643e91f2eb
9 changed files with 184 additions and 60 deletions
+11 -2
View File
@@ -565,7 +565,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
// Call our linkChange code once with the current state.
// Following changes are triggered via the eventbus.
cd, err := netmon.NewChangeDelta(nil, b.interfaceState, false, netMon.TailscaleInterfaceName(), false)
cd, err := netmon.NewChangeDelta(nil, b.interfaceState, false, false)
if err != nil {
b.logf("[unexpected] setting initial netmon state failed: %v", err)
} else {
@@ -5321,7 +5321,11 @@ func (b *LocalBackend) initPeerAPIListenerLocked() {
var err error
skipListen := i > 0 && isNetstack
if !skipListen {
ln, err = ps.listen(a.Addr(), b.interfaceState.TailscaleInterfaceIndex)
// We don't care about the error here. Not all platforms set this.
// If ps.listen needs it, it will check for zero values and error out.
tsIfIndex, _ := netmon.TailscaleInterfaceIndex()
ln, err = ps.listen(a.Addr(), tsIfIndex)
if err != nil {
if peerAPIListenAsync {
b.logf("[v1] possibly transient peerapi listen(%q) error, will try again on linkChange: %v", a.Addr(), err)
@@ -5329,6 +5333,11 @@ func (b *LocalBackend) initPeerAPIListenerLocked() {
// ("peerAPIListeners too low").
continue
}
// Sandboxed macOS specifically requires the interface index to be non-zero.
if version.IsSandboxedMacOS() && tsIfIndex == 0 {
b.logf("[v1] peerapi listen(%q) error: interface index is 0 on darwin; try restarting tailscaled", a.Addr())
continue
}
b.logf("[unexpected] peerapi listen(%q) error: %v", a.Addr(), err)
continue
}