mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 03:28:50 +00:00 
			
		
		
		
	cmd/tailscaled, wgengine, ipn: add /debug/ipn handler with world state
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
		 Brad Fitzpatrick
					Brad Fitzpatrick
				
			
				
					committed by
					
						 Brad Fitzpatrick
						Brad Fitzpatrick
					
				
			
			
				
	
			
			
			 Brad Fitzpatrick
						Brad Fitzpatrick
					
				
			
						parent
						
							dbca186a64
						
					
				
				
					commit
					322499473e
				
			| @@ -62,8 +62,10 @@ func main() { | ||||
| 		log.Fatalf("--socket is required") | ||||
| 	} | ||||
|  | ||||
| 	var debugMux *http.ServeMux | ||||
| 	if *debug != "" { | ||||
| 		go runDebugServer(*debug) | ||||
| 		debugMux = newDebugMux() | ||||
| 		go runDebugServer(debugMux, *debug) | ||||
| 	} | ||||
|  | ||||
| 	var e wgengine.Engine | ||||
| @@ -84,6 +86,7 @@ func main() { | ||||
| 		AutostartStateKey:  globalStateKey, | ||||
| 		LegacyConfigPath:   paths.LegacyConfigPath, | ||||
| 		SurviveDisconnects: true, | ||||
| 		DebugMux:           debugMux, | ||||
| 	} | ||||
| 	err = ipnserver.Run(context.Background(), logf, pol.PublicID.String(), opts, e) | ||||
| 	if err != nil { | ||||
| @@ -98,14 +101,18 @@ func main() { | ||||
| 	pol.Shutdown(ctx) | ||||
| } | ||||
|  | ||||
| func runDebugServer(addr string) { | ||||
| func newDebugMux() *http.ServeMux { | ||||
| 	mux := http.NewServeMux() | ||||
| 	mux.HandleFunc("/debug/pprof/", pprof.Index) | ||||
| 	mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) | ||||
| 	mux.HandleFunc("/debug/pprof/profile", pprof.Profile) | ||||
| 	mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) | ||||
| 	mux.HandleFunc("/debug/pprof/trace", pprof.Trace) | ||||
| 	srv := http.Server{ | ||||
| 	return mux | ||||
| } | ||||
|  | ||||
| func runDebugServer(mux *http.ServeMux, addr string) { | ||||
| 	srv := &http.Server{ | ||||
| 		Addr:    addr, | ||||
| 		Handler: mux, | ||||
| 	} | ||||
|   | ||||
| @@ -8,8 +8,10 @@ import ( | ||||
| 	"bufio" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"html" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"os/signal" | ||||
| @@ -56,6 +58,10 @@ type Options struct { | ||||
| 	// its existing state, and accepts new frontend connections. If | ||||
| 	// false, the server dumps its state and becomes idle. | ||||
| 	SurviveDisconnects bool | ||||
|  | ||||
| 	// DebugMux, if non-nil, specifies an HTTP ServeMux in which | ||||
| 	// to register a debug handler. | ||||
| 	DebugMux *http.ServeMux | ||||
| } | ||||
|  | ||||
| func pump(logf logger.Logf, ctx context.Context, bs *ipn.BackendServer, s net.Conn) { | ||||
| @@ -112,6 +118,12 @@ func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e w | ||||
| 		return zstd.NewReader(nil) | ||||
| 	}) | ||||
|  | ||||
| 	if opts.DebugMux != nil { | ||||
| 		opts.DebugMux.HandleFunc("/debug/ipn", func(w http.ResponseWriter, r *http.Request) { | ||||
| 			serveDebugHandler(w, r, logid, opts, b, e) | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	var s net.Conn | ||||
| 	serverToClient := func(b []byte) { | ||||
| 		if s != nil { // TODO: racy access to s? | ||||
| @@ -299,3 +311,101 @@ func BabysitProc(ctx context.Context, args []string, logf logger.Logf) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func serveDebugHandler(w http.ResponseWriter, r *http.Request, logid string, opts Options, b *ipn.LocalBackend, e wgengine.Engine) { | ||||
| 	f := func(format string, args ...interface{}) { fmt.Fprintf(w, format, args...) } | ||||
| 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
|  | ||||
| 	f(`<html><head><style> | ||||
| .owner { font-size: 80%%; color: #444; } | ||||
| .tailaddr { font-size: 80%%; font-family: monospace: } | ||||
| </style></head>`) | ||||
| 	f("<body><h1>IPN state</h1><h2>Run args</h2>") | ||||
| 	f("<p><b>logid:</b> %s</p>\n", logid) | ||||
| 	f("<p><b>opts:</b> <code>%s</code></p>\n", html.EscapeString(fmt.Sprintf("%+v", opts))) | ||||
|  | ||||
| 	st := b.Status() | ||||
| 	f("<table border=1 cellpadding=5><tr><th>Peer</th><th>Node</th><th>Rx</th><th>Tx</th><th>Handshake</th><th>Endpoints</th></tr>") | ||||
|  | ||||
| 	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() { | ||||
| 		ps := st.Peer[peer] | ||||
| 		var hsAgo string | ||||
| 		if !ps.LastHandshake.IsZero() { | ||||
| 			hsAgo = now.Sub(ps.LastHandshake).Round(time.Second).String() + " ago" | ||||
| 		} else { | ||||
| 			if ps.LastSeen.Before(longAgo) { | ||||
| 				hsAgo = "<i>offline</i>" | ||||
| 			} else if !ps.KeepAlive { | ||||
| 				hsAgo = "on demand" | ||||
| 			} else { | ||||
| 				hsAgo = "<b>pending</b>" | ||||
| 			} | ||||
| 		} | ||||
| 		var owner string | ||||
| 		if up, ok := st.User[ps.UserID]; ok { | ||||
| 			owner = up.LoginName | ||||
| 			if i := strings.Index(owner, "@"); i != -1 { | ||||
| 				owner = owner[:i] | ||||
| 			} | ||||
| 		} | ||||
| 		f("<tr><td>%s</td><td>%s<div class=owner>%s</div><div class=tailaddr>%s</div></td><td>%v</td><td>%v</td><td>%v</td>", | ||||
| 			peer.ShortString(), | ||||
| 			osEmoji(ps.OS)+" "+html.EscapeString(simplifyHostname(ps.HostName)), | ||||
| 			html.EscapeString(owner), | ||||
| 			ps.TailAddr, | ||||
| 			ps.RxBytes, | ||||
| 			ps.TxBytes, | ||||
| 			hsAgo, | ||||
| 		) | ||||
| 		f("<td>") | ||||
| 		match := false | ||||
| 		for _, addr := range ps.Addrs { | ||||
| 			if addr == ps.CurAddr { | ||||
| 				match = true | ||||
| 				f("<b>%s</b> 🔗<br>\n", addr) | ||||
| 			} else { | ||||
| 				f("%s<br>\n", addr) | ||||
| 			} | ||||
| 		} | ||||
| 		if ps.CurAddr != "" && !match { | ||||
| 			f("<b>%s</b> \xf0\x9f\xa7\xb3<br>\n", ps.CurAddr) | ||||
| 		} | ||||
| 		f("</tr>") // end Addrs | ||||
|  | ||||
| 		f("</tr>\n") | ||||
| 	} | ||||
| 	f("</table>") | ||||
| } | ||||
|  | ||||
| func osEmoji(os string) string { | ||||
| 	switch os { | ||||
| 	case "linux": | ||||
| 		return "🐧" | ||||
| 	case "macOS": | ||||
| 		return "🍎" | ||||
| 	case "windows": | ||||
| 		return "🖥️" | ||||
| 	case "iOS": | ||||
| 		return "📱" | ||||
| 	case "android": | ||||
| 		return "🤖" | ||||
| 	case "freebsd": | ||||
| 		return "👿" | ||||
| 	case "openbsd": | ||||
| 		return "🐡" | ||||
| 	} | ||||
| 	return "👽" | ||||
| } | ||||
|  | ||||
| func simplifyHostname(s string) string { | ||||
| 	s = strings.TrimSuffix(s, ".local") | ||||
| 	s = strings.TrimSuffix(s, ".localdomain") | ||||
| 	return s | ||||
| } | ||||
|   | ||||
							
								
								
									
										172
									
								
								ipn/ipnstate/ipnstate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								ipn/ipnstate/ipnstate.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| // Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // Package ipnstate captures the entire state of the Tailscale network. | ||||
| // | ||||
| // It's a leaf package so ipn, wgengine, and magicsock can all depend on it. | ||||
| package ipnstate | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"log" | ||||
| 	"sort" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"tailscale.com/tailcfg" | ||||
| 	"tailscale.com/types/key" | ||||
| ) | ||||
|  | ||||
| // Status represents the entire state of the IPN network. | ||||
| type Status struct { | ||||
| 	BackendState string | ||||
| 	Peer         map[key.Public]*PeerStatus | ||||
| 	User         map[tailcfg.UserID]tailcfg.UserProfile | ||||
| } | ||||
|  | ||||
| func (s *Status) Peers() []key.Public { | ||||
| 	kk := make([]key.Public, 0, len(s.Peer)) | ||||
| 	for k := range s.Peer { | ||||
| 		kk = append(kk, k) | ||||
| 	} | ||||
| 	sort.Slice(kk, func(i, j int) bool { return bytes.Compare(kk[i][:], kk[j][:]) < 0 }) | ||||
| 	return kk | ||||
| } | ||||
|  | ||||
| type PeerStatus struct { | ||||
| 	PublicKey key.Public | ||||
| 	HostName  string // HostInfo's Hostname (not a DNS name or necessarily unique) | ||||
| 	OS        string // HostInfo.OS | ||||
| 	UserID    tailcfg.UserID | ||||
|  | ||||
| 	TailAddr string // Tailscale IP | ||||
|  | ||||
| 	// Endpoints: | ||||
| 	Addrs   []string | ||||
| 	CurAddr string // one of Addrs, or unique if roaming | ||||
|  | ||||
| 	RxBytes       int64 | ||||
| 	TxBytes       int64 | ||||
| 	Created       time.Time // time registered with tailcontrol | ||||
| 	LastSeen      time.Time // last seen to tailcontrol | ||||
| 	LastHandshake time.Time // with local wireguard | ||||
| 	KeepAlive     bool | ||||
|  | ||||
| 	// InNetworkMap means that this peer was seen in our latest network map. | ||||
| 	// In theory, all of InNetworkMap and InMagicSock and InEngine should all be true. | ||||
| 	InNetworkMap bool | ||||
|  | ||||
| 	// InMagicSock means that this peer is being tracked by magicsock. | ||||
| 	// In theory, all of InNetworkMap and InMagicSock and InEngine should all be true. | ||||
| 	InMagicSock bool | ||||
|  | ||||
| 	// InEngine means that this peer is tracked by the wireguard engine. | ||||
| 	// In theory, all of InNetworkMap and InMagicSock and InEngine should all be true. | ||||
| 	InEngine bool | ||||
| } | ||||
|  | ||||
| type StatusBuilder struct { | ||||
| 	mu     sync.Mutex | ||||
| 	locked bool | ||||
| 	st     Status | ||||
| } | ||||
|  | ||||
| func (sb *StatusBuilder) Status() *Status { | ||||
| 	sb.mu.Lock() | ||||
| 	defer sb.mu.Unlock() | ||||
| 	sb.locked = true | ||||
| 	return &sb.st | ||||
| } | ||||
|  | ||||
| // AddUser adds a user profile to the status. | ||||
| func (sb *StatusBuilder) AddUser(id tailcfg.UserID, up tailcfg.UserProfile) { | ||||
| 	sb.mu.Lock() | ||||
| 	defer sb.mu.Unlock() | ||||
| 	if sb.locked { | ||||
| 		log.Printf("[unexpected] ipnstate: AddUser after Locked") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if sb.st.User == nil { | ||||
| 		sb.st.User = make(map[tailcfg.UserID]tailcfg.UserProfile) | ||||
| 	} | ||||
|  | ||||
| 	sb.st.User[id] = up | ||||
| } | ||||
|  | ||||
| // AddPeer adds a peer node to the status. | ||||
| // | ||||
| // Its PeerStatus is mixed with any previous status already added. | ||||
| func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) { | ||||
| 	if st == nil { | ||||
| 		panic("nil PeerStatus") | ||||
| 	} | ||||
|  | ||||
| 	sb.mu.Lock() | ||||
| 	defer sb.mu.Unlock() | ||||
| 	if sb.locked { | ||||
| 		log.Printf("[unexpected] ipnstate: AddPeer after Locked") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if sb.st.Peer == nil { | ||||
| 		sb.st.Peer = make(map[key.Public]*PeerStatus) | ||||
| 	} | ||||
| 	e, ok := sb.st.Peer[peer] | ||||
| 	if !ok { | ||||
| 		sb.st.Peer[peer] = st | ||||
| 		st.PublicKey = peer | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if v := st.HostName; v != "" { | ||||
| 		e.HostName = v | ||||
| 	} | ||||
| 	if v := st.UserID; v != 0 { | ||||
| 		e.UserID = v | ||||
| 	} | ||||
| 	if v := st.TailAddr; v != "" { | ||||
| 		e.TailAddr = v | ||||
| 	} | ||||
| 	if v := st.OS; v != "" { | ||||
| 		e.OS = st.OS | ||||
| 	} | ||||
| 	if v := st.Addrs; v != nil { | ||||
| 		e.Addrs = v | ||||
| 	} | ||||
| 	if v := st.CurAddr; v != "" { | ||||
| 		e.CurAddr = v | ||||
| 	} | ||||
| 	if v := st.RxBytes; v != 0 { | ||||
| 		e.RxBytes = v | ||||
| 	} | ||||
| 	if v := st.TxBytes; v != 0 { | ||||
| 		e.TxBytes = v | ||||
| 	} | ||||
| 	if v := st.LastHandshake; !v.IsZero() { | ||||
| 		e.LastHandshake = v | ||||
| 	} | ||||
| 	if v := st.Created; !v.IsZero() { | ||||
| 		e.Created = v | ||||
| 	} | ||||
| 	if v := st.LastSeen; !v.IsZero() { | ||||
| 		e.LastSeen = v | ||||
| 	} | ||||
| 	if st.InNetworkMap { | ||||
| 		e.InNetworkMap = true | ||||
| 	} | ||||
| 	if st.InMagicSock { | ||||
| 		e.InMagicSock = true | ||||
| 	} | ||||
| 	if st.InEngine { | ||||
| 		e.InEngine = true | ||||
| 	} | ||||
| 	if st.KeepAlive { | ||||
| 		e.KeepAlive = true | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type StatusUpdater interface { | ||||
| 	UpdateStatus(*StatusBuilder) | ||||
| } | ||||
							
								
								
									
										45
									
								
								ipn/local.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								ipn/local.go
									
									
									
									
									
								
							| @@ -15,9 +15,11 @@ import ( | ||||
|  | ||||
| 	"github.com/tailscale/wireguard-go/wgcfg" | ||||
| 	"tailscale.com/control/controlclient" | ||||
| 	"tailscale.com/ipn/ipnstate" | ||||
| 	"tailscale.com/portlist" | ||||
| 	"tailscale.com/tailcfg" | ||||
| 	"tailscale.com/types/empty" | ||||
| 	"tailscale.com/types/key" | ||||
| 	"tailscale.com/types/logger" | ||||
| 	"tailscale.com/version" | ||||
| 	"tailscale.com/wgengine" | ||||
| @@ -103,6 +105,49 @@ func (b *LocalBackend) Shutdown() { | ||||
| 	b.e.Wait() | ||||
| } | ||||
|  | ||||
| // Status returns the latest status of the Tailscale network from all the various components. | ||||
| func (b *LocalBackend) Status() *ipnstate.Status { | ||||
| 	sb := new(ipnstate.StatusBuilder) | ||||
| 	b.UpdateStatus(sb) | ||||
| 	return sb.Status() | ||||
| } | ||||
|  | ||||
| func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) { | ||||
| 	b.e.UpdateStatus(sb) | ||||
|  | ||||
| 	b.mu.Lock() | ||||
| 	defer b.mu.Unlock() | ||||
|  | ||||
| 	// TODO: hostinfo, and its networkinfo | ||||
| 	// TODO: EngineStatus copy (and deprecate it?) | ||||
| 	if b.netMapCache != nil { | ||||
| 		for id, up := range b.netMapCache.UserProfiles { | ||||
| 			sb.AddUser(id, up) | ||||
| 		} | ||||
| 		for _, p := range b.netMapCache.Peers { | ||||
| 			var lastSeen time.Time | ||||
| 			if p.LastSeen != nil { | ||||
| 				lastSeen = *p.LastSeen | ||||
| 			} | ||||
| 			var tailAddr string | ||||
| 			if len(p.Addresses) > 0 { | ||||
| 				tailAddr = strings.TrimSuffix(p.Addresses[0].String(), "/32") | ||||
| 			} | ||||
| 			sb.AddPeer(key.Public(p.Key), &ipnstate.PeerStatus{ | ||||
| 				InNetworkMap: true, | ||||
| 				UserID:       p.User, | ||||
| 				TailAddr:     tailAddr, | ||||
| 				HostName:     p.Hostinfo.Hostname, | ||||
| 				OS:           p.Hostinfo.OS, | ||||
| 				KeepAlive:    p.KeepAlive, | ||||
| 				Created:      p.Created, | ||||
| 				LastSeen:     lastSeen, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // SetDecompressor sets a decompression function, which must be a zstd | ||||
| // reader. | ||||
| // | ||||
|   | ||||
| @@ -5,7 +5,11 @@ | ||||
| // Package key defines some types related to curve25519 keys. | ||||
| package key | ||||
|  | ||||
| import "golang.org/x/crypto/curve25519" | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
|  | ||||
| 	"golang.org/x/crypto/curve25519" | ||||
| ) | ||||
|  | ||||
| // Private represents a curve25519 private key. | ||||
| type Private [32]byte | ||||
| @@ -24,6 +28,13 @@ type Public [32]byte | ||||
| // Public reports whether p is the zero value. | ||||
| func (p Public) IsZero() bool { return p == Public{} } | ||||
|  | ||||
| // ShortString returns the Tailscale conventional debug representation | ||||
| // of a public key: the first five base64 digits of the key, in square | ||||
| // brackets. | ||||
| func (p Public) ShortString() string { | ||||
| 	return "[" + base64.StdEncoding.EncodeToString(p[:])[:5] + "]" | ||||
| } | ||||
|  | ||||
| // B32 returns k as the *[32]byte type that's used by the | ||||
| // golang.org/x/crypto packages. This allocates; it might | ||||
| // not be appropriate for performance-sensitive paths. | ||||
|   | ||||
| @@ -33,6 +33,7 @@ import ( | ||||
| 	"tailscale.com/derp" | ||||
| 	"tailscale.com/derp/derphttp" | ||||
| 	"tailscale.com/derp/derpmap" | ||||
| 	"tailscale.com/ipn/ipnstate" | ||||
| 	"tailscale.com/net/dnscache" | ||||
| 	"tailscale.com/net/interfaces" | ||||
| 	"tailscale.com/netcheck" | ||||
| @@ -1898,3 +1899,37 @@ func sbPrintAddr(sb *strings.Builder, a net.UDPAddr) { | ||||
| 	} | ||||
| 	fmt.Fprintf(sb, ":%d", a.Port) | ||||
| } | ||||
|  | ||||
| func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
|  | ||||
| 	for k, as := range c.addrsByKey { | ||||
| 		ps := &ipnstate.PeerStatus{ | ||||
| 			InMagicSock: true, | ||||
| 		} | ||||
| 		for i, ua := range as.addrs { | ||||
| 			uaStr := udpAddrDebugString(ua) | ||||
| 			ps.Addrs = append(ps.Addrs, uaStr) | ||||
| 			if as.curAddr == i { | ||||
| 				ps.CurAddr = uaStr | ||||
| 			} | ||||
| 		} | ||||
| 		if as.roamAddr != nil { | ||||
| 			ps.CurAddr = udpAddrDebugString(*as.roamAddr) | ||||
| 		} | ||||
| 		sb.AddPeer(k, ps) | ||||
| 	} | ||||
|  | ||||
| 	c.foreachActiveDerpSortedLocked(func(node int, ad activeDerp) { | ||||
| 		// TODO(bradfitz): add to ipnstate.StatusBuilder | ||||
| 		//f("<li><b>derp-%v</b>: cr%v,wr%v</li>", node, simpleDur(now.Sub(ad.createTime)), simpleDur(now.Sub(*ad.lastWrite))) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func udpAddrDebugString(ua net.UDPAddr) string { | ||||
| 	if ua.IP.Equal(derpMagicIP) { | ||||
| 		return fmt.Sprintf("derp-%d", ua.Port) | ||||
| 	} | ||||
| 	return ua.String() | ||||
| } | ||||
|   | ||||
| @@ -18,8 +18,10 @@ import ( | ||||
| 	"github.com/tailscale/wireguard-go/device" | ||||
| 	"github.com/tailscale/wireguard-go/tun" | ||||
| 	"github.com/tailscale/wireguard-go/wgcfg" | ||||
| 	"tailscale.com/ipn/ipnstate" | ||||
| 	"tailscale.com/net/interfaces" | ||||
| 	"tailscale.com/tailcfg" | ||||
| 	"tailscale.com/types/key" | ||||
| 	"tailscale.com/types/logger" | ||||
| 	"tailscale.com/wgengine/filter" | ||||
| 	"tailscale.com/wgengine/magicsock" | ||||
| @@ -666,3 +668,21 @@ func (e *userspaceEngine) SetNetInfoCallback(cb NetInfoCallback) { | ||||
| func (e *userspaceEngine) SetDERPEnabled(v bool) { | ||||
| 	e.magicConn.SetDERPEnabled(v) | ||||
| } | ||||
|  | ||||
| func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) { | ||||
| 	st, err := e.getStatus() | ||||
| 	if err != nil { | ||||
| 		e.logf("wgengine: getStatus: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 	for _, ps := range st.Peers { | ||||
| 		sb.AddPeer(key.Public(ps.NodeKey), &ipnstate.PeerStatus{ | ||||
| 			RxBytes:       int64(ps.RxBytes), | ||||
| 			TxBytes:       int64(ps.TxBytes), | ||||
| 			LastHandshake: ps.LastHandshake, | ||||
| 			InEngine:      true, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	e.magicConn.UpdateStatus(sb) | ||||
| } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/tailscale/wireguard-go/wgcfg" | ||||
| 	"tailscale.com/ipn/ipnstate" | ||||
| 	"tailscale.com/wgengine/filter" | ||||
| ) | ||||
|  | ||||
| @@ -74,6 +75,9 @@ func (e *watchdogEngine) SetFilter(filt *filter.Filter) { | ||||
| func (e *watchdogEngine) SetStatusCallback(cb StatusCallback) { | ||||
| 	e.watchdog("SetStatusCallback", func() { e.wrap.SetStatusCallback(cb) }) | ||||
| } | ||||
| func (e *watchdogEngine) UpdateStatus(sb *ipnstate.StatusBuilder) { | ||||
| 	e.watchdog("UpdateStatus", func() { e.wrap.UpdateStatus(sb) }) | ||||
| } | ||||
| func (e *watchdogEngine) SetNetInfoCallback(cb NetInfoCallback) { | ||||
| 	e.watchdog("SetNetInfoCallback", func() { e.wrap.SetNetInfoCallback(cb) }) | ||||
| } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import ( | ||||
| 	"github.com/tailscale/wireguard-go/device" | ||||
| 	"github.com/tailscale/wireguard-go/tun" | ||||
| 	"github.com/tailscale/wireguard-go/wgcfg" | ||||
| 	"tailscale.com/ipn/ipnstate" | ||||
| 	"tailscale.com/tailcfg" | ||||
| 	"tailscale.com/types/logger" | ||||
| 	"tailscale.com/wgengine/filter" | ||||
| @@ -29,6 +30,8 @@ type PeerStatus struct { | ||||
| } | ||||
|  | ||||
| // Status is the Engine status. | ||||
| // | ||||
| // TODO(bradfitz): remove this, subset of ipnstate? Need to migrate users. | ||||
| type Status struct { | ||||
| 	Peers      []PeerStatus | ||||
| 	LocalAddrs []string // TODO(crawshaw): []wgcfg.Endpoint? | ||||
| @@ -144,4 +147,8 @@ type Engine interface { | ||||
| 	// SetNetInfoCallback sets the function to call when a | ||||
| 	// new NetInfo summary is available. | ||||
| 	SetNetInfoCallback(NetInfoCallback) | ||||
|  | ||||
| 	// UpdateStatus populates the network state using the provided | ||||
| 	// status builder. | ||||
| 	UpdateStatus(*ipnstate.StatusBuilder) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user