diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 8cc0e3809..b17a3a4fd 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1020,7 +1020,12 @@ func (b *LocalBackend) updateFilter(netMap *netmap.NetworkMap, prefs *ipn.Prefs) // wifi": you get internet access, but to additionally // get LAN access the LAN(s) need to be offered // explicitly as well. - s, err := shrinkDefaultRoute(r) + localInterfaceRoutes, hostIPs, err := interfaceRoutes() + if err != nil { + b.logf("getting local interface routes: %v", err) + continue + } + s, err := shrinkDefaultRoute(r, localInterfaceRoutes, hostIPs) if err != nil { b.logf("computing default route filter: %v", err) continue @@ -1164,17 +1169,14 @@ func interfaceRoutes() (ips *netaddr.IPSet, hostIPs []netaddr.IP, err error) { } // shrinkDefaultRoute returns an IPSet representing the IPs in route, -// minus those in removeFromDefaultRoute and local interface subnets. -func shrinkDefaultRoute(route netaddr.IPPrefix) (*netaddr.IPSet, error) { - interfaceRoutes, hostIPs, err := interfaceRoutes() - if err != nil { - return nil, err - } +// minus those in removeFromDefaultRoute and localInterfaceRoutes, +// plus the IPs in hostIPs. +func shrinkDefaultRoute(route netaddr.IPPrefix, localInterfaceRoutes *netaddr.IPSet, hostIPs []netaddr.IP) (*netaddr.IPSet, error) { var b netaddr.IPSetBuilder // Add the default route. b.AddPrefix(route) // Remove the local interface routes. - b.RemoveSet(interfaceRoutes) + b.RemoveSet(localInterfaceRoutes) // Having removed all the LAN subnets, re-add the hosts's own // IPs. It's fine for clients to connect to an exit node's public diff --git a/ipn/ipnlocal/local_test.go b/ipn/ipnlocal/local_test.go index 2f3769e93..271b83bd7 100644 --- a/ipn/ipnlocal/local_test.go +++ b/ipn/ipnlocal/local_test.go @@ -178,9 +178,31 @@ func TestShrinkDefaultRoute(t *testing.T) { }, } + // Construct a fake local network environment to make this test hermetic. + // localInterfaceRoutes and hostIPs would normally come from calling interfaceRoutes, + // and localAddresses would normally come from calling interfaces.LocalAddresses. + var b netaddr.IPSetBuilder + for _, c := range []string{"127.0.0.0/8", "192.168.9.0/24", "fe80::/32"} { + p := netaddr.MustParseIPPrefix(c) + b.AddPrefix(p) + } + localInterfaceRoutes, err := b.IPSet() + if err != nil { + t.Fatal(err) + } + hostIPs := []netaddr.IP{ + netaddr.MustParseIP("127.0.0.1"), + netaddr.MustParseIP("192.168.9.39"), + netaddr.MustParseIP("fe80::1"), + netaddr.MustParseIP("fe80::437d:feff:feca:49a7"), + } + localAddresses := []netaddr.IP{ + netaddr.MustParseIP("192.168.9.39"), + } + for _, test := range tests { def := netaddr.MustParseIPPrefix(test.route) - got, err := shrinkDefaultRoute(def) + got, err := shrinkDefaultRoute(def, localInterfaceRoutes, hostIPs) if err != nil { t.Fatalf("shrinkDefaultRoute(%q): %v", test.route, err) } @@ -194,11 +216,7 @@ func TestShrinkDefaultRoute(t *testing.T) { t.Errorf("shrink(%q).Contains(%v) = true, want false", test.route, ip) } } - ips, _, err := interfaces.LocalAddresses() - if err != nil { - t.Fatal(err) - } - for _, ip := range ips { + for _, ip := range localAddresses { want := test.localIPFn(ip) if gotContains := got.Contains(ip); gotContains != want { t.Errorf("shrink(%q).Contains(%v) = %v, want %v", test.route, ip, gotContains, want)