From ad1cc6cff9ed68536b57c0edb667eeb97585a832 Mon Sep 17 00:00:00 2001 From: James Tucker Date: Fri, 26 Aug 2022 11:35:53 -0700 Subject: [PATCH] wgengine: use Go API rather than UAPI for status Signed-off-by: James Tucker --- cmd/tailscaled/depaware.txt | 1 + wgengine/userspace.go | 133 +++++++----------------------------- 2 files changed, 25 insertions(+), 109 deletions(-) diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index cb62f5790..2e0e99e2b 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -296,6 +296,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+ tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+ tailscale.com/wgengine/wgcfg/nmcfg from tailscale.com/ipn/ipnlocal + 💣 tailscale.com/wgengine/wgint from tailscale.com/wgengine tailscale.com/wgengine/wglog from tailscale.com/wgengine W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router golang.org/x/crypto/acme from tailscale.com/ipn/localapi diff --git a/wgengine/userspace.go b/wgengine/userspace.go index bfc0df59e..9da7a823e 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -6,7 +6,6 @@ package wgengine import ( "bufio" - "bytes" crand "crypto/rand" "errors" "fmt" @@ -19,7 +18,6 @@ import ( "sync" "time" - "go4.org/mem" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun" "tailscale.com/control/controlclient" @@ -53,6 +51,7 @@ import ( "tailscale.com/wgengine/monitor" "tailscale.com/wgengine/router" "tailscale.com/wgengine/wgcfg" + "tailscale.com/wgengine/wgint" "tailscale.com/wgengine/wglog" ) @@ -990,16 +989,11 @@ func (e *userspaceEngine) getStatus() (*Status, error) { derpConns := e.magicConn.DERPs() e.wgLock.Lock() - defer e.wgLock.Unlock() - - e.mu.Lock() - closing := e.closing - e.mu.Unlock() - if closing { - return nil, ErrEngineClosing - } + wgdev := e.wgdev + e.wgLock.Unlock() - if e.wgdev == nil { + // Assume that once created, wgdev is typically not replaced in-flight. + if wgdev == nil { // RequestStatus was invoked before the wgengine has // finished initializing. This can happen when wgegine // provides a callback to magicsock for endpoint @@ -1007,109 +1001,30 @@ func (e *userspaceEngine) getStatus() (*Status, error) { return nil, nil } - pr, pw := io.Pipe() - defer pr.Close() // to unblock writes on error path returns - - errc := make(chan error, 1) - go func() { - defer pw.Close() - // TODO(apenwarr): get rid of silly uapi stuff for in-process comms - // FIXME: get notified of status changes instead of polling. - err := e.wgdev.IpcGetOperation(pw) - if err != nil { - err = fmt.Errorf("IpcGetOperation: %w", err) - } - errc <- err - }() - - pp := make(map[key.NodePublic]ipnstate.PeerStatusLite) - var p ipnstate.PeerStatusLite - - var hst1, hst2, n int64 - - br := e.statusBufioReader - if br != nil { - br.Reset(pr) - } else { - br = bufio.NewReaderSize(pr, 1<<10) - e.statusBufioReader = br - } - for { - line, err := br.ReadSlice('\n') - if err == io.EOF { - break - } - if err != nil { - return nil, fmt.Errorf("reading from UAPI pipe: %w", err) - } - line = bytes.TrimSuffix(line, singleNewline) - k := line - var v mem.RO - if i := bytes.IndexByte(line, '='); i != -1 { - k = line[:i] - v = mem.B(line[i+1:]) - } - switch string(k) { - case "public_key": - pk, err := key.ParseNodePublicUntyped(v) - if err != nil { - return nil, fmt.Errorf("IpcGetOperation: invalid key in line %q", line) - } - if !p.NodeKey.IsZero() { - pp[p.NodeKey] = p - } - p = ipnstate.PeerStatusLite{NodeKey: pk} - case "rx_bytes": - n, err = mem.ParseInt(v, 10, 64) - p.RxBytes = n - if err != nil { - return nil, fmt.Errorf("IpcGetOperation: rx_bytes invalid: %#v", line) - } - case "tx_bytes": - n, err = mem.ParseInt(v, 10, 64) - p.TxBytes = n - if err != nil { - return nil, fmt.Errorf("IpcGetOperation: tx_bytes invalid: %#v", line) - } - case "last_handshake_time_sec": - hst1, err = mem.ParseInt(v, 10, 64) - if err != nil { - return nil, fmt.Errorf("IpcGetOperation: hst1 invalid: %#v", line) - } - case "last_handshake_time_nsec": - hst2, err = mem.ParseInt(v, 10, 64) - if err != nil { - return nil, fmt.Errorf("IpcGetOperation: hst2 invalid: %#v", line) - } - if hst1 != 0 || hst2 != 0 { - p.LastHandshake = time.Unix(hst1, hst2) - } // else leave at time.IsZero() - } - } - if !p.NodeKey.IsZero() { - pp[p.NodeKey] = p - } - if err := <-errc; err != nil { - return nil, fmt.Errorf("IpcGetOperation: %v", err) - } - e.mu.Lock() - defer e.mu.Unlock() + closing := e.closing + peerKeys := make([]key.NodePublic, len(e.peerSequence)) + copy(peerKeys, e.peerSequence) + e.mu.Unlock() - // Do two passes, one to calculate size and the other to populate. - // This code is sensitive to allocations. - npeers := 0 - for _, pk := range e.peerSequence { - if _, ok := pp[pk]; ok { // ignore idle ones not in wireguard-go's config - npeers++ - } + if closing { + return nil, ErrEngineClosing } - peers := make([]ipnstate.PeerStatusLite, 0, npeers) - for _, pk := range e.peerSequence { - if p, ok := pp[pk]; ok { // ignore idle ones not in wireguard-go's config - peers = append(peers, p) + peers := make([]ipnstate.PeerStatusLite, 0, len(peerKeys)) + for _, key := range peerKeys { + // LookupPeer is internally locked in wgdev. + peer := wgdev.LookupPeer(key.Raw32()) + if peer == nil { + continue } + + var p ipnstate.PeerStatusLite + p.NodeKey = key + p.RxBytes = int64(wgint.PeerRxBytes(peer)) + p.TxBytes = int64(wgint.PeerTxBytes(peer)) + p.LastHandshake = time.Unix(0, wgint.PeerLastHandshakeNano(peer)) + peers = append(peers, p) } return &Status{