@ -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"
)
@ -22,12 +23,12 @@ func UpdateLastKnownDefaultRouteInterface(ifName string) {
return
}
if old := lastKnownDefaultRouteIfName . Swap ( ifName ) ; old != ifName {
log . Printf ( "defaultroute_ ios : update from Swift, ifName = %s (was %s)", ifName , old )
log . Printf ( "defaultroute_ darwin : update from Swift, ifName = %s (was %s)", ifName , old )
}
}
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,16 +38,13 @@ 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 ( )
if err != nil {
log . Printf ( "defaultroute_ ios : could not get interfaces: %v", err )
log . Printf ( "defaultroute_ darwin : could not get interfaces: %v", err )
return d , ErrNoGatewayIndexFound
}
@ -57,13 +55,13 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
}
if ! ifc . IsUp ( ) {
log . Printf ( "defaultroute_ ios : %s is down", name )
log . Printf ( "defaultroute_ darwin : %s is down", name )
return nil
}
addrs , _ := ifc . Addrs ( )
if len ( addrs ) == 0 {
log . Printf ( "defaultroute_ ios : %s has no addresses", name )
log . Printf ( "defaultroute_ darwin : %s has no addresses", name )
return nil
}
return & ifc
@ -79,30 +77,22 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
if ifc != nil {
d . InterfaceName = ifc . Name
d . InterfaceIndex = ifc . Index
log . Printf ( "defaultroute_darwin: using lastKnownDefaultRouteInterface %s %v" , d . InterfaceName , d . InterfaceIndex )
return d , nil
}
}
// 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
idx , err := DefaultRouteInterfaceIndex ( )
if err != nil {
return d , err
}
// 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
iface , err := net . InterfaceByIndex ( idx )
if err != nil {
return d , err
}
log . Println ( "defaultroute_ios: no running interfaces available" )
return d , ErrNoGatewayIndexFound
d . InterfaceName = iface . Name
d . InterfaceIndex = idx
log . Printf ( "defaultroute_darwin: using table dervied default if %s %v" , d . InterfaceName , d . InterfaceIndex )
return d , nil
}