@ -6,6 +6,8 @@
package netmon
import (
"errors"
"fmt"
"log"
"net"
@ -16,14 +18,26 @@ var (
lastKnownDefaultRouteIfName syncs . AtomicValue [ string ]
)
// UpdateLastKnownDefaultRouteInterface is called by ipn-go-bridge in the iOS app when
// UpdateLastKnownDefaultRouteInterface is called by ipn-go-bridge from apple network extensions when
// our NWPathMonitor instance detects a network path transition.
func UpdateLastKnownDefaultRouteInterface ( ifName string ) {
if ifName == "" {
return
}
if old := lastKnownDefaultRouteIfName . Swap ( ifName ) ; old != ifName {
log . Printf ( "defaultroute_darwin: update from Swift, ifName = %s (was %s)" , ifName , old )
interfaces , err := netInterfaces ( )
if err != nil {
log . Printf ( "defaultroute_darwin: UpdateLastKnownDefaultRouteInterface could not get interfaces: %v" , err )
return
}
netif , err := getInterfaceByName ( ifName , interfaces )
if err != nil {
log . Printf ( "defaultroute_darwin: UpdateLastKnownDefaultRouteInterface could not find interface index for %s: %v" , ifName , err )
return
}
log . Printf ( "defaultroute_darwin: updated last known default if from OS, ifName = %s index: %d (was %s)" , ifName , netif . Index , old )
}
}
@ -40,57 +54,69 @@ func defaultRoute() (d DefaultRouteDetails, err error) {
//
// If for any reason the Swift machinery didn't work and we don't get any updates, we will
// fallback to the BSD logic.
osRoute , osRouteErr := OSDefaultRoute ( )
if osRouteErr == nil {
// If we got a valid interface from the OS, use it.
d . InterfaceName = osRoute . InterfaceName
d . InterfaceIndex = osRoute . InterfaceIndex
return d , nil
}
// Fallback to the BSD logic
idx , err := DefaultRouteInterfaceIndex ( )
if err != nil {
return d , err
}
iface , err := net . InterfaceByIndex ( idx )
if err != nil {
return d , err
}
d . InterfaceName = iface . Name
d . InterfaceIndex = idx
return d , nil
}
// OSDefaultRoute returns the DefaultRouteDetails for the default interface as provided by the OS
// via UpdateLastKnownDefaultRouteInterface. If UpdateLastKnownDefaultRouteInterface has not been called,
// the interface name is not valid, or we cannot find its index, an error is returned.
func OSDefaultRoute ( ) ( d DefaultRouteDetails , err error ) {
// Did Swift set lastKnownDefaultRouteInterface? If so, we should use it and don't bother
// with anything else. However, for sanity, do check whether Swift gave us with an interface
// that exists, is up, and has an address and is not the tunnel itself.
if swiftIfName := lastKnownDefaultRouteIfName . Load ( ) ; swiftIfName != "" {
// Start by getting all available interfaces.
interfaces , err := netInterfaces ( )
if err != nil {
log . Printf ( "defaultroute_darwin: could not get interfaces: %v" , err )
return d , ErrNoGatewayIndexFound
return d , err
}
getInterfaceByName := func ( name string ) * Interface {
if ifc , err := getInterfaceByName ( swiftIfName , interfaces ) ; err == nil {
d . InterfaceName = ifc . Name
d . InterfaceIndex = ifc . Index
return d , nil
}
}
err = errors . New ( "no os provided default route interface found" )
return d , err
}
func getInterfaceByName ( name string , interfaces [ ] Interface ) ( * Interface , error ) {
for _ , ifc := range interfaces {
if ifc . Name != name {
continue
}
if ! ifc . IsUp ( ) {
log . Printf ( "defaultroute_darwin: %s is down" , name )
return nil
return nil , fmt . Errorf ( "defaultroute_darwin: %s is down" , name )
}
addrs , _ := ifc . Addrs ( )
if len ( addrs ) == 0 {
log . Printf ( "defaultroute_darwin: %s has no addresses" , name )
return nil
}
return & ifc
}
return nil
}
// Did Swift set lastKnownDefaultRouteInterface? If so, we should use it and don't bother
// with anything else. However, for sanity, do check whether Swift gave us with an interface
// that exists, is up, and has an address.
if swiftIfName := lastKnownDefaultRouteIfName . Load ( ) ; swiftIfName != "" {
ifc := getInterfaceByName ( swiftIfName )
if ifc != nil {
d . InterfaceName = ifc . Name
d . InterfaceIndex = ifc . Index
return d , nil
return nil , fmt . Errorf ( "defaultroute_darwin: %s has no addresses" , name )
}
return & ifc , nil
}
// Fallback to the BSD logic
idx , err := DefaultRouteInterfaceIndex ( )
if err != nil {
return d , err
}
iface , err := net . InterfaceByIndex ( idx )
if err != nil {
return d , err
}
d . InterfaceName = iface . Name
d . InterfaceIndex = idx
return d , nil
return nil , errors . New ( "no interfaces found" )
}