diff --git a/cmd/tailscale/cli/status.go b/cmd/tailscale/cli/status.go index e781c0429..022eb1328 100644 --- a/cmd/tailscale/cli/status.go +++ b/cmd/tailscale/cli/status.go @@ -14,7 +14,6 @@ import ( "net" "net/http" "os" - "sort" "strings" "time" @@ -181,7 +180,7 @@ func runStatus(ctx context.Context, args []string) error { } peers = append(peers, ps) } - sort.Slice(peers, func(i, j int) bool { return sortKey(peers[i]) < sortKey(peers[j]) }) + ipnstate.SortPeers(peers) for _, ps := range peers { active := peerActive(ps) if statusArgs.active && !active { @@ -211,16 +210,6 @@ func dnsOrQuoteHostname(st *ipnstate.Status, ps *ipnstate.PeerStatus) string { return fmt.Sprintf("(%q)", strings.ReplaceAll(ps.SimpleHostName(), " ", "_")) } -func sortKey(ps *ipnstate.PeerStatus) string { - if ps.DNSName != "" { - return ps.DNSName - } - if ps.HostName != "" { - return ps.HostName - } - return ps.TailAddr -} - func ownerLogin(st *ipnstate.Status, ps *ipnstate.PeerStatus) string { if ps.UserID.IsZero() { return "-" diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index 2f4ce10bd..fbddbc46b 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -21,6 +21,7 @@ import ( "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/key" + "tailscale.com/util/dnsname" ) // Status represents the entire state of the IPN network. @@ -280,13 +281,22 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; } f("

Tailscale IP: %s", strings.Join(ips, ", ")) f("\n\n") - f("\n") + f("\n") f("\n\n") now := time.Now() + var peers []*PeerStatus for _, peer := range st.Peers() { ps := st.Peer[peer] + if ps.ShareeNode { + continue + } + peers = append(peers, ps) + } + SortPeers(peers) + + for _, ps := range peers { var actAgo string if !ps.LastWrite.IsZero() { ago := now.Sub(ps.LastWrite) @@ -302,40 +312,44 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; } owner = owner[:i] } } - f("", - peer.ShortString(), - html.EscapeString(ps.SimpleHostName()), + + hostName := ps.SimpleHostName() + dnsName := strings.TrimRight(ps.DNSName, ".") + if i := strings.Index(dnsName, "."); i != -1 && dnsname.HasSuffix(dnsName, st.MagicDNSSuffix) { + dnsName = dnsName[:i] + } + if strings.EqualFold(dnsName, hostName) || ps.UserID != st.Self.UserID { + hostName = "" + } + var hostNameHTML string + if hostName != "" { + hostNameHTML = "
" + html.EscapeString(hostName) + } + + f(""+ + "", + ps.PublicKey.ShortString(), osEmoji(ps.OS), + html.EscapeString(dnsName), + hostNameHTML, ps.TailAddr, html.EscapeString(owner), ps.RxBytes, ps.TxBytes, actAgo, ) - f("") // end Addrs f("\n") @@ -381,3 +395,17 @@ type PingResult struct { // TODO(bradfitz): details like whether port mapping was used on either side? (Once supported) } + +func SortPeers(peers []*PeerStatus) { + sort.Slice(peers, func(i, j int) bool { return sortKey(peers[i]) < sortKey(peers[j]) }) +} + +func sortKey(ps *PeerStatus) string { + if ps.DNSName != "" { + return ps.DNSName + } + if ps.HostName != "" { + return ps.HostName + } + return ps.TailAddr +}
PeerNodeOwnerRxTxActivityEndpoints
PeerOSNodeOwnerRxTxActivityConnection
%s%s %s
%s
%s%v%v%v
%s%s%s%s
%s
%s%v%v%v") + f("") + // TODO: let server report this active bool instead active := !ps.LastWrite.IsZero() && time.Since(ps.LastWrite) < 2*time.Minute - relay := ps.Relay - if relay != "" { - if active && ps.CurAddr == "" { - f("🔗 derp-%v
", html.EscapeString(relay)) - } else { - f("derp-%v
", html.EscapeString(relay)) + if active { + if ps.Relay != "" && ps.CurAddr == "" { + f("relay %s", html.EscapeString(ps.Relay)) + } else if ps.CurAddr != "" { + f("direct %s", html.EscapeString(ps.CurAddr)) } } - match := false - for _, addr := range ps.Addrs { - if addr == ps.CurAddr { - match = true - f("🔗 %s
", addr) - } else { - f("%s
", addr) - } - } - if ps.CurAddr != "" && !match { - f("%s \xf0\x9f\xa7\xb3
", ps.CurAddr) - } f("