mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-26 04:31:56 +02:00
[client] Add output similar to wg show to the debug package (#3922)
This commit is contained in:
parent
609654eee7
commit
9424b88db2
@ -69,6 +69,22 @@ func (a *Anonymizer) AnonymizeIP(ip netip.Addr) netip.Addr {
|
|||||||
return a.ipAnonymizer[ip]
|
return a.ipAnonymizer[ip]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Anonymizer) AnonymizeUDPAddr(addr net.UDPAddr) net.UDPAddr {
|
||||||
|
// Convert IP to netip.Addr
|
||||||
|
ip, ok := netip.AddrFromSlice(addr.IP)
|
||||||
|
if !ok {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
anonIP := a.AnonymizeIP(ip)
|
||||||
|
|
||||||
|
return net.UDPAddr{
|
||||||
|
IP: anonIP.AsSlice(),
|
||||||
|
Port: addr.Port,
|
||||||
|
Zone: addr.Zone,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// isInAnonymizedRange checks if an IP is within the range of already assigned anonymized IPs
|
// isInAnonymizedRange checks if an IP is within the range of already assigned anonymized IPs
|
||||||
func (a *Anonymizer) isInAnonymizedRange(ip netip.Addr) bool {
|
func (a *Anonymizer) isInAnonymizedRange(ip netip.Addr) bool {
|
||||||
if ip.Is4() && ip.Compare(a.startAnonIPv4) >= 0 && ip.Compare(a.currentAnonIPv4) <= 0 {
|
if ip.Is4() && ip.Compare(a.startAnonIPv4) >= 0 && ip.Compare(a.currentAnonIPv4) <= 0 {
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var zeroKey wgtypes.Key
|
||||||
|
|
||||||
type KernelConfigurer struct {
|
type KernelConfigurer struct {
|
||||||
deviceName string
|
deviceName string
|
||||||
}
|
}
|
||||||
@ -201,6 +203,47 @@ func (c *KernelConfigurer) configure(config wgtypes.Config) error {
|
|||||||
func (c *KernelConfigurer) Close() {
|
func (c *KernelConfigurer) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *KernelConfigurer) FullStats() (*Stats, error) {
|
||||||
|
wg, err := wgctrl.New()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("wgctl: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = wg.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Got error while closing wgctl: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wgDevice, err := wg.Device(c.deviceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get device %s: %w", c.deviceName, err)
|
||||||
|
}
|
||||||
|
fullStats := &Stats{
|
||||||
|
DeviceName: wgDevice.Name,
|
||||||
|
PublicKey: wgDevice.PublicKey.String(),
|
||||||
|
ListenPort: wgDevice.ListenPort,
|
||||||
|
FWMark: wgDevice.FirewallMark,
|
||||||
|
Peers: []Peer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range wgDevice.Peers {
|
||||||
|
peer := Peer{
|
||||||
|
PublicKey: p.PublicKey.String(),
|
||||||
|
AllowedIPs: p.AllowedIPs,
|
||||||
|
TxBytes: p.TransmitBytes,
|
||||||
|
RxBytes: p.ReceiveBytes,
|
||||||
|
LastHandshake: p.LastHandshakeTime,
|
||||||
|
PresharedKey: p.PresharedKey != zeroKey,
|
||||||
|
}
|
||||||
|
if p.Endpoint != nil {
|
||||||
|
peer.Endpoint = *p.Endpoint
|
||||||
|
}
|
||||||
|
fullStats.Peers = append(fullStats.Peers, peer)
|
||||||
|
}
|
||||||
|
return fullStats, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *KernelConfigurer) GetStats() (map[string]WGStats, error) {
|
func (c *KernelConfigurer) GetStats() (map[string]WGStats, error) {
|
||||||
stats := make(map[string]WGStats)
|
stats := make(map[string]WGStats)
|
||||||
wg, err := wgctrl.New()
|
wg, err := wgctrl.New()
|
||||||
|
@ -19,10 +19,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
privateKey = "private_key"
|
||||||
ipcKeyLastHandshakeTimeSec = "last_handshake_time_sec"
|
ipcKeyLastHandshakeTimeSec = "last_handshake_time_sec"
|
||||||
ipcKeyLastHandshakeTimeNsec = "last_handshake_time_nsec"
|
ipcKeyLastHandshakeTimeNsec = "last_handshake_time_nsec"
|
||||||
ipcKeyTxBytes = "tx_bytes"
|
ipcKeyTxBytes = "tx_bytes"
|
||||||
ipcKeyRxBytes = "rx_bytes"
|
ipcKeyRxBytes = "rx_bytes"
|
||||||
|
allowedIP = "allowed_ip"
|
||||||
|
endpoint = "endpoint"
|
||||||
|
fwmark = "fwmark"
|
||||||
|
listenPort = "listen_port"
|
||||||
|
publicKey = "public_key"
|
||||||
|
presharedKey = "preshared_key"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found")
|
var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found")
|
||||||
@ -186,6 +193,15 @@ func (c *WGUSPConfigurer) RemoveAllowedIP(peerKey string, ip string) error {
|
|||||||
return c.device.IpcSet(toWgUserspaceString(config))
|
return c.device.IpcSet(toWgUserspaceString(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *WGUSPConfigurer) FullStats() (*Stats, error) {
|
||||||
|
ipcStr, err := c.device.IpcGet()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("IpcGet failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseStatus(c.deviceName, ipcStr)
|
||||||
|
}
|
||||||
|
|
||||||
// startUAPI starts the UAPI listener for managing the WireGuard interface via external tool
|
// startUAPI starts the UAPI listener for managing the WireGuard interface via external tool
|
||||||
func (t *WGUSPConfigurer) startUAPI() {
|
func (t *WGUSPConfigurer) startUAPI() {
|
||||||
var err error
|
var err error
|
||||||
@ -365,3 +381,136 @@ func getFwmark() int {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hexToWireguardKey(hexKey string) (wgtypes.Key, error) {
|
||||||
|
// Decode hex string to bytes
|
||||||
|
keyBytes, err := hex.DecodeString(hexKey)
|
||||||
|
if err != nil {
|
||||||
|
return wgtypes.Key{}, fmt.Errorf("failed to decode hex key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have the right number of bytes (WireGuard keys are 32 bytes)
|
||||||
|
if len(keyBytes) != 32 {
|
||||||
|
return wgtypes.Key{}, fmt.Errorf("invalid key length: expected 32 bytes, got %d", len(keyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to wgtypes.Key
|
||||||
|
var key wgtypes.Key
|
||||||
|
copy(key[:], keyBytes)
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStatus(deviceName, ipcStr string) (*Stats, error) {
|
||||||
|
stats := &Stats{DeviceName: deviceName}
|
||||||
|
var currentPeer *Peer
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(ipcStr), "\n") {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := parts[0]
|
||||||
|
val := parts[1]
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case privateKey:
|
||||||
|
key, err := hexToWireguardKey(val)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to parse private key: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stats.PublicKey = key.PublicKey().String()
|
||||||
|
case publicKey:
|
||||||
|
// Save previous peer
|
||||||
|
if currentPeer != nil {
|
||||||
|
stats.Peers = append(stats.Peers, *currentPeer)
|
||||||
|
}
|
||||||
|
key, err := hexToWireguardKey(val)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to parse public key: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentPeer = &Peer{
|
||||||
|
PublicKey: key.String(),
|
||||||
|
}
|
||||||
|
case listenPort:
|
||||||
|
if port, err := strconv.Atoi(val); err == nil {
|
||||||
|
stats.ListenPort = port
|
||||||
|
}
|
||||||
|
case fwmark:
|
||||||
|
if fwmark, err := strconv.Atoi(val); err == nil {
|
||||||
|
stats.FWMark = fwmark
|
||||||
|
}
|
||||||
|
case endpoint:
|
||||||
|
if currentPeer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
host, portStr, err := net.SplitHostPort(strings.Trim(val, "[]"))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to parse endpoint: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
port, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to parse endpoint port: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentPeer.Endpoint = net.UDPAddr{
|
||||||
|
IP: net.ParseIP(host),
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
case allowedIP:
|
||||||
|
if currentPeer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, ipnet, err := net.ParseCIDR(val)
|
||||||
|
if err == nil {
|
||||||
|
currentPeer.AllowedIPs = append(currentPeer.AllowedIPs, *ipnet)
|
||||||
|
}
|
||||||
|
case ipcKeyTxBytes:
|
||||||
|
if currentPeer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rxBytes, err := toBytes(val)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentPeer.TxBytes = rxBytes
|
||||||
|
case ipcKeyRxBytes:
|
||||||
|
if currentPeer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rxBytes, err := toBytes(val)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentPeer.RxBytes = rxBytes
|
||||||
|
|
||||||
|
case ipcKeyLastHandshakeTimeSec:
|
||||||
|
if currentPeer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ts, err := toLastHandshake(val)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currentPeer.LastHandshake = ts
|
||||||
|
case presharedKey:
|
||||||
|
if currentPeer == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if val != "" {
|
||||||
|
currentPeer.PresharedKey = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentPeer != nil {
|
||||||
|
stats.Peers = append(stats.Peers, *currentPeer)
|
||||||
|
}
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
24
client/iface/configurer/wgshow.go
Normal file
24
client/iface/configurer/wgshow.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package configurer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Peer struct {
|
||||||
|
PublicKey string
|
||||||
|
Endpoint net.UDPAddr
|
||||||
|
AllowedIPs []net.IPNet
|
||||||
|
TxBytes int64
|
||||||
|
RxBytes int64
|
||||||
|
LastHandshake time.Time
|
||||||
|
PresharedKey bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
DeviceName string
|
||||||
|
PublicKey string
|
||||||
|
ListenPort int
|
||||||
|
FWMark int
|
||||||
|
Peers []Peer
|
||||||
|
}
|
@ -17,4 +17,5 @@ type WGConfigurer interface {
|
|||||||
RemoveAllowedIP(peerKey string, allowedIP string) error
|
RemoveAllowedIP(peerKey string, allowedIP string) error
|
||||||
Close()
|
Close()
|
||||||
GetStats() (map[string]configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
|
FullStats() (*configurer.Stats, error)
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,10 @@ func (w *WGIface) GetStats() (map[string]configurer.WGStats, error) {
|
|||||||
return w.configurer.GetStats()
|
return w.configurer.GetStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WGIface) FullStats() (*configurer.Stats, error) {
|
||||||
|
return w.configurer.FullStats()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WGIface) waitUntilRemoved() error {
|
func (w *WGIface) waitUntilRemoved() error {
|
||||||
maxWaitTime := 5 * time.Second
|
maxWaitTime := 5 * time.Second
|
||||||
timeout := time.NewTimer(maxWaitTime)
|
timeout := time.NewTimer(maxWaitTime)
|
||||||
|
@ -270,11 +270,16 @@ func (g *BundleGenerator) createArchive() error {
|
|||||||
log.Errorf("Failed to add corrupted state files to debug bundle: %v", err)
|
log.Errorf("Failed to add corrupted state files to debug bundle: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := g.addWgShow(); err != nil {
|
||||||
|
log.Errorf("Failed to add wg show output: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if g.logFile != "console" {
|
if g.logFile != "console" {
|
||||||
if err := g.addLogfile(); err != nil {
|
if err := g.addLogfile(); err != nil {
|
||||||
return fmt.Errorf("add log file: %w", err)
|
return fmt.Errorf("add log file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
66
client/internal/debug/wgshow.go
Normal file
66
client/internal/debug/wgshow.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WGIface interface {
|
||||||
|
FullStats() (*configurer.Stats, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *BundleGenerator) addWgShow() error {
|
||||||
|
result, err := g.statusRecorder.PeersStatus()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
output := g.toWGShowFormat(result)
|
||||||
|
reader := bytes.NewReader([]byte(output))
|
||||||
|
|
||||||
|
if err := g.addFileToZip(reader, "wgshow.txt"); err != nil {
|
||||||
|
return fmt.Errorf("add wg show to zip: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *BundleGenerator) toWGShowFormat(s *configurer.Stats) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
sb.WriteString(fmt.Sprintf("interface: %s\n", s.DeviceName))
|
||||||
|
sb.WriteString(fmt.Sprintf(" public key: %s\n", s.PublicKey))
|
||||||
|
sb.WriteString(fmt.Sprintf(" listen port: %d\n", s.ListenPort))
|
||||||
|
if s.FWMark != 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf(" fwmark: %#x\n", s.FWMark))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, peer := range s.Peers {
|
||||||
|
sb.WriteString(fmt.Sprintf("\npeer: %s\n", peer.PublicKey))
|
||||||
|
if peer.Endpoint.IP != nil {
|
||||||
|
if g.anonymize {
|
||||||
|
anonEndpoint := g.anonymizer.AnonymizeUDPAddr(peer.Endpoint)
|
||||||
|
sb.WriteString(fmt.Sprintf(" endpoint: %s\n", anonEndpoint.String()))
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf(" endpoint: %s\n", peer.Endpoint.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(peer.AllowedIPs) > 0 {
|
||||||
|
var ipStrings []string
|
||||||
|
for _, ipnet := range peer.AllowedIPs {
|
||||||
|
ipStrings = append(ipStrings, ipnet.String())
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf(" allowed ips: %s\n", strings.Join(ipStrings, ", ")))
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf(" latest handshake: %s\n", peer.LastHandshake.Format(time.RFC1123)))
|
||||||
|
sb.WriteString(fmt.Sprintf(" transfer: %d B received, %d B sent\n", peer.RxBytes, peer.TxBytes))
|
||||||
|
if peer.PresharedKey {
|
||||||
|
sb.WriteString(" preshared key: (hidden)\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
@ -359,6 +359,7 @@ func (e *Engine) Start() error {
|
|||||||
return fmt.Errorf("new wg interface: %w", err)
|
return fmt.Errorf("new wg interface: %w", err)
|
||||||
}
|
}
|
||||||
e.wgInterface = wgIface
|
e.wgInterface = wgIface
|
||||||
|
e.statusRecorder.SetWgIface(wgIface)
|
||||||
|
|
||||||
// start flow manager right after interface creation
|
// start flow manager right after interface creation
|
||||||
publicKey := e.config.WgPrivateKey.PublicKey()
|
publicKey := e.config.WgPrivateKey.PublicKey()
|
||||||
@ -380,7 +381,6 @@ func (e *Engine) Start() error {
|
|||||||
return fmt.Errorf("run rosenpass manager: %w", err)
|
return fmt.Errorf("run rosenpass manager: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.stateManager.Start()
|
e.stateManager.Start()
|
||||||
|
|
||||||
initialRoutes, dnsServer, err := e.newDnsServer()
|
initialRoutes, dnsServer, err := e.newDnsServer()
|
||||||
@ -1453,6 +1453,7 @@ func (e *Engine) close() {
|
|||||||
log.Errorf("failed closing Netbird interface %s %v", e.config.WgIfaceName, err)
|
log.Errorf("failed closing Netbird interface %s %v", e.config.WgIfaceName, err)
|
||||||
}
|
}
|
||||||
e.wgInterface = nil
|
e.wgInterface = nil
|
||||||
|
e.statusRecorder.SetWgIface(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isNil(e.sshServer) {
|
if !isNil(e.sshServer) {
|
||||||
|
@ -99,6 +99,10 @@ type MockWGIface struct {
|
|||||||
GetNetFunc func() *netstack.Net
|
GetNetFunc func() *netstack.Net
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockWGIface) FullStats() (*configurer.Stats, error) {
|
||||||
|
return nil, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MockWGIface) GetInterfaceGUIDString() (string, error) {
|
func (m *MockWGIface) GetInterfaceGUIDString() (string, error) {
|
||||||
return m.GetInterfaceGUIDStringFunc()
|
return m.GetInterfaceGUIDStringFunc()
|
||||||
}
|
}
|
||||||
|
@ -37,4 +37,5 @@ type wgIfaceBase interface {
|
|||||||
GetWGDevice() *wgdevice.Device
|
GetWGDevice() *wgdevice.Device
|
||||||
GetStats() (map[string]configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
GetNet() *netstack.Net
|
GetNet() *netstack.Net
|
||||||
|
FullStats() (*configurer.Stats, error)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package peer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
@ -32,6 +33,10 @@ type ResolvedDomainInfo struct {
|
|||||||
ParentDomain domain.Domain
|
ParentDomain domain.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WGIfaceStatus interface {
|
||||||
|
FullStats() (*configurer.Stats, error)
|
||||||
|
}
|
||||||
|
|
||||||
type EventListener interface {
|
type EventListener interface {
|
||||||
OnEvent(event *proto.SystemEvent)
|
OnEvent(event *proto.SystemEvent)
|
||||||
}
|
}
|
||||||
@ -202,6 +207,7 @@ type Status struct {
|
|||||||
ingressGwMgr *ingressgw.Manager
|
ingressGwMgr *ingressgw.Manager
|
||||||
|
|
||||||
routeIDLookup routeIDLookup
|
routeIDLookup routeIDLookup
|
||||||
|
wgIface WGIfaceStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRecorder returns a new Status instance
|
// NewRecorder returns a new Status instance
|
||||||
@ -1078,6 +1084,23 @@ func (d *Status) GetEventHistory() []*proto.SystemEvent {
|
|||||||
return d.eventQueue.GetAll()
|
return d.eventQueue.GetAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Status) SetWgIface(wgInterface WGIfaceStatus) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
d.wgIface = wgInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Status) PeersStatus() (*configurer.Stats, error) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
if d.wgIface == nil {
|
||||||
|
return nil, fmt.Errorf("wgInterface is nil, cannot retrieve peers status")
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.wgIface.FullStats()
|
||||||
|
}
|
||||||
|
|
||||||
type EventQueue struct {
|
type EventQueue struct {
|
||||||
maxSize int
|
maxSize int
|
||||||
events []*proto.SystemEvent
|
events []*proto.SystemEvent
|
||||||
|
Loading…
x
Reference in New Issue
Block a user