ipn/ipnlocal: Fill out self IP addresses in StatusWithoutPeers

Fixes tailscale/tailscale#4311
Signed-off-by: Claire Wang <claire@tailscale.com>
This commit is contained in:
Claire Wang 2022-11-30 11:11:07 -05:00
parent 47002d93a3
commit 0ce4695e13
2 changed files with 69 additions and 28 deletions

View File

@ -570,6 +570,27 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func(*ipnstate.StatusBuilder)) { func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func(*ipnstate.StatusBuilder)) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() 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) { sb.MutateStatus(func(s *ipnstate.Status) {
s.Version = version.Long s.Version = version.Long
s.BackendState = b.state.String() 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.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
s.CurrentTailnet.MagicDNSEnabled = b.netMap.DNS.Proxied s.CurrentTailnet.MagicDNSEnabled = b.netMap.DNS.Proxied
s.CurrentTailnet.Name = b.netMap.Domain s.CurrentTailnet.Name = b.netMap.Domain
s.TailscaleIPs = s.Self.TailscaleIPs
if prefs := b.pm.CurrentPrefs(); prefs.Valid() { if prefs := b.pm.CurrentPrefs(); prefs.Valid() {
if !prefs.RouteAll() && b.netMap.AnyPeersAdvertiseRoutes() { if !prefs.RouteAll() && b.netMap.AnyPeersAdvertiseRoutes() {
s.Health = append(s.Health, healthmsg.WarnAcceptRoutesOff) 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: hostinfo, and its networkinfo
// TODO: EngineStatus copy (and deprecate it?) // TODO: EngineStatus copy (and deprecate it?)
if extraLocked != nil { if extraLocked != nil {
extraLocked(sb) extraLocked(sb)
} }
@ -660,16 +661,9 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
if p.LastSeen != nil { if p.LastSeen != nil {
lastSeen = *p.LastSeen 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{ ps := &ipnstate.PeerStatus{
InNetworkMap: true, InNetworkMap: true,
UserID: p.User, UserID: p.User,
TailscaleIPs: tailscaleIPs,
HostName: p.Hostinfo.Hostname(), HostName: p.Hostinfo.Hostname(),
DNSName: p.Name, DNSName: p.Name,
OS: p.Hostinfo.OS(), OS: p.Hostinfo.OS(),
@ -707,6 +701,12 @@ func peerStatusFromNode(ps *ipnstate.PeerStatus, n *tailcfg.Node) {
v := views.IPPrefixSliceOf(n.PrimaryRoutes) v := views.IPPrefixSliceOf(n.PrimaryRoutes)
ps.PrimaryRoutes = &v 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. // WhoIs reports the node and user who owns the node with the given IP:port.

View File

@ -14,11 +14,13 @@
"time" "time"
"go4.org/netipx" "go4.org/netipx"
"tailscale.com/control/controlclient"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/store/mem" "tailscale.com/ipn/store/mem"
"tailscale.com/net/interfaces" "tailscale.com/net/interfaces"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tstest"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/wgengine" "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 // legacyBackend was the interface between Tailscale frontends
// (e.g. cmd/tailscale, iOS/MacOS/Windows GUIs) and the tailscale // (e.g. cmd/tailscale, iOS/MacOS/Windows GUIs) and the tailscale
// backend (e.g. cmd/tailscaled) running on the same machine. // backend (e.g. cmd/tailscaled) running on the same machine.