ipn/ipnstate: move tailscale status "active" determination to tailscaled

Fixes #2579

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/2584/head
Brad Fitzpatrick 3 years ago committed by Brad Fitzpatrick
parent 0858673f1f
commit f3c96df162

@ -14,7 +14,6 @@ import (
"net/http" "net/http"
"os" "os"
"strings" "strings"
"time"
"github.com/peterbourgon/ff/v2/ffcli" "github.com/peterbourgon/ff/v2/ffcli"
"github.com/toqueteos/webbrowser" "github.com/toqueteos/webbrowser"
@ -23,7 +22,6 @@ import (
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces" "tailscale.com/net/interfaces"
"tailscale.com/tstime/mono"
"tailscale.com/util/dnsname" "tailscale.com/util/dnsname"
) )
@ -63,7 +61,7 @@ func runStatus(ctx context.Context, args []string) error {
if statusArgs.json { if statusArgs.json {
if statusArgs.active { if statusArgs.active {
for peer, ps := range st.Peer { for peer, ps := range st.Peer {
if !peerActive(ps) { if !ps.Active {
delete(st.Peer, peer) delete(st.Peer, peer)
} }
} }
@ -131,7 +129,6 @@ func runStatus(ctx context.Context, args []string) error {
var buf bytes.Buffer var buf bytes.Buffer
f := func(format string, a ...interface{}) { fmt.Fprintf(&buf, format, a...) } f := func(format string, a ...interface{}) { fmt.Fprintf(&buf, format, a...) }
printPS := func(ps *ipnstate.PeerStatus) { printPS := func(ps *ipnstate.PeerStatus) {
active := peerActive(ps)
f("%-15s %-20s %-12s %-7s ", f("%-15s %-20s %-12s %-7s ",
firstIPString(ps.TailscaleIPs), firstIPString(ps.TailscaleIPs),
dnsOrQuoteHostname(st, ps), dnsOrQuoteHostname(st, ps),
@ -140,7 +137,7 @@ func runStatus(ctx context.Context, args []string) error {
) )
relay := ps.Relay relay := ps.Relay
anyTraffic := ps.TxBytes != 0 || ps.RxBytes != 0 anyTraffic := ps.TxBytes != 0 || ps.RxBytes != 0
if !active { if !ps.Active {
if ps.ExitNode { if ps.ExitNode {
f("idle; exit node") f("idle; exit node")
} else if anyTraffic { } else if anyTraffic {
@ -179,8 +176,7 @@ func runStatus(ctx context.Context, args []string) error {
} }
ipnstate.SortPeers(peers) ipnstate.SortPeers(peers)
for _, ps := range peers { for _, ps := range peers {
active := peerActive(ps) if statusArgs.active && !ps.Active {
if statusArgs.active && !active {
continue continue
} }
printPS(ps) printPS(ps)
@ -190,13 +186,6 @@ func runStatus(ctx context.Context, args []string) error {
return nil 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 { func dnsOrQuoteHostname(st *ipnstate.Status, ps *ipnstate.PeerStatus) string {
baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix) baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix)
if baseName != "" { if baseName != "" {

@ -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/syncs from tailscale.com/net/interfaces+
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+ tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
W tailscale.com/tsconst from tailscale.com/net/interfaces 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/tstime/rate from tailscale.com/wgengine/filter
tailscale.com/types/empty from tailscale.com/ipn tailscale.com/types/empty from tailscale.com/ipn
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+ tailscale.com/types/ipproto from tailscale.com/net/flowtrack+

@ -20,7 +20,6 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tstime/mono"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/util/dnsname" "tailscale.com/util/dnsname"
) )
@ -91,12 +90,19 @@ type PeerStatus struct {
RxBytes int64 RxBytes int64
TxBytes int64 TxBytes int64
Created time.Time // time registered with tailcontrol 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 LastSeen time.Time // last seen to tailcontrol
LastHandshake time.Time // with local wireguard LastHandshake time.Time // with local wireguard
KeepAlive bool KeepAlive bool
ExitNode bool // true if this is the currently selected exit node. 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 PeerAPIURL []string
Capabilities []string `json:",omitempty"` Capabilities []string `json:",omitempty"`
@ -278,6 +284,9 @@ func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) {
if st.ShareeNode { if st.ShareeNode {
e.ShareeNode = true e.ShareeNode = true
} }
if st.Active {
e.Active = true
}
} }
type StatusUpdater interface { type StatusUpdater interface {
@ -321,7 +330,7 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; }
f("<tr><th>Peer</th><th>OS</th><th>Node</th><th>Owner</th><th>Rx</th><th>Tx</th><th>Activity</th><th>Connection</th></tr>\n") f("<tr><th>Peer</th><th>OS</th><th>Node</th><th>Owner</th><th>Rx</th><th>Tx</th><th>Activity</th><th>Connection</th></tr>\n")
f("</thead>\n<tbody>\n") f("</thead>\n<tbody>\n")
now := mono.Now() now := time.Now()
var peers []*PeerStatus var peers []*PeerStatus
for _, peer := range st.Peers() { for _, peer := range st.Peers() {
@ -378,9 +387,7 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; }
) )
f("<td>") f("<td>")
// TODO: let server report this active bool instead if ps.Active {
active := !ps.LastWrite.IsZero() && mono.Since(ps.LastWrite) < 2*time.Minute
if active {
if ps.Relay != "" && ps.CurAddr == "" { if ps.Relay != "" && ps.CurAddr == "" {
f("relay <b>%s</b>", html.EscapeString(ps.Relay)) f("relay <b>%s</b>", html.EscapeString(ps.Relay))
} else if ps.CurAddr != "" { } else if ps.CurAddr != "" {

@ -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)) 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. // MarshalJSON formats t for JSON as if it were a time.Time.
// We format Time this way for backwards-compatibility. // We format Time this way for backwards-compatibility.
// This is best-effort only. Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged. // 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. // 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. // Even in the best of circumstances, it may vary by a few milliseconds.
func (t Time) MarshalJSON() ([]byte, error) { func (t Time) MarshalJSON() ([]byte, error) {
var tt time.Time tt := t.WallTime()
if !t.IsZero() {
tt = baseWall.Add(t.Sub(baseMono)).Truncate(0)
}
return tt.MarshalJSON() return tt.MarshalJSON()
} }

@ -538,7 +538,7 @@ func (as *addrSet) populatePeerStatus(ps *ipnstate.PeerStatus) {
as.mu.Lock() as.mu.Lock()
defer as.mu.Unlock() defer as.mu.Unlock()
ps.LastWrite = as.lastSend ps.LastWrite = as.lastSend.WallTime()
for i, ua := range as.ipPorts { for i, ua := range as.ipPorts {
if ua.IP() == derpMagicIPAddr { if ua.IP() == derpMagicIPAddr {
continue continue

@ -3836,9 +3836,10 @@ func (de *discoEndpoint) populatePeerStatus(ps *ipnstate.PeerStatus) {
return return
} }
ps.LastWrite = de.lastSend
now := mono.Now() 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() { if udpAddr, derpAddr := de.addrForSendLocked(now); !udpAddr.IsZero() && derpAddr.IsZero() {
ps.CurAddr = udpAddr.String() ps.CurAddr = udpAddr.String()
} }

Loading…
Cancel
Save