net/interfaces: swap to ios swift based defaultroute on macos

Updates tailscale/corp#18960

iOS uses Apple's NetworkMonitor to track the default interface and
there's no reason we can't also use this on  macOS (for the same reasons
noted in the comments).  This eliminates the need to
load and parse the routing table in the critical path where failure
is not an option.

A slight modification here (on both platforms) to fallback to the default
BSD logic in the unhappy-path rather than making assumptions that
may not hold.  If netmon is eventually parsing AF_ROUTE and able
to give a consistently correct answer for the  default interface index,
we can fall back to that, or even eliminate the need to resort to
Swift on both platforms.
Jonathan Nobels 3 weeks ago
parent 1e6cdb7d86
commit ad69da6f67

@ -1,11 +1,11 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Common code for FreeBSD and Darwin. This might also work on other
// Common code for FreeBSD. This might also work on other
// BSD systems (e.g. OpenBSD) but has not been tested.
// Not used on iOS. See defaultroute_ios.go.
// Not used on iOS or macOS. See defaultroute_darwin.go.
//go:build !ios && (darwin || freebsd)
//go:build freebsd
package netmon

@ -1,12 +1,13 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build ios
//go:build darwin || ios
package netmon
import (
"log"
"net"
"tailscale.com/syncs"
)
@ -27,7 +28,7 @@ func UpdateLastKnownDefaultRouteInterface(ifName string) {
}
func defaultRoute() (d DefaultRouteDetails, err error) {
// We cannot rely on the delegated interface data on iOS. The NetworkExtension framework
// We cannot rely on the delegated interface data on darwin. The NetworkExtension framework
// seems to set the delegate interface only once, upon the *creation* of the VPN tunnel.
// If a network transition (e.g. from Wi-Fi to Cellular) happens while the tunnel is
// connected, it will be ignored and we will still try to set Wi-Fi as the default route
@ -37,11 +38,8 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
// the interface name of the first currently satisfied network path. Our Swift code will
// call into `UpdateLastKnownDefaultRouteInterface`, so we can rely on that when it is set.
//
// If for any reason the Swift machinery didn't work and we don't get any updates, here
// we also have some fallback logic: we try finding a hardcoded Wi-Fi interface called en0.
// If en0 is down, we fall back to cellular (pdp_ip0) as a last resort. This doesn't handle
// all edge cases like USB-Ethernet adapters or multiple Ethernet interfaces, but is good
// enough to ensure connectivity isn't broken.
// If for any reason the Swift machinery didn't work and we don't get any updates, we will
// fallback to the BSD logic.
// Start by getting all available interfaces.
interfaces, err := netInterfaces()
@ -83,26 +81,17 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
}
}
// Start of our fallback logic if Swift didn't give us an interface name, or gave us an invalid
// one.
// We start by attempting to use the Wi-Fi interface, which on iPhone is always called en0.
enZeroIf := getInterfaceByName("en0")
if enZeroIf != nil {
log.Println("defaultroute_ios: using en0 (fallback)")
d.InterfaceName = enZeroIf.Name
d.InterfaceIndex = enZeroIf.Index
return d, nil
}
// Fallback to the BSD logic
// Did it not work? Let's try with Cellular (pdp_ip0).
cellIf := getInterfaceByName("pdp_ip0")
if cellIf != nil {
log.Println("defaultroute_ios: using pdp_ip0 (fallback)")
d.InterfaceName = cellIf.Name
d.InterfaceIndex = cellIf.Index
return d, nil
idx, err := DefaultRouteInterfaceIndex()
if err != nil {
return d, err
}
log.Println("defaultroute_ios: no running interfaces available")
return d, ErrNoGatewayIndexFound
iface, err := net.InterfaceByIndex(idx)
if err != nil {
return d, err
}
d.InterfaceName = iface.Name
d.InterfaceIndex = idx
return d, nil
}
Loading…
Cancel
Save