From a284cb93ffdcf207ed245f0021cb4d96715381fb Mon Sep 17 00:00:00 2001 From: Kevin Liang Date: Fri, 26 Apr 2024 17:41:21 +0000 Subject: [PATCH] add debug cmd for routeInfo --- appc/appconnector.go | 4 +++- appc/routeinfo/routeinfo.go | 1 + client/tailscale/localclient.go | 13 +++++++++++++ cmd/tailscale/cli/debug.go | 17 +++++++++++++++++ ipn/ipnlocal/local.go | 16 ++++++++++++++-- ipn/localapi/localapi.go | 22 ++++++++++++++++++++++ wgengine/watchdog.go | 2 +- 7 files changed, 71 insertions(+), 4 deletions(-) diff --git a/appc/appconnector.go b/appc/appconnector.go index 30a5a2ae3..779284a0c 100644 --- a/appc/appconnector.go +++ b/appc/appconnector.go @@ -98,6 +98,8 @@ func (e *AppConnector) UpdateDomainsAndRoutes(domains []string, routes []netip.P } func (e *AppConnector) RouteInfo() *routeinfo.RouteInfo { + // e.mu.Lock() + // defer e.mu.Unlock() if e.routeInfo == nil { ret, err := e.routeAdvertiser.ReadRouteInfo() if err != nil { @@ -113,7 +115,7 @@ func (e *AppConnector) RouteInfo() *routeinfo.RouteInfo { // RecreateRouteInfoFromStore searches from presist store for existing routeInfo for current profile, // then update local routes of routeInfo in store as the current advertised routes. -func (e *AppConnector) RecreateRouteInfoFromStore(localRoutes []netip.Prefix) { +func (e *AppConnector) RecreateRouteInfoFromStore() { e.queue.Add(func() { e.mu.Lock() defer e.mu.Unlock() diff --git a/appc/routeinfo/routeinfo.go b/appc/routeinfo/routeinfo.go index acc9441ec..4e63206ea 100644 --- a/appc/routeinfo/routeinfo.go +++ b/appc/routeinfo/routeinfo.go @@ -22,6 +22,7 @@ func NewRouteInfo() *RouteInfo { return &RouteInfo{ Control: []netip.Prefix{}, Discovered: discovered, + Wildcards: []string{}, } } diff --git a/client/tailscale/localclient.go b/client/tailscale/localclient.go index bb3b0c216..8ba3f3964 100644 --- a/client/tailscale/localclient.go +++ b/client/tailscale/localclient.go @@ -27,6 +27,7 @@ import ( "time" "go4.org/mem" + "tailscale.com/appc/routeinfo" "tailscale.com/client/tailscale/apitype" "tailscale.com/drive" "tailscale.com/envknob" @@ -722,6 +723,18 @@ func (lc *LocalClient) GetPrefs(ctx context.Context) (*ipn.Prefs, error) { return &p, nil } +func (lc *LocalClient) GetRouteInfo(ctx context.Context) (*routeinfo.RouteInfo, error) { + body, err := lc.get200(ctx, "/localapi/v0/routeInfo") + if err != nil { + return nil, err + } + var p routeinfo.RouteInfo + if err := json.Unmarshal(body, &p); err != nil { + return nil, fmt.Errorf("invalid prefs JSON: %w", err) + } + return &p, nil +} + func (lc *LocalClient) EditPrefs(ctx context.Context, mp *ipn.MaskedPrefs) (*ipn.Prefs, error) { body, err := lc.send(ctx, "PATCH", "/localapi/v0/prefs", http.StatusOK, jsonBody(mp)) if err != nil { diff --git a/cmd/tailscale/cli/debug.go b/cmd/tailscale/cli/debug.go index b8ac7bd9b..93c8e5bf7 100644 --- a/cmd/tailscale/cli/debug.go +++ b/cmd/tailscale/cli/debug.go @@ -316,6 +316,12 @@ var debugCmd = &ffcli.Command{ return fs })(), }, + { + Name: "appc-routes", + ShortUsage: "tailscale debug appc-routes", + Exec: runDebugAppc, + ShortHelp: "Prints the routes the node is advertising if it is running as an appConnector. ", + }, }, } @@ -1115,3 +1121,14 @@ func runDebugDialTypes(ctx context.Context, args []string) error { fmt.Printf("%s", body) return nil } + +func runDebugAppc(ctx context.Context, args []string) error { + prefs, err := localClient.GetRouteInfo(ctx) + if err != nil { + return err + } + j, _ := json.MarshalIndent(prefs, "", "\t") + outln(string(j)) + + return nil +} diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index f9a38774a..349c16479 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -3573,7 +3573,7 @@ func (b *LocalBackend) reconfigAppConnectorLocked(nm *netmap.NetworkMap, prefs i if b.appConnector == nil || shouldAppCStoreRoutesHasChanged { b.appConnector = appc.NewAppConnector(b.logf, b, shouldAppCStoreRoutes) if shouldAppCStoreRoutes { - b.appConnector.RecreateRouteInfoFromStore(prefs.AsStruct().AdvertiseRoutes) + b.appConnector.RecreateRouteInfoFromStore() } else if shouldAppCStoreRoutesHasChanged && !shouldAppCStoreRoutes { b.appConnector.UpdateRouteInfo(nil) } @@ -4179,7 +4179,9 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs ipn.PrefsView, oneC netfilterKind = prefs.NetfilterKind() } + b.mu.Lock() toAdvertise := b.appConnector.RouteInfo().Routes(true, true) + b.mu.Unlock() toAdvertise = append(toAdvertise, prefs.AdvertiseRoutes().AsSlice()...) rs := &router.Config{ LocalAddrs: unmapIPPrefixes(cfg.Addresses), @@ -4270,7 +4272,7 @@ func (b *LocalBackend) applyPrefsToHostinfoLocked(hi *tailcfg.Hostinfo, prefs ip var routableIPs []netip.Prefix if b.appConnector != nil { - routableIPs = append(routableIPs, b.appConnector.RouteInfo().Routes(true, true)...) + routableIPs = b.appConnector.RouteInfo().Routes(true, true) } routableIPs = append(routableIPs, prefs.AdvertiseRoutes().AsSlice()...) hi.RoutableIPs = routableIPs @@ -6096,6 +6098,16 @@ func (b *LocalBackend) StreamDebugCapture(ctx context.Context, w io.Writer) erro return nil } +func (b *LocalBackend) AppcRouteInfo() *routeinfo.RouteInfo { + b.mu.Lock() + defer b.mu.Unlock() + if b.appConnector == nil { + return nil + } + ri := b.appConnector.RouteInfo() + return ri +} + func (b *LocalBackend) GetPeerEndpointChanges(ctx context.Context, ip netip.Addr) ([]magicsock.EndpointChange, error) { pip, ok := b.e.PeerForIP(ip) if !ok { diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 2d3ca70db..9c6f2910c 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -32,6 +32,7 @@ import ( "time" "github.com/google/uuid" + "tailscale.com/appc/routeinfo" "tailscale.com/client/tailscale/apitype" "tailscale.com/clientupdate" "tailscale.com/drive" @@ -114,6 +115,7 @@ var handler = map[string]localAPIHandler{ "query-feature": (*Handler).serveQueryFeature, "reload-config": (*Handler).reloadConfig, "reset-auth": (*Handler).serveResetAuth, + "routeInfo": (*Handler).serveRouteInfo, "serve-config": (*Handler).serveServeConfig, "set-dns": (*Handler).serveSetDNS, "set-expiry-sooner": (*Handler).serveSetExpirySooner, @@ -1387,6 +1389,26 @@ func (h *Handler) servePrefs(w http.ResponseWriter, r *http.Request) { e.Encode(prefs) } +func (h *Handler) serveRouteInfo(w http.ResponseWriter, r *http.Request) { + if !h.PermitRead { + http.Error(w, "routeInfo access denied", http.StatusForbidden) + return + } + if r.Method != "GET" { + http.Error(w, "unsupported method", http.StatusMethodNotAllowed) + return + } + + var ri *routeinfo.RouteInfo + + ri = h.b.AppcRouteInfo() + w.Header().Set("Content-Type", "application/json") + e := json.NewEncoder(w) + e.SetIndent("", "\t") + e.Encode(ri) + +} + type resJSON struct { Error string `json:",omitempty"` } diff --git a/wgengine/watchdog.go b/wgengine/watchdog.go index 9ff342bcd..826966e8c 100644 --- a/wgengine/watchdog.go +++ b/wgengine/watchdog.go @@ -39,7 +39,7 @@ func NewWatchdog(e Engine) Engine { wrap: e, logf: log.Printf, fatalf: log.Fatalf, - maxWait: 45 * time.Second, + maxWait: 45 * time.Minute, inFlight: make(map[inFlightKey]time.Time), } }