macOS: ignore on-link default routes when picking the physical uplink
check / check (push) Successful in 2m32s

A VM with NAT networking makes vmnet install a default route scoped to
its bridge (bridge100) with no gateway. The detector counted it as a
second physical default route and refused to start:

    multiple physical default-route interfaces found (bridge100, en0)

Scoped routes cannot simply be dropped: while a VPN holds the default
route, the physical interface keeps only a default route scoped to
itself. That route still names its gateway (flag G). The on-link one
does not, so a physical default route now requires a next-hop gateway.

parseNetstat records a gateway only when the G flag is set; a link#N
gateway column is the interface itself, not a next hop.
This commit is contained in:
sneak committed 2026-11-06 19:08:12 -08:00
1 parent 7686440108
commit 6dc3b9e395
3 files changed
+138 -23

No files matched your search

+6 -1
View File
@@ -55,7 +55,12 @@ primary pane alongside the physical interface. A tunnel that is up without the
default route is ignored, whatever its address: Tailscale without an exit node,
or a tunnel a disconnected client left behind. A default route scoped to the
tunnel alone (flag `I`) does not count either. With no VPN connected, only the
physical interface is monitored. Interface names are detected on macOS; the
physical interface is monitored. The physical interface is the one non-tunnel
`default` row with a next-hop gateway (flag `G`); it keeps that gateway even
while a VPN holds the default route and its own default is scoped. An on-link
`default` row with no gateway, such as the one vmnet or Internet Sharing
installs on `bridge100` for a VM's subnet, is ignored. Interface names are
detected on macOS; the
`--ifaceA`/`--ifaceB` flags are not used there, but `--labelA`/`--labelB` still
set the pane labels.