From 94a053099175db3141ec83ed92cc406fff11b498 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 24 Aug 2023 11:13:41 +0000 Subject: [PATCH] cmd/cli: fix default route IP with public interface For reporting router queries, ctrld uses private IP of the default route interface. However, when the default route is conntected directly to ISP, the interface will have a public IP, and another interface with the same MAC address will be created for LAN ip. So when no private IP found for default route interface, ctrld must look at the other interface to find the corret LAN ip. --- cmd/cli/prog.go | 71 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/cmd/cli/prog.go b/cmd/cli/prog.go index 4169fb8..e1850bf 100644 --- a/cmd/cli/prog.go +++ b/cmd/cli/prog.go @@ -1,10 +1,12 @@ package cli import ( + "bytes" "errors" "fmt" "math/rand" "net" + "net/netip" "net/url" "os" "runtime" @@ -364,29 +366,58 @@ func errUrlNetworkError(err error) bool { return false } -// defaultRouteIP returns IP string of the default route if present, prefer IPv4 over IPv6. -func defaultRouteIP() string { - if dr, err := interfaces.DefaultRoute(); err == nil { - if netIface, err := netInterface(dr.InterfaceName); err == nil { - addrs, _ := netIface.Addrs() - do := func(v4 bool) net.IP { - for _, addr := range addrs { - if netIP, ok := addr.(*net.IPNet); ok && netIP.IP.IsPrivate() { - if v4 { - return netIP.IP.To4() - } - return netIP.IP - } +func ifaceFirstPrivateIP(iface *net.Interface) string { + if iface == nil { + return "" + } + do := func(addrs []net.Addr, v4 bool) net.IP { + for _, addr := range addrs { + if netIP, ok := addr.(*net.IPNet); ok && netIP.IP.IsPrivate() { + if v4 { + return netIP.IP.To4() } - return nil - } - if ip := do(true); ip != nil { - return ip.String() - } - if ip := do(false); ip != nil { - return ip.String() + return netIP.IP } } + return nil + } + addrs, _ := iface.Addrs() + if ip := do(addrs, true); ip != nil { + return ip.String() + } + if ip := do(addrs, false); ip != nil { + return ip.String() + } + return "" +} + +// defaultRouteIP returns private IP string of the default route if present, prefer IPv4 over IPv6. +func defaultRouteIP() string { + dr, err := interfaces.DefaultRoute() + if err != nil { + return "" + } + drNetIface, err := netInterface(dr.InterfaceName) + if err != nil { + return "" + } + if ip := ifaceFirstPrivateIP(drNetIface); ip != "" { + return ip + } + + // If we reach here, it means the default route interface is connected directly to ISP. + // We need to find the LAN interface with the same Mac address with drNetIface. + var drLanNetIface *net.Interface + interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) { + if i.Name == drNetIface.Name { + return + } + if bytes.Equal(i.HardwareAddr, drNetIface.HardwareAddr) { + drLanNetIface = i.Interface + } + }) + if ip := ifaceFirstPrivateIP(drLanNetIface); ip != "" { + return ip } return "" }