From 6a0fbacc28ac707b9294ebc4c49a0ac09ac0eff3 Mon Sep 17 00:00:00 2001 From: Fran Bull Date: Mon, 15 Apr 2024 11:30:00 -0700 Subject: [PATCH] appc: setting AdvertiseRoutes explicitly discards app connector routes This fixes bugs where after using the cli to set AdvertiseRoutes users were finding that they had to restart tailscaled before the app connector would advertise previously learned routes again. And seems more in line with user expectations. Fixes #11006 Signed-off-by: Fran Bull --- appc/appconnector.go | 10 ++++++++++ ipn/ipnlocal/local.go | 13 +++++++++++++ ipn/localapi/localapi.go | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/appc/appconnector.go b/appc/appconnector.go index 63e1946e7..91641a294 100644 --- a/appc/appconnector.go +++ b/appc/appconnector.go @@ -116,6 +116,16 @@ func (e *AppConnector) storeRoutesLocked() error { }) } +// ClearRoutes removes all route state from the AppConnector. +func (e *AppConnector) ClearRoutes() error { + e.mu.Lock() + defer e.mu.Unlock() + e.controlRoutes = nil + e.domains = nil + e.wildcards = nil + return e.storeRoutesLocked() +} + // UpdateDomainsAndRoutes starts an asynchronous update of the configuration // given the new domains and routes. func (e *AppConnector) UpdateDomainsAndRoutes(domains []string, routes []netip.Prefix) { diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index c3e92cdad..c67c87c20 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -3138,6 +3138,19 @@ func (b *LocalBackend) SetUseExitNodeEnabled(v bool) (ipn.PrefsView, error) { return b.editPrefsLockedOnEntry(mp, unlock) } +// MaybeClearAppConnector clears the routes from any AppConnector if +// AdvertiseRoutes has been set in the MaskedPrefs. +func (b *LocalBackend) MaybeClearAppConnector(mp *ipn.MaskedPrefs) error { + var err error + if b.appConnector != nil && mp.AdvertiseRoutesSet { + err = b.appConnector.ClearRoutes() + if err != nil { + b.logf("appc: clear routes error: %v", err) + } + } + return err +} + func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (ipn.PrefsView, error) { if mp.SetsInternal() { return ipn.PrefsView{}, errors.New("can't set Internal fields") diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 9a31dd304..a822aad69 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -1366,6 +1366,12 @@ func (h *Handler) servePrefs(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusBadRequest) return } + if err := h.b.MaybeClearAppConnector(mp); err != nil { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(resJSON{Error: err.Error()}) + return + } var err error prefs, err = h.b.EditPrefs(mp) if err != nil {