mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-12 00:19:47 +02:00
introduces a new flag --filter-by-connection-type to the status command. It allows users to filter peers by connection type (P2P or Relayed) in both JSON and detailed views. Input validation is added in parseFilters() to ensure proper usage, and --detail is auto-enabled if no output format is specified (consistent with other filters).
732 lines
23 KiB
Go
732 lines
23 KiB
Go
package status
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"os"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/netbirdio/netbird/client/anonymize"
|
|
"github.com/netbirdio/netbird/client/internal/peer"
|
|
"github.com/netbirdio/netbird/client/proto"
|
|
"github.com/netbirdio/netbird/management/domain"
|
|
"github.com/netbirdio/netbird/version"
|
|
)
|
|
|
|
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"`
|
|
IceCandidateType IceCandidateType `json:"iceCandidateType" yaml:"iceCandidateType"`
|
|
IceCandidateEndpoint IceCandidateType `json:"iceCandidateEndpoint" yaml:"iceCandidateEndpoint"`
|
|
RelayAddress string `json:"relayAddress" yaml:"relayAddress"`
|
|
LastWireguardHandshake time.Time `json:"lastWireguardHandshake" yaml:"lastWireguardHandshake"`
|
|
TransferReceived int64 `json:"transferReceived" yaml:"transferReceived"`
|
|
TransferSent int64 `json:"transferSent" yaml:"transferSent"`
|
|
Latency time.Duration `json:"latency" yaml:"latency"`
|
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
|
Networks []string `json:"networks" yaml:"networks"`
|
|
}
|
|
|
|
type PeersStateOutput struct {
|
|
Total int `json:"total" yaml:"total"`
|
|
Connected int `json:"connected" yaml:"connected"`
|
|
Details []PeerStateDetailOutput `json:"details" yaml:"details"`
|
|
}
|
|
|
|
type SignalStateOutput struct {
|
|
URL string `json:"url" yaml:"url"`
|
|
Connected bool `json:"connected" yaml:"connected"`
|
|
Error string `json:"error" yaml:"error"`
|
|
}
|
|
|
|
type ManagementStateOutput struct {
|
|
URL string `json:"url" yaml:"url"`
|
|
Connected bool `json:"connected" yaml:"connected"`
|
|
Error string `json:"error" yaml:"error"`
|
|
}
|
|
|
|
type RelayStateOutputDetail struct {
|
|
URI string `json:"uri" yaml:"uri"`
|
|
Available bool `json:"available" yaml:"available"`
|
|
Error string `json:"error" yaml:"error"`
|
|
}
|
|
|
|
type RelayStateOutput struct {
|
|
Total int `json:"total" yaml:"total"`
|
|
Available int `json:"available" yaml:"available"`
|
|
Details []RelayStateOutputDetail `json:"details" yaml:"details"`
|
|
}
|
|
|
|
type IceCandidateType struct {
|
|
Local string `json:"local" yaml:"local"`
|
|
Remote string `json:"remote" yaml:"remote"`
|
|
}
|
|
|
|
type NsServerGroupStateOutput struct {
|
|
Servers []string `json:"servers" yaml:"servers"`
|
|
Domains []string `json:"domains" yaml:"domains"`
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
Error string `json:"error" yaml:"error"`
|
|
}
|
|
|
|
type OutputOverview struct {
|
|
Peers PeersStateOutput `json:"peers" yaml:"peers"`
|
|
CliVersion string `json:"cliVersion" yaml:"cliVersion"`
|
|
DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"`
|
|
ManagementState ManagementStateOutput `json:"management" yaml:"management"`
|
|
SignalState SignalStateOutput `json:"signal" yaml:"signal"`
|
|
Relays RelayStateOutput `json:"relays" yaml:"relays"`
|
|
IP string `json:"netbirdIp" yaml:"netbirdIp"`
|
|
PubKey string `json:"publicKey" yaml:"publicKey"`
|
|
KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"`
|
|
FQDN string `json:"fqdn" yaml:"fqdn"`
|
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
|
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
|
|
Networks []string `json:"networks" yaml:"networks"`
|
|
NumberOfForwardingRules int `json:"forwardingRules" yaml:"forwardingRules"`
|
|
NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
|
Events []SystemEventOutput `json:"events" yaml:"events"`
|
|
LazyConnectionEnabled bool `json:"lazyConnectionEnabled" yaml:"lazyConnectionEnabled"`
|
|
}
|
|
|
|
func ConvertToStatusOutputOverview(resp *proto.StatusResponse, anon bool, statusFilter string, prefixNamesFilter []string, prefixNamesFilterMap map[string]struct{}, ipsFilter map[string]struct{}, connectionTypeFilter string) OutputOverview {
|
|
pbFullStatus := resp.GetFullStatus()
|
|
|
|
managementState := pbFullStatus.GetManagementState()
|
|
managementOverview := ManagementStateOutput{
|
|
URL: managementState.GetURL(),
|
|
Connected: managementState.GetConnected(),
|
|
Error: managementState.Error,
|
|
}
|
|
|
|
signalState := pbFullStatus.GetSignalState()
|
|
signalOverview := SignalStateOutput{
|
|
URL: signalState.GetURL(),
|
|
Connected: signalState.GetConnected(),
|
|
Error: signalState.Error,
|
|
}
|
|
|
|
relayOverview := mapRelays(pbFullStatus.GetRelays())
|
|
peersOverview := mapPeers(resp.GetFullStatus().GetPeers(), statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilter, connectionTypeFilter)
|
|
|
|
overview := OutputOverview{
|
|
Peers: peersOverview,
|
|
CliVersion: version.NetbirdVersion(),
|
|
DaemonVersion: resp.GetDaemonVersion(),
|
|
ManagementState: managementOverview,
|
|
SignalState: signalOverview,
|
|
Relays: relayOverview,
|
|
IP: pbFullStatus.GetLocalPeerState().GetIP(),
|
|
PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(),
|
|
KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(),
|
|
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
|
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
|
|
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
|
|
Networks: pbFullStatus.GetLocalPeerState().GetNetworks(),
|
|
NumberOfForwardingRules: int(pbFullStatus.GetNumberOfForwardingRules()),
|
|
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
|
Events: mapEvents(pbFullStatus.GetEvents()),
|
|
LazyConnectionEnabled: pbFullStatus.GetLazyConnectionEnabled(),
|
|
}
|
|
|
|
if anon {
|
|
anonymizer := anonymize.NewAnonymizer(anonymize.DefaultAddresses())
|
|
anonymizeOverview(anonymizer, &overview)
|
|
}
|
|
|
|
return overview
|
|
}
|
|
|
|
func mapRelays(relays []*proto.RelayState) RelayStateOutput {
|
|
var relayStateDetail []RelayStateOutputDetail
|
|
|
|
var relaysAvailable int
|
|
for _, relay := range relays {
|
|
available := relay.GetAvailable()
|
|
relayStateDetail = append(relayStateDetail,
|
|
RelayStateOutputDetail{
|
|
URI: relay.URI,
|
|
Available: available,
|
|
Error: relay.GetError(),
|
|
},
|
|
)
|
|
|
|
if available {
|
|
relaysAvailable++
|
|
}
|
|
}
|
|
|
|
return RelayStateOutput{
|
|
Total: len(relays),
|
|
Available: relaysAvailable,
|
|
Details: relayStateDetail,
|
|
}
|
|
}
|
|
|
|
func mapNSGroups(servers []*proto.NSGroupState) []NsServerGroupStateOutput {
|
|
mappedNSGroups := make([]NsServerGroupStateOutput, 0, len(servers))
|
|
for _, pbNsGroupServer := range servers {
|
|
mappedNSGroups = append(mappedNSGroups, NsServerGroupStateOutput{
|
|
Servers: pbNsGroupServer.GetServers(),
|
|
Domains: pbNsGroupServer.GetDomains(),
|
|
Enabled: pbNsGroupServer.GetEnabled(),
|
|
Error: pbNsGroupServer.GetError(),
|
|
})
|
|
}
|
|
return mappedNSGroups
|
|
}
|
|
|
|
func mapPeers(
|
|
peers []*proto.PeerState,
|
|
statusFilter string,
|
|
prefixNamesFilter []string,
|
|
prefixNamesFilterMap map[string]struct{},
|
|
ipsFilter map[string]struct{},
|
|
connectionTypeFilter string,
|
|
) PeersStateOutput {
|
|
var peersStateDetail []PeerStateDetailOutput
|
|
peersConnected := 0
|
|
for _, pbPeerState := range peers {
|
|
localICE := ""
|
|
remoteICE := ""
|
|
localICEEndpoint := ""
|
|
remoteICEEndpoint := ""
|
|
relayServerAddress := ""
|
|
connType := ""
|
|
lastHandshake := time.Time{}
|
|
transferReceived := int64(0)
|
|
transferSent := int64(0)
|
|
|
|
isPeerConnected := pbPeerState.ConnStatus == peer.StatusConnected.String()
|
|
if skipDetailByFilters(pbPeerState, pbPeerState.ConnStatus, statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilter, connectionTypeFilter) {
|
|
continue
|
|
}
|
|
if isPeerConnected {
|
|
peersConnected++
|
|
|
|
localICE = pbPeerState.GetLocalIceCandidateType()
|
|
remoteICE = pbPeerState.GetRemoteIceCandidateType()
|
|
localICEEndpoint = pbPeerState.GetLocalIceCandidateEndpoint()
|
|
remoteICEEndpoint = pbPeerState.GetRemoteIceCandidateEndpoint()
|
|
connType = pbPeerState.GetConnectionType()
|
|
relayServerAddress = pbPeerState.GetRelayAddress()
|
|
lastHandshake = pbPeerState.GetLastWireguardHandshake().AsTime().Local()
|
|
transferReceived = pbPeerState.GetBytesRx()
|
|
transferSent = pbPeerState.GetBytesTx()
|
|
}
|
|
|
|
timeLocal := pbPeerState.GetConnStatusUpdate().AsTime().Local()
|
|
peerState := PeerStateDetailOutput{
|
|
IP: pbPeerState.GetIP(),
|
|
PubKey: pbPeerState.GetPubKey(),
|
|
Status: pbPeerState.GetConnStatus(),
|
|
LastStatusUpdate: timeLocal,
|
|
ConnType: connType,
|
|
IceCandidateType: IceCandidateType{
|
|
Local: localICE,
|
|
Remote: remoteICE,
|
|
},
|
|
IceCandidateEndpoint: IceCandidateType{
|
|
Local: localICEEndpoint,
|
|
Remote: remoteICEEndpoint,
|
|
},
|
|
RelayAddress: relayServerAddress,
|
|
FQDN: pbPeerState.GetFqdn(),
|
|
LastWireguardHandshake: lastHandshake,
|
|
TransferReceived: transferReceived,
|
|
TransferSent: transferSent,
|
|
Latency: pbPeerState.GetLatency().AsDuration(),
|
|
RosenpassEnabled: pbPeerState.GetRosenpassEnabled(),
|
|
Networks: pbPeerState.GetNetworks(),
|
|
}
|
|
|
|
peersStateDetail = append(peersStateDetail, peerState)
|
|
}
|
|
|
|
sortPeersByIP(peersStateDetail)
|
|
|
|
peersOverview := PeersStateOutput{
|
|
Total: len(peersStateDetail),
|
|
Connected: peersConnected,
|
|
Details: peersStateDetail,
|
|
}
|
|
return peersOverview
|
|
}
|
|
|
|
func sortPeersByIP(peersStateDetail []PeerStateDetailOutput) {
|
|
if len(peersStateDetail) > 0 {
|
|
sort.SliceStable(peersStateDetail, func(i, j int) bool {
|
|
iAddr, _ := netip.ParseAddr(peersStateDetail[i].IP)
|
|
jAddr, _ := netip.ParseAddr(peersStateDetail[j].IP)
|
|
return iAddr.Compare(jAddr) == -1
|
|
})
|
|
}
|
|
}
|
|
|
|
func ParseToJSON(overview OutputOverview) (string, error) {
|
|
jsonBytes, err := json.Marshal(overview)
|
|
if err != nil {
|
|
return "", fmt.Errorf("json marshal failed")
|
|
}
|
|
return string(jsonBytes), err
|
|
}
|
|
|
|
func ParseToYAML(overview OutputOverview) (string, error) {
|
|
yamlBytes, err := yaml.Marshal(overview)
|
|
if err != nil {
|
|
return "", fmt.Errorf("yaml marshal failed")
|
|
}
|
|
return string(yamlBytes), nil
|
|
}
|
|
|
|
func ParseGeneralSummary(overview OutputOverview, showURL bool, showRelays bool, showNameServers bool) string {
|
|
var managementConnString string
|
|
if overview.ManagementState.Connected {
|
|
managementConnString = "Connected"
|
|
if showURL {
|
|
managementConnString = fmt.Sprintf("%s to %s", managementConnString, overview.ManagementState.URL)
|
|
}
|
|
} else {
|
|
managementConnString = "Disconnected"
|
|
if overview.ManagementState.Error != "" {
|
|
managementConnString = fmt.Sprintf("%s, reason: %s", managementConnString, overview.ManagementState.Error)
|
|
}
|
|
}
|
|
|
|
var signalConnString string
|
|
if overview.SignalState.Connected {
|
|
signalConnString = "Connected"
|
|
if showURL {
|
|
signalConnString = fmt.Sprintf("%s to %s", signalConnString, overview.SignalState.URL)
|
|
}
|
|
} else {
|
|
signalConnString = "Disconnected"
|
|
if overview.SignalState.Error != "" {
|
|
signalConnString = fmt.Sprintf("%s, reason: %s", signalConnString, overview.SignalState.Error)
|
|
}
|
|
}
|
|
|
|
interfaceTypeString := "Userspace"
|
|
interfaceIP := overview.IP
|
|
if overview.KernelInterface {
|
|
interfaceTypeString = "Kernel"
|
|
} else if overview.IP == "" {
|
|
interfaceTypeString = "N/A"
|
|
interfaceIP = "N/A"
|
|
}
|
|
|
|
var relaysString string
|
|
if showRelays {
|
|
for _, relay := range overview.Relays.Details {
|
|
available := "Available"
|
|
reason := ""
|
|
if !relay.Available {
|
|
available = "Unavailable"
|
|
reason = fmt.Sprintf(", reason: %s", relay.Error)
|
|
}
|
|
relaysString += fmt.Sprintf("\n [%s] is %s%s", relay.URI, available, reason)
|
|
}
|
|
} else {
|
|
relaysString = fmt.Sprintf("%d/%d Available", overview.Relays.Available, overview.Relays.Total)
|
|
}
|
|
|
|
networks := "-"
|
|
if len(overview.Networks) > 0 {
|
|
sort.Strings(overview.Networks)
|
|
networks = strings.Join(overview.Networks, ", ")
|
|
}
|
|
|
|
var dnsServersString string
|
|
if showNameServers {
|
|
for _, nsServerGroup := range overview.NSServerGroups {
|
|
enabled := "Available"
|
|
if !nsServerGroup.Enabled {
|
|
enabled = "Unavailable"
|
|
}
|
|
errorString := ""
|
|
if nsServerGroup.Error != "" {
|
|
errorString = fmt.Sprintf(", reason: %s", nsServerGroup.Error)
|
|
errorString = strings.TrimSpace(errorString)
|
|
}
|
|
|
|
domainsString := strings.Join(nsServerGroup.Domains, ", ")
|
|
if domainsString == "" {
|
|
domainsString = "." // Show "." for the default zone
|
|
}
|
|
dnsServersString += fmt.Sprintf(
|
|
"\n [%s] for [%s] is %s%s",
|
|
strings.Join(nsServerGroup.Servers, ", "),
|
|
domainsString,
|
|
enabled,
|
|
errorString,
|
|
)
|
|
}
|
|
} else {
|
|
dnsServersString = fmt.Sprintf("%d/%d Available", countEnabled(overview.NSServerGroups), len(overview.NSServerGroups))
|
|
}
|
|
|
|
rosenpassEnabledStatus := "false"
|
|
if overview.RosenpassEnabled {
|
|
rosenpassEnabledStatus = "true"
|
|
if overview.RosenpassPermissive {
|
|
rosenpassEnabledStatus = "true (permissive)" //nolint:gosec
|
|
}
|
|
}
|
|
|
|
lazyConnectionEnabledStatus := "false"
|
|
if overview.LazyConnectionEnabled {
|
|
lazyConnectionEnabledStatus = "true"
|
|
}
|
|
|
|
peersCountString := fmt.Sprintf("%d/%d Connected", overview.Peers.Connected, overview.Peers.Total)
|
|
|
|
goos := runtime.GOOS
|
|
goarch := runtime.GOARCH
|
|
goarm := ""
|
|
if goarch == "arm" {
|
|
goarm = fmt.Sprintf(" (ARMv%s)", os.Getenv("GOARM"))
|
|
}
|
|
|
|
summary := fmt.Sprintf(
|
|
"OS: %s\n"+
|
|
"Daemon version: %s\n"+
|
|
"CLI version: %s\n"+
|
|
"Management: %s\n"+
|
|
"Signal: %s\n"+
|
|
"Relays: %s\n"+
|
|
"Nameservers: %s\n"+
|
|
"FQDN: %s\n"+
|
|
"NetBird IP: %s\n"+
|
|
"Interface type: %s\n"+
|
|
"Quantum resistance: %s\n"+
|
|
"Lazy connection: %s\n"+
|
|
"Networks: %s\n"+
|
|
"Forwarding rules: %d\n"+
|
|
"Peers count: %s\n",
|
|
fmt.Sprintf("%s/%s%s", goos, goarch, goarm),
|
|
overview.DaemonVersion,
|
|
version.NetbirdVersion(),
|
|
managementConnString,
|
|
signalConnString,
|
|
relaysString,
|
|
dnsServersString,
|
|
domain.Domain(overview.FQDN).SafeString(),
|
|
interfaceIP,
|
|
interfaceTypeString,
|
|
rosenpassEnabledStatus,
|
|
lazyConnectionEnabledStatus,
|
|
networks,
|
|
overview.NumberOfForwardingRules,
|
|
peersCountString,
|
|
)
|
|
return summary
|
|
}
|
|
|
|
func ParseToFullDetailSummary(overview OutputOverview) string {
|
|
parsedPeersString := parsePeers(overview.Peers, overview.RosenpassEnabled, overview.RosenpassPermissive)
|
|
parsedEventsString := parseEvents(overview.Events)
|
|
summary := ParseGeneralSummary(overview, true, true, true)
|
|
|
|
return fmt.Sprintf(
|
|
"Peers detail:"+
|
|
"%s\n"+
|
|
"Events:"+
|
|
"%s\n"+
|
|
"%s",
|
|
parsedPeersString,
|
|
parsedEventsString,
|
|
summary,
|
|
)
|
|
}
|
|
|
|
func parsePeers(peers PeersStateOutput, rosenpassEnabled, rosenpassPermissive bool) string {
|
|
var (
|
|
peersString = ""
|
|
)
|
|
|
|
for _, peerState := range peers.Details {
|
|
|
|
localICE := "-"
|
|
if peerState.IceCandidateType.Local != "" {
|
|
localICE = peerState.IceCandidateType.Local
|
|
}
|
|
|
|
remoteICE := "-"
|
|
if peerState.IceCandidateType.Remote != "" {
|
|
remoteICE = peerState.IceCandidateType.Remote
|
|
}
|
|
|
|
localICEEndpoint := "-"
|
|
if peerState.IceCandidateEndpoint.Local != "" {
|
|
localICEEndpoint = peerState.IceCandidateEndpoint.Local
|
|
}
|
|
|
|
remoteICEEndpoint := "-"
|
|
if peerState.IceCandidateEndpoint.Remote != "" {
|
|
remoteICEEndpoint = peerState.IceCandidateEndpoint.Remote
|
|
}
|
|
|
|
rosenpassEnabledStatus := "false"
|
|
if rosenpassEnabled {
|
|
if peerState.RosenpassEnabled {
|
|
rosenpassEnabledStatus = "true"
|
|
} else {
|
|
if rosenpassPermissive {
|
|
rosenpassEnabledStatus = "false (remote didn't enable quantum resistance)"
|
|
} else {
|
|
rosenpassEnabledStatus = "false (connection won't work without a permissive mode)"
|
|
}
|
|
}
|
|
} else {
|
|
if peerState.RosenpassEnabled {
|
|
rosenpassEnabledStatus = "false (connection might not work without a remote permissive mode)"
|
|
}
|
|
}
|
|
|
|
networks := "-"
|
|
if len(peerState.Networks) > 0 {
|
|
sort.Strings(peerState.Networks)
|
|
networks = strings.Join(peerState.Networks, ", ")
|
|
}
|
|
|
|
peerString := fmt.Sprintf(
|
|
"\n %s:\n"+
|
|
" NetBird IP: %s\n"+
|
|
" Public key: %s\n"+
|
|
" Status: %s\n"+
|
|
" -- detail --\n"+
|
|
" Connection type: %s\n"+
|
|
" ICE candidate (Local/Remote): %s/%s\n"+
|
|
" ICE candidate endpoints (Local/Remote): %s/%s\n"+
|
|
" Relay server address: %s\n"+
|
|
" Last connection update: %s\n"+
|
|
" Last WireGuard handshake: %s\n"+
|
|
" Transfer status (received/sent) %s/%s\n"+
|
|
" Quantum resistance: %s\n"+
|
|
" Networks: %s\n"+
|
|
" Latency: %s\n",
|
|
domain.Domain(peerState.FQDN).SafeString(),
|
|
peerState.IP,
|
|
peerState.PubKey,
|
|
peerState.Status,
|
|
peerState.ConnType,
|
|
localICE,
|
|
remoteICE,
|
|
localICEEndpoint,
|
|
remoteICEEndpoint,
|
|
peerState.RelayAddress,
|
|
timeAgo(peerState.LastStatusUpdate),
|
|
timeAgo(peerState.LastWireguardHandshake),
|
|
toIEC(peerState.TransferReceived),
|
|
toIEC(peerState.TransferSent),
|
|
rosenpassEnabledStatus,
|
|
networks,
|
|
peerState.Latency.String(),
|
|
)
|
|
|
|
peersString += peerString
|
|
}
|
|
return peersString
|
|
}
|
|
|
|
func skipDetailByFilters(peerState *proto.PeerState, peerStatus string, statusFilter string, prefixNamesFilter []string, prefixNamesFilterMap map[string]struct{}, ipsFilter map[string]struct{}, connectionTypeFilter string) bool {
|
|
statusEval := false
|
|
ipEval := false
|
|
nameEval := true
|
|
connectionTypeEval := false
|
|
|
|
if statusFilter != "" {
|
|
if !strings.EqualFold(peerStatus, statusFilter) {
|
|
statusEval = true
|
|
}
|
|
}
|
|
|
|
if len(ipsFilter) > 0 {
|
|
_, ok := ipsFilter[peerState.IP]
|
|
if !ok {
|
|
ipEval = true
|
|
}
|
|
}
|
|
|
|
if len(prefixNamesFilter) > 0 {
|
|
for prefixNameFilter := range prefixNamesFilterMap {
|
|
if strings.HasPrefix(peerState.Fqdn, prefixNameFilter) {
|
|
nameEval = false
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
nameEval = false
|
|
}
|
|
if connectionTypeFilter != "" && !strings.EqualFold(peerState.GetConnectionType(), connectionTypeFilter) {
|
|
connectionTypeEval = true
|
|
}
|
|
|
|
return statusEval || ipEval || nameEval || connectionTypeEval
|
|
}
|
|
|
|
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])
|
|
}
|
|
|
|
func countEnabled(dnsServers []NsServerGroupStateOutput) int {
|
|
count := 0
|
|
for _, server := range dnsServers {
|
|
if server.Enabled {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
// timeAgo returns a string representing the duration since the provided time in a human-readable format.
|
|
func timeAgo(t time.Time) string {
|
|
if t.IsZero() || t.Equal(time.Unix(0, 0)) {
|
|
return "-"
|
|
}
|
|
duration := time.Since(t)
|
|
switch {
|
|
case duration < time.Second:
|
|
return "Now"
|
|
case duration < time.Minute:
|
|
seconds := int(duration.Seconds())
|
|
if seconds == 1 {
|
|
return "1 second ago"
|
|
}
|
|
return fmt.Sprintf("%d seconds ago", seconds)
|
|
case duration < time.Hour:
|
|
minutes := int(duration.Minutes())
|
|
seconds := int(duration.Seconds()) % 60
|
|
if minutes == 1 {
|
|
if seconds == 1 {
|
|
return "1 minute, 1 second ago"
|
|
} else if seconds > 0 {
|
|
return fmt.Sprintf("1 minute, %d seconds ago", seconds)
|
|
}
|
|
return "1 minute ago"
|
|
}
|
|
if seconds > 0 {
|
|
return fmt.Sprintf("%d minutes, %d seconds ago", minutes, seconds)
|
|
}
|
|
return fmt.Sprintf("%d minutes ago", minutes)
|
|
case duration < 24*time.Hour:
|
|
hours := int(duration.Hours())
|
|
minutes := int(duration.Minutes()) % 60
|
|
if hours == 1 {
|
|
if minutes == 1 {
|
|
return "1 hour, 1 minute ago"
|
|
} else if minutes > 0 {
|
|
return fmt.Sprintf("1 hour, %d minutes ago", minutes)
|
|
}
|
|
return "1 hour ago"
|
|
}
|
|
if minutes > 0 {
|
|
return fmt.Sprintf("%d hours, %d minutes ago", hours, minutes)
|
|
}
|
|
return fmt.Sprintf("%d hours ago", hours)
|
|
}
|
|
|
|
days := int(duration.Hours()) / 24
|
|
hours := int(duration.Hours()) % 24
|
|
if days == 1 {
|
|
if hours == 1 {
|
|
return "1 day, 1 hour ago"
|
|
} else if hours > 0 {
|
|
return fmt.Sprintf("1 day, %d hours ago", hours)
|
|
}
|
|
return "1 day ago"
|
|
}
|
|
if hours > 0 {
|
|
return fmt.Sprintf("%d days, %d hours ago", days, hours)
|
|
}
|
|
return fmt.Sprintf("%d days ago", days)
|
|
}
|
|
|
|
func anonymizePeerDetail(a *anonymize.Anonymizer, peer *PeerStateDetailOutput) {
|
|
peer.FQDN = a.AnonymizeDomain(peer.FQDN)
|
|
if localIP, port, err := net.SplitHostPort(peer.IceCandidateEndpoint.Local); err == nil {
|
|
peer.IceCandidateEndpoint.Local = fmt.Sprintf("%s:%s", a.AnonymizeIPString(localIP), port)
|
|
}
|
|
if remoteIP, port, err := net.SplitHostPort(peer.IceCandidateEndpoint.Remote); err == nil {
|
|
peer.IceCandidateEndpoint.Remote = fmt.Sprintf("%s:%s", a.AnonymizeIPString(remoteIP), port)
|
|
}
|
|
|
|
peer.RelayAddress = a.AnonymizeURI(peer.RelayAddress)
|
|
|
|
for i, route := range peer.Networks {
|
|
peer.Networks[i] = a.AnonymizeIPString(route)
|
|
}
|
|
|
|
for i, route := range peer.Networks {
|
|
peer.Networks[i] = a.AnonymizeRoute(route)
|
|
}
|
|
}
|
|
|
|
func anonymizeOverview(a *anonymize.Anonymizer, overview *OutputOverview) {
|
|
for i, peer := range overview.Peers.Details {
|
|
peer := peer
|
|
anonymizePeerDetail(a, &peer)
|
|
overview.Peers.Details[i] = peer
|
|
}
|
|
|
|
overview.ManagementState.URL = a.AnonymizeURI(overview.ManagementState.URL)
|
|
overview.ManagementState.Error = a.AnonymizeString(overview.ManagementState.Error)
|
|
overview.SignalState.URL = a.AnonymizeURI(overview.SignalState.URL)
|
|
overview.SignalState.Error = a.AnonymizeString(overview.SignalState.Error)
|
|
|
|
overview.IP = a.AnonymizeIPString(overview.IP)
|
|
for i, detail := range overview.Relays.Details {
|
|
detail.URI = a.AnonymizeURI(detail.URI)
|
|
detail.Error = a.AnonymizeString(detail.Error)
|
|
overview.Relays.Details[i] = detail
|
|
}
|
|
|
|
for i, nsGroup := range overview.NSServerGroups {
|
|
for j, domain := range nsGroup.Domains {
|
|
overview.NSServerGroups[i].Domains[j] = a.AnonymizeDomain(domain)
|
|
}
|
|
for j, ns := range nsGroup.Servers {
|
|
host, port, err := net.SplitHostPort(ns)
|
|
if err == nil {
|
|
overview.NSServerGroups[i].Servers[j] = fmt.Sprintf("%s:%s", a.AnonymizeIPString(host), port)
|
|
}
|
|
}
|
|
}
|
|
|
|
for i, route := range overview.Networks {
|
|
overview.Networks[i] = a.AnonymizeRoute(route)
|
|
}
|
|
|
|
overview.FQDN = a.AnonymizeDomain(overview.FQDN)
|
|
|
|
for i, event := range overview.Events {
|
|
overview.Events[i].Message = a.AnonymizeString(event.Message)
|
|
overview.Events[i].UserMessage = a.AnonymizeString(event.UserMessage)
|
|
|
|
for k, v := range event.Metadata {
|
|
event.Metadata[k] = a.AnonymizeString(v)
|
|
}
|
|
}
|
|
}
|