diff --git a/net/interfaces/interfaces.go b/net/interfaces/interfaces.go index 6fe24c651..d1fc3be56 100644 --- a/net/interfaces/interfaces.go +++ b/net/interfaces/interfaces.go @@ -557,6 +557,15 @@ func LikelyHomeRouterIP() (gateway, myIP netip.Addr, ok bool) { // always return an IPv4 address. return } + + // If this prefix ("interface") doesn't contain the gateway, + // then we skip it; this can happen if we have multiple valid + // interfaces and the interface with the route to the internet + // is ordered after another valid+running interface. + if !pfx.Contains(gateway) { + return + } + if gateway.IsPrivate() && ip.IsPrivate() { myIP = ip ok = true diff --git a/net/interfaces/interfaces_test.go b/net/interfaces/interfaces_test.go index 98448dbb2..40e9d42d2 100644 --- a/net/interfaces/interfaces_test.go +++ b/net/interfaces/interfaces_test.go @@ -125,6 +125,76 @@ func TestLikelyHomeRouterIP(t *testing.T) { }) } +// https://github.com/tailscale/tailscale/issues/10466 +func TestLikelyHomeRouterIP_Prefix(t *testing.T) { + ipnet := func(s string) net.Addr { + ip, ipnet, err := net.ParseCIDR(s) + ipnet.IP = ip + if err != nil { + t.Fatal(err) + } + return ipnet + } + + mockInterfaces := []Interface{ + // Valid and running interface that doesn't have a route to the + // internet, and comes before the interface that does. + { + Interface: &net.Interface{ + Index: 1, + MTU: 1500, + Name: "docker0", + Flags: net.FlagUp | + net.FlagBroadcast | + net.FlagMulticast | + net.FlagRunning, + }, + AltAddrs: []net.Addr{ + ipnet("172.17.0.0/16"), + }, + }, + + // Fake interface with a gateway to the internet. + { + Interface: &net.Interface{ + Index: 2, + MTU: 1500, + Name: "fake0", + Flags: net.FlagUp | + net.FlagBroadcast | + net.FlagMulticast | + net.FlagRunning, + }, + AltAddrs: []net.Addr{ + ipnet("192.168.7.100/24"), + }, + }, + } + + // Mock out the responses from netInterfaces() + tstest.Replace(t, &altNetInterfaces, func() ([]Interface, error) { + return mockInterfaces, nil + }) + + // Mock out the likelyHomeRouterIP to return a known gateway. + tstest.Replace(t, &likelyHomeRouterIP, func() (netip.Addr, bool) { + return netip.MustParseAddr("192.168.7.1"), true + }) + + gw, my, ok := LikelyHomeRouterIP() + if !ok { + t.Fatal("expected success") + } + t.Logf("myIP = %v; gw = %v", my, gw) + + if want := netip.MustParseAddr("192.168.7.1"); gw != want { + t.Errorf("got gateway %v; want %v", gw, want) + } + if want := netip.MustParseAddr("192.168.7.100"); my != want { + t.Errorf("got self IP %v; want %v", my, want) + } +} + func TestLikelyHomeRouterIP_NoMocks(t *testing.T) { // Verify that this works properly when called on a real live system, // without any mocks.