From 0ce4695e131c58a299a33e02cadba3374e5e4a4a Mon Sep 17 00:00:00 2001 From: Claire Wang Date: Wed, 30 Nov 2022 11:11:07 -0500 Subject: [PATCH] ipn/ipnlocal: Fill out self IP addresses in StatusWithoutPeers Fixes tailscale/tailscale#4311 Signed-off-by: Claire Wang --- ipn/ipnlocal/local.go | 56 +++++++++++++++++++------------------- ipn/ipnlocal/local_test.go | 41 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 27095a85c..0afab3b87 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -570,6 +570,27 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) { func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func(*ipnstate.StatusBuilder)) { b.mu.Lock() defer b.mu.Unlock() + sb.MutateSelfStatus(func(ss *ipnstate.PeerStatus) { + ss.Online = health.GetInPollNetMap() + if b.netMap != nil { + ss.InNetworkMap = true + ss.HostName = b.netMap.Hostinfo.Hostname + ss.DNSName = b.netMap.Name + ss.UserID = b.netMap.User + if sn := b.netMap.SelfNode; sn != nil { + peerStatusFromNode(ss, sn) + if c := sn.Capabilities; len(c) > 0 { + ss.Capabilities = append([]string(nil), c...) + } + } + } else { + ss.HostName, _ = os.Hostname() + } + for _, pln := range b.peerAPIListeners { + ss.PeerAPIURL = append(ss.PeerAPIURL, pln.urlStr) + } + }) + // MutateSelfStatus is called before MutateStatus, so that way the self node's tailscale ips can be assigned to ipn state's tailscale ips. sb.MutateStatus(func(s *ipnstate.Status) { s.Version = version.Long s.BackendState = b.state.String() @@ -599,6 +620,7 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func s.CurrentTailnet.MagicDNSSuffix = b.netMap.MagicDNSSuffix() s.CurrentTailnet.MagicDNSEnabled = b.netMap.DNS.Proxied s.CurrentTailnet.Name = b.netMap.Domain + s.TailscaleIPs = s.Self.TailscaleIPs if prefs := b.pm.CurrentPrefs(); prefs.Valid() { if !prefs.RouteAll() && b.netMap.AnyPeersAdvertiseRoutes() { s.Health = append(s.Health, healthmsg.WarnAcceptRoutesOff) @@ -619,29 +641,8 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func } } }) - sb.MutateSelfStatus(func(ss *ipnstate.PeerStatus) { - ss.Online = health.GetInPollNetMap() - if b.netMap != nil { - ss.InNetworkMap = true - ss.HostName = b.netMap.Hostinfo.Hostname - ss.DNSName = b.netMap.Name - ss.UserID = b.netMap.User - if sn := b.netMap.SelfNode; sn != nil { - peerStatusFromNode(ss, sn) - if c := sn.Capabilities; len(c) > 0 { - ss.Capabilities = append([]string(nil), c...) - } - } - } else { - ss.HostName, _ = os.Hostname() - } - for _, pln := range b.peerAPIListeners { - ss.PeerAPIURL = append(ss.PeerAPIURL, pln.urlStr) - } - }) // TODO: hostinfo, and its networkinfo // TODO: EngineStatus copy (and deprecate it?) - if extraLocked != nil { extraLocked(sb) } @@ -660,16 +661,9 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) { if p.LastSeen != nil { lastSeen = *p.LastSeen } - var tailscaleIPs = make([]netip.Addr, 0, len(p.Addresses)) - for _, addr := range p.Addresses { - if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) { - tailscaleIPs = append(tailscaleIPs, addr.Addr()) - } - } ps := &ipnstate.PeerStatus{ InNetworkMap: true, UserID: p.User, - TailscaleIPs: tailscaleIPs, HostName: p.Hostinfo.Hostname(), DNSName: p.Name, OS: p.Hostinfo.OS(), @@ -707,6 +701,12 @@ func peerStatusFromNode(ps *ipnstate.PeerStatus, n *tailcfg.Node) { v := views.IPPrefixSliceOf(n.PrimaryRoutes) ps.PrimaryRoutes = &v } + ps.TailscaleIPs = make([]netip.Addr, 0, len(n.Addresses)) + for _, addr := range n.Addresses { + if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) { + ps.TailscaleIPs = append(ps.TailscaleIPs, addr.Addr()) + } + } } // WhoIs reports the node and user who owns the node with the given IP:port. diff --git a/ipn/ipnlocal/local_test.go b/ipn/ipnlocal/local_test.go index 6d46491bc..4c462f75d 100644 --- a/ipn/ipnlocal/local_test.go +++ b/ipn/ipnlocal/local_test.go @@ -14,11 +14,13 @@ "time" "go4.org/netipx" + "tailscale.com/control/controlclient" "tailscale.com/ipn" "tailscale.com/ipn/store/mem" "tailscale.com/net/interfaces" "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" + "tailscale.com/tstest" "tailscale.com/types/logger" "tailscale.com/types/netmap" "tailscale.com/wgengine" @@ -744,6 +746,45 @@ func TestPacketFilterPermitsUnlockedNodes(t *testing.T) { } +func TestStatusWithoutPeers(t *testing.T) { + logf := tstest.WhileTestRunningLogger(t) + store := new(testStateStorage) + e, err := wgengine.NewFakeUserspaceEngine(logf, 0) + if err != nil { + t.Fatalf("NewFakeUserspaceEngine: %v", err) + } + t.Cleanup(e.Close) + + b, err := NewLocalBackend(logf, "logid", store, "", nil, e, 0) + if err != nil { + t.Fatalf("NewLocalBackend: %v", err) + } + var cc *mockControl + b.SetControlClientGetterForTesting(func(opts controlclient.Options) (controlclient.Client, error) { + cc = newClient(t, opts) + + t.Logf("ccGen: new mockControl.") + cc.called("New") + return cc, nil + }) + b.Start(ipn.Options{}) + b.Login(nil) + cc.send(nil, "", false, &netmap.NetworkMap{ + MachineStatus: tailcfg.MachineAuthorized, + Addresses: ipps("100.101.101.101"), + SelfNode: &tailcfg.Node{ + Addresses: ipps("100.101.101.101"), + }, + }) + got := b.StatusWithoutPeers() + if got.TailscaleIPs == nil { + t.Errorf("got nil, expected TailscaleIPs value to not be nil") + } + if !reflect.DeepEqual(got.TailscaleIPs, got.Self.TailscaleIPs) { + t.Errorf("got %v, expected %v", got.TailscaleIPs, got.Self.TailscaleIPs) + } +} + // legacyBackend was the interface between Tailscale frontends // (e.g. cmd/tailscale, iOS/MacOS/Windows GUIs) and the tailscale // backend (e.g. cmd/tailscaled) running on the same machine.