@ -1,12 +1,13 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-License-Identifier: BSD-3-Clause
//go:build ios
//go:build darwin || ios
package netmon
package netmon
import (
import (
"log"
"log"
"net"
"tailscale.com/syncs"
"tailscale.com/syncs"
)
)
@ -22,12 +23,12 @@ func UpdateLastKnownDefaultRouteInterface(ifName string) {
return
return
}
}
if old := lastKnownDefaultRouteIfName . Swap ( ifName ) ; old != ifName {
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 ) {
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.
// 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
// 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
// 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
// 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.
// 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
// If for any reason the Swift machinery didn't work and we don't get any updates, we will
// we also have some fallback logic: we try finding a hardcoded Wi-Fi interface called en0.
// fallback to the BSD logic.
// 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.
// Start by getting all available interfaces.
// Start by getting all available interfaces.
interfaces , err := netInterfaces ( )
interfaces , err := netInterfaces ( )
if err != nil {
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
return d , ErrNoGatewayIndexFound
}
}
@ -57,13 +55,13 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
}
}
if ! ifc . IsUp ( ) {
if ! ifc . IsUp ( ) {
log . Printf ( "defaultroute_ ios : %s is down", name )
log . Printf ( "defaultroute_ darwin : %s is down", name )
return nil
return nil
}
}
addrs , _ := ifc . Addrs ( )
addrs , _ := ifc . Addrs ( )
if len ( addrs ) == 0 {
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 nil
}
}
return & ifc
return & ifc
@ -79,30 +77,22 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
if ifc != nil {
if ifc != nil {
d . InterfaceName = ifc . Name
d . InterfaceName = ifc . Name
d . InterfaceIndex = ifc . Index
d . InterfaceIndex = ifc . Index
log . Printf ( "defaultroute_darwin: using lastKnownDefaultRouteInterface %s %v" , d . InterfaceName , d . InterfaceIndex )
return d , nil
return d , nil
}
}
}
}
// Start of our fallback logic if Swift didn't give us an interface name, or gave us an invalid
// Fallback to the BSD logic
// one.
idx , err := DefaultRouteInterfaceIndex ( )
// We start by attempting to use the Wi-Fi interface, which on iPhone is always called en0.
if err != nil {
enZeroIf := getInterfaceByName ( "en0" )
return d , err
if enZeroIf != nil {
log . Println ( "defaultroute_ios: using en0 (fallback)" )
d . InterfaceName = enZeroIf . Name
d . InterfaceIndex = enZeroIf . Index
return d , nil
}
}
iface , err := net . InterfaceByIndex ( idx )
// Did it not work? Let's try with Cellular (pdp_ip0).
if err != nil {
cellIf := getInterfaceByName ( "pdp_ip0" )
return d , err
if cellIf != nil {
log . Println ( "defaultroute_ios: using pdp_ip0 (fallback)" )
d . InterfaceName = cellIf . Name
d . InterfaceIndex = cellIf . Index
return d , nil
}
}
d . InterfaceName = iface . Name
log . Println ( "defaultroute_ios: no running interfaces available" )
d . InterfaceIndex = idx
return d , ErrNoGatewayIndexFound
log . Printf ( "defaultroute_darwin: using table dervied default if %s %v" , d . InterfaceName , d . InterfaceIndex )
return d , nil
}
}