diff --git a/cmd/tailscale/cli/status.go b/cmd/tailscale/cli/status.go index d6245947b..8e40067a7 100644 --- a/cmd/tailscale/cli/status.go +++ b/cmd/tailscale/cli/status.go @@ -14,7 +14,6 @@ import ( "net/http" "os" "strings" - "time" "github.com/peterbourgon/ff/v2/ffcli" "github.com/toqueteos/webbrowser" @@ -23,7 +22,6 @@ import ( "tailscale.com/ipn" "tailscale.com/ipn/ipnstate" "tailscale.com/net/interfaces" - "tailscale.com/tstime/mono" "tailscale.com/util/dnsname" ) @@ -63,7 +61,7 @@ func runStatus(ctx context.Context, args []string) error { if statusArgs.json { if statusArgs.active { for peer, ps := range st.Peer { - if !peerActive(ps) { + if !ps.Active { delete(st.Peer, peer) } } @@ -131,7 +129,6 @@ func runStatus(ctx context.Context, args []string) error { var buf bytes.Buffer f := func(format string, a ...interface{}) { fmt.Fprintf(&buf, format, a...) } printPS := func(ps *ipnstate.PeerStatus) { - active := peerActive(ps) f("%-15s %-20s %-12s %-7s ", firstIPString(ps.TailscaleIPs), dnsOrQuoteHostname(st, ps), @@ -140,7 +137,7 @@ func runStatus(ctx context.Context, args []string) error { ) relay := ps.Relay anyTraffic := ps.TxBytes != 0 || ps.RxBytes != 0 - if !active { + if !ps.Active { if ps.ExitNode { f("idle; exit node") } else if anyTraffic { @@ -179,8 +176,7 @@ func runStatus(ctx context.Context, args []string) error { } ipnstate.SortPeers(peers) for _, ps := range peers { - active := peerActive(ps) - if statusArgs.active && !active { + if statusArgs.active && !ps.Active { continue } printPS(ps) @@ -190,13 +186,6 @@ func runStatus(ctx context.Context, args []string) error { return nil } -// peerActive reports whether ps has recent activity. -// -// TODO: have the server report this bool instead. -func peerActive(ps *ipnstate.PeerStatus) bool { - return !ps.LastWrite.IsZero() && mono.Since(ps.LastWrite) < 2*time.Minute -} - func dnsOrQuoteHostname(st *ipnstate.Status, ps *ipnstate.PeerStatus) string { baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix) if baseName != "" { diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 37989e550..1a5f70f61 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -49,7 +49,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/syncs from tailscale.com/net/interfaces+ tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+ W tailscale.com/tsconst from tailscale.com/net/interfaces - 💣 tailscale.com/tstime/mono from tailscale.com/cmd/tailscale/cli+ + 💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate tailscale.com/tstime/rate from tailscale.com/wgengine/filter tailscale.com/types/empty from tailscale.com/ipn tailscale.com/types/ipproto from tailscale.com/net/flowtrack+ diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index 86c190f45..62d43b332 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -20,7 +20,6 @@ import ( "inet.af/netaddr" "tailscale.com/tailcfg" - "tailscale.com/tstime/mono" "tailscale.com/types/key" "tailscale.com/util/dnsname" ) @@ -91,12 +90,19 @@ type PeerStatus struct { RxBytes int64 TxBytes int64 Created time.Time // time registered with tailcontrol - LastWrite mono.Time // time last packet sent + LastWrite time.Time // time last packet sent LastSeen time.Time // last seen to tailcontrol LastHandshake time.Time // with local wireguard KeepAlive bool ExitNode bool // true if this is the currently selected exit node. + // Active is whether the node was recently active. The + // definition is somewhat undefined but has historically and + // currently means that there was some packet sent to this + // peer in the past two minutes. That definition is subject to + // change. + Active bool + PeerAPIURL []string Capabilities []string `json:",omitempty"` @@ -278,6 +284,9 @@ func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) { if st.ShareeNode { e.ShareeNode = true } + if st.Active { + e.Active = true + } } type StatusUpdater interface { @@ -321,7 +330,7 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; } f("PeerOSNodeOwnerRxTxActivityConnection\n") f("\n\n") - now := mono.Now() + now := time.Now() var peers []*PeerStatus for _, peer := range st.Peers() { @@ -378,9 +387,7 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; } ) f("") - // TODO: let server report this active bool instead - active := !ps.LastWrite.IsZero() && mono.Since(ps.LastWrite) < 2*time.Minute - if active { + if ps.Active { if ps.Relay != "" && ps.CurAddr == "" { f("relay %s", html.EscapeString(ps.Relay)) } else if ps.CurAddr != "" { diff --git a/tstime/mono/mono.go b/tstime/mono/mono.go index eda90684c..3e582f644 100644 --- a/tstime/mono/mono.go +++ b/tstime/mono/mono.go @@ -95,16 +95,21 @@ func (t Time) String() string { return fmt.Sprintf("mono.Time(ns=%d, estimated wall=%v)", int64(t), baseWall.Add(t.Sub(baseMono)).Truncate(0)) } +// WallTime returns an approximate wall time that corresponded to t. +func (t Time) WallTime() time.Time { + if !t.IsZero() { + return baseWall.Add(t.Sub(baseMono)).Truncate(0) + } + return time.Time{} +} + // MarshalJSON formats t for JSON as if it were a time.Time. // We format Time this way for backwards-compatibility. // This is best-effort only. Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged. // Since t is a monotonic time, it can vary from the actual wall clock by arbitrary amounts. // Even in the best of circumstances, it may vary by a few milliseconds. func (t Time) MarshalJSON() ([]byte, error) { - var tt time.Time - if !t.IsZero() { - tt = baseWall.Add(t.Sub(baseMono)).Truncate(0) - } + tt := t.WallTime() return tt.MarshalJSON() } diff --git a/wgengine/magicsock/legacy.go b/wgengine/magicsock/legacy.go index 23a531e7e..373aef370 100644 --- a/wgengine/magicsock/legacy.go +++ b/wgengine/magicsock/legacy.go @@ -538,7 +538,7 @@ func (as *addrSet) populatePeerStatus(ps *ipnstate.PeerStatus) { as.mu.Lock() defer as.mu.Unlock() - ps.LastWrite = as.lastSend + ps.LastWrite = as.lastSend.WallTime() for i, ua := range as.ipPorts { if ua.IP() == derpMagicIPAddr { continue diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 145eea7b7..107df0388 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -3836,9 +3836,10 @@ func (de *discoEndpoint) populatePeerStatus(ps *ipnstate.PeerStatus) { return } - ps.LastWrite = de.lastSend - now := mono.Now() + ps.LastWrite = de.lastSend.WallTime() + ps.Active = now.Sub(de.lastSend) < sessionActiveTimeout + if udpAddr, derpAddr := de.addrForSendLocked(now); !udpAddr.IsZero() && derpAddr.IsZero() { ps.CurAddr = udpAddr.String() }