|
|
@@ -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
|
|
|
+ }
|
|
|
|
|
|
- // Start by getting all available interfaces.
|
|
|
- interfaces, err := netInterfaces()
|
|
|
+ // Fallback to the BSD logic
|
|
|
+ idx, err := DefaultRouteInterfaceIndex()
|
|
|
if err != nil {
|
|
|
- log.Printf("defaultroute_darwin: could not get interfaces: %v", err)
|
|
|
- return d, ErrNoGatewayIndexFound
|
|
|
+ return d, err
|
|
|
}
|
|
|
-
|
|
|
- getInterfaceByName := func(name string) *Interface {
|
|
|
- for _, ifc := range interfaces {
|
|
|
- if ifc.Name != name {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- if !ifc.IsUp() {
|
|
|
- log.Printf("defaultroute_darwin: %s is down", name)
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- addrs, _ := ifc.Addrs()
|
|
|
- if len(addrs) == 0 {
|
|
|
- log.Printf("defaultroute_darwin: %s has no addresses", name)
|
|
|
- return nil
|
|
|
- }
|
|
|
- return &ifc
|
|
|
- }
|
|
|
- return nil
|
|
|
+ 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.
|
|
|
+ // that exists, is up, and has an address and is not the tunnel itself.
|
|
|
if swiftIfName := lastKnownDefaultRouteIfName.Load(); swiftIfName != "" {
|
|
|
- ifc := getInterfaceByName(swiftIfName)
|
|
|
- if ifc != nil {
|
|
|
+ // Start by getting all available interfaces.
|
|
|
+ interfaces, err := netInterfaces()
|
|
|
+ if err != nil {
|
|
|
+ log.Printf("defaultroute_darwin: could not get interfaces: %v", err)
|
|
|
+ return d, err
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+}
|
|
|
|
|
|
- // 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
|
|
|
+func getInterfaceByName(name string, interfaces []Interface) (*Interface, error) {
|
|
|
+ for _, ifc := range interfaces {
|
|
|
+ if ifc.Name != name {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if !ifc.IsUp() {
|
|
|
+ return nil, fmt.Errorf("defaultroute_darwin: %s is down", name)
|
|
|
+ }
|
|
|
+
|
|
|
+ addrs, _ := ifc.Addrs()
|
|
|
+ if len(addrs) == 0 {
|
|
|
+ return nil, fmt.Errorf("defaultroute_darwin: %s has no addresses", name)
|
|
|
+ }
|
|
|
+ return &ifc, nil
|
|
|
}
|
|
|
- d.InterfaceName = iface.Name
|
|
|
- d.InterfaceIndex = idx
|
|
|
- return d, nil
|
|
|
+ return nil, errors.New("no interfaces found")
|
|
|
}
|