Skip to content

Commit

Permalink
Add wireguard last handshake and transfer bytes status info
Browse files Browse the repository at this point in the history
  • Loading branch information
lixmal committed Jan 16, 2024
1 parent bdc3082 commit f2182bc
Show file tree
Hide file tree
Showing 16 changed files with 485 additions and 113 deletions.
58 changes: 47 additions & 11 deletions client/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ import (
)

type peerStateDetailOutput struct {
FQDN string `json:"fqdn" yaml:"fqdn"`
IP string `json:"netbirdIp" yaml:"netbirdIp"`
PubKey string `json:"publicKey" yaml:"publicKey"`
Status string `json:"status" yaml:"status"`
LastStatusUpdate time.Time `json:"lastStatusUpdate" yaml:"lastStatusUpdate"`
ConnType string `json:"connectionType" yaml:"connectionType"`
Direct bool `json:"direct" yaml:"direct"`
IceCandidateType iceCandidateType `json:"iceCandidateType" yaml:"iceCandidateType"`
IceCandidateEndpoint iceCandidateType `json:"iceCandidateEndpoint" yaml:"iceCandidateEndpoint"`
FQDN string `json:"fqdn" yaml:"fqdn"`
IP string `json:"netbirdIp" yaml:"netbirdIp"`
PubKey string `json:"publicKey" yaml:"publicKey"`
Status string `json:"status" yaml:"status"`
LastStatusUpdate time.Time `json:"lastStatusUpdate" yaml:"lastStatusUpdate"`
ConnType string `json:"connectionType" yaml:"connectionType"`
Direct bool `json:"direct" yaml:"direct"`
IceCandidateType iceCandidateType `json:"iceCandidateType" yaml:"iceCandidateType"`
IceCandidateEndpoint iceCandidateType `json:"iceCandidateEndpoint" yaml:"iceCandidateEndpoint"`
LastWireguardHandshake time.Time `json:"lastWireguardHandshake" yaml:"lastWireguardHandshake"`
TransferReceived int64 `json:"transferReceived" yaml:"transferReceived"`
TransferSent int64 `json:"transferSent" yaml:"transferSent"`
}

type peersStateOutput struct {
Expand Down Expand Up @@ -299,6 +302,9 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
remoteICEEndpoint := ""
connType := ""
peersConnected := 0
lastHandshake := time.Time{}
transferReceived := int64(0)
transferSent := int64(0)
for _, pbPeerState := range peers {
isPeerConnected := pbPeerState.ConnStatus == peer.StatusConnected.String()
if skipDetailByFilters(pbPeerState, isPeerConnected) {
Expand All @@ -315,6 +321,9 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
if pbPeerState.Relayed {
connType = "Relayed"
}
lastHandshake = pbPeerState.GetLastWireguardHandshake().AsTime().Local()
transferReceived = pbPeerState.GetBytesRx()
transferSent = pbPeerState.GetBytesTx()
}

timeLocal := pbPeerState.GetConnStatusUpdate().AsTime().Local()
Expand All @@ -333,7 +342,10 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
Local: localICEEndpoint,
Remote: remoteICEEndpoint,
},
FQDN: pbPeerState.GetFqdn(),
FQDN: pbPeerState.GetFqdn(),
LastWireguardHandshake: lastHandshake,
TransferReceived: transferReceived,
TransferSent: transferSent,
}

peersStateDetail = append(peersStateDetail, peerState)
Expand Down Expand Up @@ -502,6 +514,11 @@ func parsePeers(peers peersStateOutput) string {
remoteICEEndpoint = peerState.IceCandidateEndpoint.Remote
}

lastWireguardHandshake := "-"
if !peerState.LastWireguardHandshake.IsZero() {
lastWireguardHandshake = peerState.LastWireguardHandshake.Format("2006-01-02 15:04:05")
}

peerString := fmt.Sprintf(
"\n %s:\n"+
" NetBird IP: %s\n"+
Expand All @@ -512,7 +529,9 @@ func parsePeers(peers peersStateOutput) string {
" Direct: %t\n"+
" ICE candidate (Local/Remote): %s/%s\n"+
" ICE candidate endpoints (Local/Remote): %s/%s\n"+
" Last connection update: %s\n",
" Last connection update: %s\n"+
" Last Wireguard handshake: %s\n"+
" Transfer status (received/sent) %s/%s\n",
peerState.FQDN,
peerState.IP,
peerState.PubKey,
Expand All @@ -524,6 +543,9 @@ func parsePeers(peers peersStateOutput) string {
localICEEndpoint,
remoteICEEndpoint,
peerState.LastStatusUpdate.Format("2006-01-02 15:04:05"),
lastWireguardHandshake,
toIEC(peerState.TransferReceived),
toIEC(peerState.TransferSent),
)

peersString += peerString
Expand Down Expand Up @@ -563,3 +585,17 @@ func skipDetailByFilters(peerState *proto.PeerState, isConnected bool) bool {

return statusEval || ipEval || nameEval
}

func toIEC(b int64) string {
const unit = 1024
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %ciB",
float64(b)/float64(div), "KMGTPE"[exp])
}
41 changes: 33 additions & 8 deletions client/internal/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,33 @@ import (

// RunClient with main logic.
func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status) error {
return runClient(ctx, config, statusRecorder, MobileDependency{}, nil, nil, nil)
return runClient(ctx, config, statusRecorder, MobileDependency{}, nil, nil, nil, nil)
}

// RunClientWithProbes runs the client's main logic with probes attached
func RunClientWithProbes(ctx context.Context, config *Config, statusRecorder *peer.Status, mgmProbe *Probe, signalProbe *Probe, relayProbe *Probe) error {
return runClient(ctx, config, statusRecorder, MobileDependency{}, mgmProbe, signalProbe, relayProbe)
func RunClientWithProbes(
ctx context.Context,
config *Config,
statusRecorder *peer.Status,
mgmProbe *Probe,
signalProbe *Probe,
relayProbe *Probe,
wgProbe *Probe,
) error {
return runClient(ctx, config, statusRecorder, MobileDependency{}, mgmProbe, signalProbe, relayProbe, wgProbe)
}

// RunClientMobile with main logic on mobile system
func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, networkChangeListener listener.NetworkChangeListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error {
func RunClientMobile(
ctx context.Context,
config *Config,
statusRecorder *peer.Status,
tunAdapter iface.TunAdapter,
iFaceDiscover stdnet.ExternalIFaceDiscover,
networkChangeListener listener.NetworkChangeListener,
dnsAddresses []string,
dnsReadyListener dns.ReadyListener,
) error {
// in case of non Android os these variables will be nil
mobileDependency := MobileDependency{
TunAdapter: tunAdapter,
Expand All @@ -45,16 +62,23 @@ func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.S
HostDNSAddresses: dnsAddresses,
DnsReadyListener: dnsReadyListener,
}
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil)
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil, nil)
}

func RunClientiOS(ctx context.Context, config *Config, statusRecorder *peer.Status, fileDescriptor int32, networkChangeListener listener.NetworkChangeListener, dnsManager dns.IosDnsManager) error {
func RunClientiOS(
ctx context.Context,
config *Config,
statusRecorder *peer.Status,
fileDescriptor int32,
networkChangeListener listener.NetworkChangeListener,
dnsManager dns.IosDnsManager,
) error {
mobileDependency := MobileDependency{
FileDescriptor: fileDescriptor,
NetworkChangeListener: networkChangeListener,
DnsManager: dnsManager,
}
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil)
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil, nil)
}

func runClient(
Expand All @@ -65,6 +89,7 @@ func runClient(
mgmProbe *Probe,
signalProbe *Probe,
relayProbe *Probe,
wgProbe *Probe,
) error {
log.Infof("starting NetBird client version %s", version.NetbirdVersion())

Expand Down Expand Up @@ -194,7 +219,7 @@ func runClient(
return wrapErr(err)
}

engine := NewEngine(engineCtx, cancel, signalClient, mgmClient, engineConfig, mobileDependency, statusRecorder, mgmProbe, signalProbe, relayProbe)
engine := NewEngine(engineCtx, cancel, signalClient, mgmClient, engineConfig, mobileDependency, statusRecorder, mgmProbe, signalProbe, relayProbe, wgProbe)
err = engine.Start()
if err != nil {
log.Errorf("error while starting Netbird Connection Engine: %s", err)
Expand Down
4 changes: 4 additions & 0 deletions client/internal/dns/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func (w *mocWGIface) SetFilter(filter iface.PacketFilter) error {
return nil
}

func (w *mocWGIface) GetStats(_ string) (iface.WGStats, error) {
return iface.WGStats{}, nil
}

var zoneRecords = []nbdns.SimpleRecord{
{
Name: "peera.netbird.cloud",
Expand Down
1 change: 1 addition & 0 deletions client/internal/dns/wgiface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ type WGIface interface {
IsUserspaceBind() bool
GetFilter() iface.PacketFilter
GetDevice() *iface.DeviceWrapper
GetStats(peerKey string) (iface.WGStats, error)
}
1 change: 1 addition & 0 deletions client/internal/dns/wgiface_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ type WGIface interface {
IsUserspaceBind() bool
GetFilter() iface.PacketFilter
GetDevice() *iface.DeviceWrapper
GetStats(peerKey string) (iface.WGStats, error)
GetInterfaceGUIDString() (string, error)
}
23 changes: 23 additions & 0 deletions client/internal/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ type Engine struct {
mgmProbe *Probe
signalProbe *Probe
relayProbe *Probe
wgProbe *Probe
}

// Peer is an instance of the Connection Peer
Expand All @@ -150,6 +151,7 @@ func NewEngine(
mgmProbe *Probe,
signalProbe *Probe,
relayProbe *Probe,
wgProbe *Probe,
) *Engine {

return &Engine{
Expand All @@ -170,6 +172,7 @@ func NewEngine(
mgmProbe: mgmProbe,
signalProbe: signalProbe,
relayProbe: relayProbe,
wgProbe: wgProbe,
}
}

Expand Down Expand Up @@ -1226,6 +1229,26 @@ func (e *Engine) receiveProbeEvents() {
return healthy
})
}

if e.wgProbe != nil {
go e.wgProbe.Receive(e.ctx, func() bool {
log.Debug("received wg probe request")

for _, peer := range e.peerConns {
key := peer.GetKey()
wgStats, err := peer.GetConf().WgConfig.WgInterface.GetStats(key)
if err != nil {
log.Debugf("failed to get wg stats for peer %s: %s", key, err)
}
// wgStats could be zero value, in which case we just reset the stats
if err := e.statusRecorder.UpdateWireguardPeerState(key, wgStats); err != nil {
log.Debugf("failed to update wg stats for peer %s: %s", key, err)
}
}

return true
})
}
}

func (e *Engine) probeSTUNs() []relay.ProbeResult {
Expand Down
1 change: 1 addition & 0 deletions client/internal/peer/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ func (conn *Conn) cleanup() error {
// todo rethink status updates
log.Debugf("error while updating peer's %s state, err: %v", conn.config.Key, err)
}
conn.statusRecorder.UpdateWireguardPeerState(conn.config.Key, iface.WGStats{})

log.Debugf("cleaned up connection to peer %s", conn.config.Key)
if err1 != nil {
Expand Down
23 changes: 23 additions & 0 deletions client/internal/peer/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/netbirdio/netbird/client/internal/relay"
"github.com/netbirdio/netbird/iface"
)

// State contains the latest state of a peer
Expand All @@ -21,6 +22,9 @@ type State struct {
RemoteIceCandidateType string
LocalIceCandidateEndpoint string
RemoteIceCandidateEndpoint string
LastWireguardHandshake time.Time
BytesTx int64
BytesRx int64
}

// LocalPeerState contains the latest state of the local peer
Expand Down Expand Up @@ -186,6 +190,25 @@ func (d *Status) UpdatePeerState(receivedState State) error {
return nil
}

// UpdateWireguardPeerState updates the wireguard bits of the peer state
func (d *Status) UpdateWireguardPeerState(pubKey string, wgStats iface.WGStats) error {
d.mux.Lock()
defer d.mux.Unlock()

peerState, ok := d.peers[pubKey]
if !ok {
return errors.New("peer doesn't exist")
}

peerState.LastWireguardHandshake = wgStats.LastHandshake
peerState.BytesRx = wgStats.RxBytes
peerState.BytesTx = wgStats.TxBytes

d.peers[pubKey] = peerState

return nil
}

func shouldSkipNotify(received, curr State) bool {
switch {
case received.ConnStatus == StatusConnecting:
Expand Down

0 comments on commit f2182bc

Please sign in to comment.