ipn/ipnlocal: re-advertise appc routes on startup, take 2 (#14740)

* Reapply "ipn/ipnlocal: re-advertise appc routes on startup (#14609)"

This reverts commit 51adaec35a.

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>

* ipn/ipnlocal: fix a deadlock in readvertiseAppConnectorRoutes

Don't hold LocalBackend.mu while calling the methods of
appc.AppConnector. Those methods could call back into LocalBackend and
try to acquire it's mutex.

Fixes https://github.com/tailscale/corp/issues/25965
Fixes #14606

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>

---------

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
Andrew Lytvynov
2025-01-22 16:50:25 -08:00
committed by GitHub
parent 3dabea0fc2
commit 3fb8a1f6bf
2 changed files with 87 additions and 3 deletions
+47
View File
@@ -1501,6 +1501,53 @@ func TestReconfigureAppConnector(t *testing.T) {
}
}
func TestBackfillAppConnectorRoutes(t *testing.T) {
// Create backend with an empty app connector.
b := newTestBackend(t)
if err := b.Start(ipn.Options{}); err != nil {
t.Fatal(err)
}
if _, err := b.EditPrefs(&ipn.MaskedPrefs{
Prefs: ipn.Prefs{
AppConnector: ipn.AppConnectorPrefs{Advertise: true},
},
AppConnectorSet: true,
}); err != nil {
t.Fatal(err)
}
b.reconfigAppConnectorLocked(b.netMap, b.pm.prefs)
// Smoke check that AdvertiseRoutes doesn't have the test IP.
ip := netip.MustParseAddr("1.2.3.4")
routes := b.Prefs().AdvertiseRoutes().AsSlice()
if slices.Contains(routes, netip.PrefixFrom(ip, ip.BitLen())) {
t.Fatalf("AdvertiseRoutes %v on a fresh backend already contains advertised route for %v", routes, ip)
}
// Store the test IP in profile data, but not in Prefs.AdvertiseRoutes.
b.ControlKnobs().AppCStoreRoutes.Store(true)
if err := b.storeRouteInfo(&appc.RouteInfo{
Domains: map[string][]netip.Addr{
"example.com": {ip},
},
}); err != nil {
t.Fatal(err)
}
// Mimic b.authReconfigure for the app connector bits.
b.mu.Lock()
b.reconfigAppConnectorLocked(b.netMap, b.pm.prefs)
b.mu.Unlock()
b.readvertiseAppConnectorRoutes()
// Check that Prefs.AdvertiseRoutes got backfilled with routes stored in
// profile data.
routes = b.Prefs().AdvertiseRoutes().AsSlice()
if !slices.Contains(routes, netip.PrefixFrom(ip, ip.BitLen())) {
t.Fatalf("AdvertiseRoutes %v was not backfilled from stored app connector routes with %v", routes, ip)
}
}
func resolversEqual(t *testing.T, a, b []*dnstype.Resolver) bool {
if a == nil && b == nil {
return true