mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
cmd/tailscale: add tailscale status region name, last write, consistently star
There's a lot of confusion around what tailscale status shows, so make it better: show region names, last write time, and put stars around DERP too if active. Now stars are always present if activity, and always somewhere.
This commit is contained in:
parent
0ea51872c9
commit
630379a1d0
@ -14,6 +14,7 @@
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/peterbourgon/ff/v2/ffcli"
|
"github.com/peterbourgon/ff/v2/ffcli"
|
||||||
"github.com/toqueteos/webbrowser"
|
"github.com/toqueteos/webbrowser"
|
||||||
@ -127,6 +128,15 @@ func runStatus(ctx context.Context, args []string) error {
|
|||||||
ps.TxBytes,
|
ps.TxBytes,
|
||||||
ps.RxBytes,
|
ps.RxBytes,
|
||||||
)
|
)
|
||||||
|
// TODO: let server report this active bool instead
|
||||||
|
active := !ps.LastWrite.IsZero() && time.Since(ps.LastWrite) < 2*time.Minute
|
||||||
|
relay := ps.Relay
|
||||||
|
if active && relay != "" && ps.CurAddr == "" {
|
||||||
|
relay = "*" + relay + "*"
|
||||||
|
} else {
|
||||||
|
relay = " " + relay
|
||||||
|
}
|
||||||
|
f("%-6s", relay)
|
||||||
for i, addr := range ps.Addrs {
|
for i, addr := range ps.Addrs {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
f(", ")
|
f(", ")
|
||||||
|
@ -49,10 +49,12 @@ type PeerStatus struct {
|
|||||||
// Endpoints:
|
// Endpoints:
|
||||||
Addrs []string
|
Addrs []string
|
||||||
CurAddr string // one of Addrs, or unique if roaming
|
CurAddr string // one of Addrs, or unique if roaming
|
||||||
|
Relay string // DERP region
|
||||||
|
|
||||||
RxBytes int64
|
RxBytes int64
|
||||||
TxBytes int64
|
TxBytes int64
|
||||||
Created time.Time // time registered with tailcontrol
|
Created time.Time // time registered with tailcontrol
|
||||||
|
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
|
||||||
@ -135,6 +137,9 @@ func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) {
|
|||||||
if v := st.HostName; v != "" {
|
if v := st.HostName; v != "" {
|
||||||
e.HostName = v
|
e.HostName = v
|
||||||
}
|
}
|
||||||
|
if v := st.Relay; v != "" {
|
||||||
|
e.Relay = v
|
||||||
|
}
|
||||||
if v := st.UserID; v != 0 {
|
if v := st.UserID; v != 0 {
|
||||||
e.UserID = v
|
e.UserID = v
|
||||||
}
|
}
|
||||||
@ -165,6 +170,9 @@ func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) {
|
|||||||
if v := st.LastSeen; !v.IsZero() {
|
if v := st.LastSeen; !v.IsZero() {
|
||||||
e.LastSeen = v
|
e.LastSeen = v
|
||||||
}
|
}
|
||||||
|
if v := st.LastWrite; !v.IsZero() {
|
||||||
|
e.LastWrite = v
|
||||||
|
}
|
||||||
if st.InNetworkMap {
|
if st.InNetworkMap {
|
||||||
e.InNetworkMap = true
|
e.InNetworkMap = true
|
||||||
}
|
}
|
||||||
@ -211,28 +219,19 @@ func (st *Status) WriteHTML(w io.Writer) {
|
|||||||
//f("<p><b>opts:</b> <code>%s</code></p>\n", html.EscapeString(fmt.Sprintf("%+v", opts)))
|
//f("<p><b>opts:</b> <code>%s</code></p>\n", html.EscapeString(fmt.Sprintf("%+v", opts)))
|
||||||
|
|
||||||
f("<table>\n<thead>\n")
|
f("<table>\n<thead>\n")
|
||||||
f("<tr><th>Peer</th><th>Node</th><th>Owner</th><th>Rx</th><th>Tx</th><th>Handshake</th><th>Endpoints</th></tr>\n")
|
f("<tr><th>Peer</th><th>Node</th><th>Owner</th><th>Rx</th><th>Tx</th><th>Activity</th><th>Endpoints</th></tr>\n")
|
||||||
f("</thead>\n<tbody>\n")
|
f("</thead>\n<tbody>\n")
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// The tailcontrol server rounds LastSeen to 10 minutes. So we
|
|
||||||
// declare that a longAgo seen time of 15 minutes means
|
|
||||||
// they're not connected.
|
|
||||||
longAgo := now.Add(-15 * time.Minute)
|
|
||||||
|
|
||||||
for _, peer := range st.Peers() {
|
for _, peer := range st.Peers() {
|
||||||
ps := st.Peer[peer]
|
ps := st.Peer[peer]
|
||||||
var hsAgo string
|
var actAgo string
|
||||||
if !ps.LastHandshake.IsZero() {
|
if !ps.LastWrite.IsZero() {
|
||||||
hsAgo = now.Sub(ps.LastHandshake).Round(time.Second).String() + " ago"
|
ago := now.Sub(ps.LastWrite)
|
||||||
} else {
|
actAgo = ago.Round(time.Second).String() + " ago"
|
||||||
if ps.LastSeen.Before(longAgo) {
|
if ago < 5*time.Minute {
|
||||||
hsAgo = "<i>offline</i>"
|
actAgo = "<b>" + actAgo + "</b>"
|
||||||
} else if !ps.KeepAlive {
|
|
||||||
hsAgo = "on demand"
|
|
||||||
} else {
|
|
||||||
hsAgo = "<b>pending</b>"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var owner string
|
var owner string
|
||||||
@ -250,9 +249,20 @@ func (st *Status) WriteHTML(w io.Writer) {
|
|||||||
html.EscapeString(owner),
|
html.EscapeString(owner),
|
||||||
ps.RxBytes,
|
ps.RxBytes,
|
||||||
ps.TxBytes,
|
ps.TxBytes,
|
||||||
hsAgo,
|
actAgo,
|
||||||
)
|
)
|
||||||
f("<td class=\"aright\">")
|
f("<td class=\"aright\">")
|
||||||
|
// 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("🔗 <b>derp-%v</b><br>", html.EscapeString(relay))
|
||||||
|
} else {
|
||||||
|
f("derp-%v<br>", html.EscapeString(relay))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match := false
|
match := false
|
||||||
for _, addr := range ps.Addrs {
|
for _, addr := range ps.Addrs {
|
||||||
if addr == ps.CurAddr {
|
if addr == ps.CurAddr {
|
||||||
|
@ -686,6 +686,8 @@ func (as *AddrSet) appendDests(dsts []netaddr.IPPort, b []byte) (_ []netaddr.IPP
|
|||||||
as.mu.Lock()
|
as.mu.Lock()
|
||||||
defer as.mu.Unlock()
|
defer as.mu.Unlock()
|
||||||
|
|
||||||
|
as.lastSend = now
|
||||||
|
|
||||||
// Some internal invariant checks.
|
// Some internal invariant checks.
|
||||||
if len(as.addrs) != len(as.ipPorts) {
|
if len(as.addrs) != len(as.ipPorts) {
|
||||||
panic(fmt.Sprintf("lena %d != leni %d", len(as.addrs), len(as.ipPorts)))
|
panic(fmt.Sprintf("lena %d != leni %d", len(as.addrs), len(as.ipPorts)))
|
||||||
@ -2094,6 +2096,8 @@ type AddrSet struct {
|
|||||||
|
|
||||||
mu sync.Mutex // guards following fields
|
mu sync.Mutex // guards following fields
|
||||||
|
|
||||||
|
lastSend time.Time
|
||||||
|
|
||||||
// roamAddr is non-nil if/when we receive a correctly signed
|
// roamAddr is non-nil if/when we receive a correctly signed
|
||||||
// WireGuard packet from an unexpected address. If so, we
|
// WireGuard packet from an unexpected address. If so, we
|
||||||
// remember it and send responses there in the future, but
|
// remember it and send responses there in the future, but
|
||||||
@ -2308,6 +2312,26 @@ func (a *AddrSet) String() string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *AddrSet) populatePeerStatus(ps *ipnstate.PeerStatus) {
|
||||||
|
as.mu.Lock()
|
||||||
|
defer as.mu.Unlock()
|
||||||
|
|
||||||
|
ps.LastWrite = as.lastSend
|
||||||
|
for i, ua := range as.addrs {
|
||||||
|
if ua.IP.Equal(derpMagicIP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uaStr := ua.String()
|
||||||
|
ps.Addrs = append(ps.Addrs, uaStr)
|
||||||
|
if as.curAddr == i {
|
||||||
|
ps.CurAddr = uaStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if as.roamAddr != nil {
|
||||||
|
ps.CurAddr = udpAddrDebugString(*as.roamAddrStd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AddrSet) Addrs() []wgcfg.Endpoint {
|
func (a *AddrSet) Addrs() []wgcfg.Endpoint {
|
||||||
var eps []wgcfg.Endpoint
|
var eps []wgcfg.Endpoint
|
||||||
for _, addr := range a.addrs {
|
for _, addr := range a.addrs {
|
||||||
@ -2566,6 +2590,28 @@ func sbPrintAddr(sb *strings.Builder, a net.UDPAddr) {
|
|||||||
fmt.Fprintf(sb, ":%d", a.Port)
|
fmt.Fprintf(sb, ":%d", a.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) derpRegionCodeOfAddrLocked(ipPort string) string {
|
||||||
|
_, portStr, err := net.SplitHostPort(ipPort)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
regionID, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return c.derpRegionCodeOfIDLocked(regionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) derpRegionCodeOfIDLocked(regionID int) string {
|
||||||
|
if c.derpMap == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if r, ok := c.derpMap.Regions[regionID]; ok {
|
||||||
|
return r.RegionCode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
@ -2574,6 +2620,7 @@ func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
ps := &ipnstate.PeerStatus{InMagicSock: true}
|
ps := &ipnstate.PeerStatus{InMagicSock: true}
|
||||||
if node, ok := c.nodeOfDisco[dk]; ok {
|
if node, ok := c.nodeOfDisco[dk]; ok {
|
||||||
ps.Addrs = append(ps.Addrs, node.Endpoints...)
|
ps.Addrs = append(ps.Addrs, node.Endpoints...)
|
||||||
|
ps.Relay = c.derpRegionCodeOfAddrLocked(node.DERP)
|
||||||
}
|
}
|
||||||
de.populatePeerStatus(ps)
|
de.populatePeerStatus(ps)
|
||||||
sb.AddPeer(de.publicKey, ps)
|
sb.AddPeer(de.publicKey, ps)
|
||||||
@ -2582,17 +2629,9 @@ func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
for k, as := range c.addrsByKey {
|
for k, as := range c.addrsByKey {
|
||||||
ps := &ipnstate.PeerStatus{
|
ps := &ipnstate.PeerStatus{
|
||||||
InMagicSock: true,
|
InMagicSock: true,
|
||||||
|
Relay: c.derpRegionCodeOfIDLocked(as.derpID()),
|
||||||
}
|
}
|
||||||
for i, ua := range as.addrs {
|
as.populatePeerStatus(ps)
|
||||||
uaStr := udpAddrDebugString(ua)
|
|
||||||
ps.Addrs = append(ps.Addrs, uaStr)
|
|
||||||
if as.curAddr == i {
|
|
||||||
ps.CurAddr = uaStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if as.roamAddr != nil {
|
|
||||||
ps.CurAddr = udpAddrDebugString(*as.roamAddrStd)
|
|
||||||
}
|
|
||||||
sb.AddPeer(k, ps)
|
sb.AddPeer(k, ps)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3078,8 +3117,10 @@ func (de *discoEndpoint) populatePeerStatus(ps *ipnstate.PeerStatus) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ps.LastWrite = de.lastSend
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if udpAddr, _ := de.addrForSendLocked(now); !udpAddr.IsZero() {
|
if udpAddr, derpAddr := de.addrForSendLocked(now); !udpAddr.IsZero() && derpAddr.IsZero() {
|
||||||
ps.CurAddr = udpAddr.String()
|
ps.CurAddr = udpAddr.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user