Skip to content

Commit 789698a

Browse files
committed
refactor(analytic): improved network stat #913
1 parent ec29a77 commit 789698a

File tree

6 files changed

+247
-108
lines changed

6 files changed

+247
-108
lines changed

api/analytic/analytic.go

+4-12
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/shirou/gopsutil/v4/cpu"
1212
"github.com/shirou/gopsutil/v4/host"
1313
"github.com/shirou/gopsutil/v4/load"
14-
"github.com/shirou/gopsutil/v4/net"
1514
"github.com/spf13/cast"
1615
"github.com/uozi-tech/cosy/logger"
1716

@@ -75,15 +74,13 @@ func Analytic(c *gin.Context) {
7574
continue
7675
}
7776

78-
network, err := net.IOCounters(false)
77+
network, err := analytic.GetNetworkStat()
7978
if err != nil {
8079
logger.Error(err)
8180
continue
8281
}
8382

84-
if len(network) > 0 {
85-
stat.Network = network[0]
86-
}
83+
stat.Network = *network
8784

8885
// write
8986
err = ws.WriteJSON(stat)
@@ -104,7 +101,7 @@ func GetAnalyticInit(c *gin.Context) {
104101
logger.Error(err)
105102
}
106103

107-
network, err := net.IOCounters(false)
104+
network, err := analytic.GetNetworkStat()
108105
if err != nil {
109106
logger.Error(err)
110107
}
@@ -119,11 +116,6 @@ func GetAnalyticInit(c *gin.Context) {
119116
logger.Error(err)
120117
}
121118

122-
var _net net.IOCountersStat
123-
if len(network) > 0 {
124-
_net = network[0]
125-
}
126-
127119
hostInfo, err := host.Info()
128120
if err != nil {
129121
logger.Error(err)
@@ -151,7 +143,7 @@ func GetAnalyticInit(c *gin.Context) {
151143
Total: analytic.CpuTotalRecord,
152144
},
153145
Network: NetworkRecords{
154-
Init: _net,
146+
Init: *network,
155147
BytesRecv: analytic.NetRecvRecord,
156148
BytesSent: analytic.NetSentRecord,
157149
},

internal/analytic/analytic.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package analytic
22

33
import (
4-
"github.com/shirou/gopsutil/v4/net"
5-
"github.com/uozi-tech/cosy/logger"
64
"time"
5+
6+
"github.com/uozi-tech/cosy/logger"
77
)
88

99
type Usage[T uint64 | float64] struct {
@@ -25,14 +25,12 @@ var (
2525
)
2626

2727
func init() {
28-
network, err := net.IOCounters(false)
28+
network, err := GetNetworkStat()
2929
if err != nil {
3030
logger.Error(err)
3131
}
32-
if len(network) > 0 {
33-
LastNetRecv = network[0].BytesRecv
34-
LastNetSent = network[0].BytesSent
35-
}
32+
LastNetRecv = network.BytesRecv
33+
LastNetSent = network.BytesSent
3634

3735
LastDiskReads, LastDiskWrites = getTotalDiskIO()
3836

internal/analytic/network.go

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package analytic
2+
3+
import (
4+
stdnet "net"
5+
6+
"github.com/shirou/gopsutil/v4/net"
7+
"github.com/uozi-tech/cosy/logger"
8+
)
9+
10+
func GetNetworkStat() (data *net.IOCountersStat, err error) {
11+
networkStats, err := net.IOCounters(true)
12+
if err != nil {
13+
return
14+
}
15+
if len(networkStats) == 0 {
16+
return &net.IOCountersStat{}, nil
17+
}
18+
// Get all network interfaces
19+
interfaces, err := stdnet.Interfaces()
20+
if err != nil {
21+
logger.Error(err)
22+
return
23+
}
24+
25+
var (
26+
totalBytesRecv uint64
27+
totalBytesSent uint64
28+
totalPacketsRecv uint64
29+
totalPacketsSent uint64
30+
totalErrIn uint64
31+
totalErrOut uint64
32+
totalDropIn uint64
33+
totalDropOut uint64
34+
totalFifoIn uint64
35+
totalFifoOut uint64
36+
)
37+
38+
// Create a map of external interface names
39+
externalInterfaces := make(map[string]bool)
40+
41+
// Identify external interfaces
42+
for _, iface := range interfaces {
43+
// Skip down or loopback interfaces
44+
if iface.Flags&stdnet.FlagUp == 0 ||
45+
iface.Flags&stdnet.FlagLoopback != 0 {
46+
continue
47+
}
48+
49+
// Get addresses for this interface
50+
addrs, err := iface.Addrs()
51+
if err != nil {
52+
logger.Error(err)
53+
continue
54+
}
55+
56+
// Skip interfaces without addresses
57+
if len(addrs) == 0 {
58+
continue
59+
}
60+
61+
// Check for non-private IP addresses
62+
for _, addr := range addrs {
63+
ip, ipNet, err := stdnet.ParseCIDR(addr.String())
64+
if err != nil {
65+
continue
66+
}
67+
68+
// Skip virtual, local, multicast, and special purpose IPs
69+
if !isRealExternalIP(ip, ipNet) {
70+
continue
71+
}
72+
73+
externalInterfaces[iface.Name] = true
74+
break
75+
}
76+
}
77+
78+
// Accumulate stats only from external interfaces
79+
for _, stat := range networkStats {
80+
if externalInterfaces[stat.Name] {
81+
totalBytesRecv += stat.BytesRecv
82+
totalBytesSent += stat.BytesSent
83+
totalPacketsRecv += stat.PacketsRecv
84+
totalPacketsSent += stat.PacketsSent
85+
totalErrIn += stat.Errin
86+
totalErrOut += stat.Errout
87+
totalDropIn += stat.Dropin
88+
totalDropOut += stat.Dropout
89+
totalFifoIn += stat.Fifoin
90+
totalFifoOut += stat.Fifoout
91+
}
92+
}
93+
94+
return &net.IOCountersStat{
95+
Name: "analytic.network",
96+
BytesRecv: totalBytesRecv,
97+
BytesSent: totalBytesSent,
98+
PacketsRecv: totalPacketsRecv,
99+
PacketsSent: totalPacketsSent,
100+
Errin: totalErrIn,
101+
Errout: totalErrOut,
102+
Dropin: totalDropIn,
103+
Dropout: totalDropOut,
104+
Fifoin: totalFifoIn,
105+
Fifoout: totalFifoOut,
106+
}, nil
107+
}
108+
109+
// isRealExternalIP checks if an IP is a genuine external (public) IP
110+
func isRealExternalIP(ip stdnet.IP, ipNet *stdnet.IPNet) bool {
111+
// Skip if it's not a global unicast address
112+
if !ip.IsGlobalUnicast() {
113+
return false
114+
}
115+
116+
// Skip private IPs
117+
if ip.IsPrivate() {
118+
return false
119+
}
120+
121+
// Skip link-local addresses
122+
if ip.IsLinkLocalUnicast() {
123+
return false
124+
}
125+
126+
// Skip loopback
127+
if ip.IsLoopback() {
128+
return false
129+
}
130+
131+
// Skip multicast
132+
if ip.IsMulticast() {
133+
return false
134+
}
135+
136+
// Check for special reserved ranges
137+
if isReservedIP(ip) {
138+
return false
139+
}
140+
141+
return true
142+
}
143+
144+
// isReservedIP checks if an IP belongs to special reserved ranges
145+
func isReservedIP(ip stdnet.IP) bool {
146+
// Handle IPv4
147+
if ip4 := ip.To4(); ip4 != nil {
148+
// TEST-NET-1: 192.0.2.0/24 (RFC 5737)
149+
if ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2 {
150+
return true
151+
}
152+
153+
// TEST-NET-2: 198.51.100.0/24 (RFC 5737)
154+
if ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100 {
155+
return true
156+
}
157+
158+
// TEST-NET-3: 203.0.113.0/24 (RFC 5737)
159+
if ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113 {
160+
return true
161+
}
162+
163+
// Benchmark tests: 198.18.0.0/15 (includes 198.19.0.0/16) (RFC 2544)
164+
if ip4[0] == 198 && (ip4[1] == 18 || ip4[1] == 19) {
165+
return true
166+
}
167+
168+
// Documentation: 240.0.0.0/4 (RFC 1112)
169+
if ip4[0] >= 240 {
170+
return true
171+
}
172+
173+
// CGNAT: 100.64.0.0/10 (RFC 6598)
174+
if ip4[0] == 100 && (ip4[1]&0xC0) == 64 {
175+
return true
176+
}
177+
} else if ip.To16() != nil {
178+
// Documentation prefix (2001:db8::/32) - RFC 3849
179+
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x0d && ip[3] == 0xb8 {
180+
return true
181+
}
182+
183+
// Unique Local Addresses (fc00::/7) - RFC 4193
184+
if (ip[0] & 0xfe) == 0xfc {
185+
return true
186+
}
187+
188+
// 6to4 relay (2002::/16) - RFC 3056
189+
if ip[0] == 0x20 && ip[1] == 0x02 {
190+
return true
191+
}
192+
193+
// Teredo tunneling (2001:0::/32) - RFC 4380
194+
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && ip[3] == 0x00 {
195+
return true
196+
}
197+
198+
// Deprecated site-local addresses (fec0::/10) - RFC 3879
199+
if (ip[0]&0xff) == 0xfe && (ip[1]&0xc0) == 0xc0 {
200+
return true
201+
}
202+
203+
// Old 6bone addresses (3ffe::/16) - Deprecated
204+
if ip[0] == 0x3f && ip[1] == 0xfe {
205+
return true
206+
}
207+
208+
// ORCHID addresses (2001:10::/28) - RFC 4843
209+
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x10 {
210+
return true
211+
}
212+
213+
// ORCHID v2 addresses (2001:20::/28) - RFC 7343
214+
if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x20 {
215+
return true
216+
}
217+
}
218+
219+
return false
220+
}

internal/analytic/node_stat.go

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package analytic
22

33
import (
4-
"github.com/shirou/gopsutil/v4/cpu"
5-
"github.com/shirou/gopsutil/v4/load"
6-
"github.com/shirou/gopsutil/v4/net"
7-
"github.com/uozi-tech/cosy/logger"
84
"math"
95
"runtime"
106
"time"
7+
8+
"github.com/shirou/gopsutil/v4/cpu"
9+
"github.com/shirou/gopsutil/v4/load"
10+
"github.com/uozi-tech/cosy/logger"
1111
)
1212

1313
func GetNodeStat() (data NodeStat) {
@@ -37,22 +37,17 @@ func GetNodeStat() (data NodeStat) {
3737
return
3838
}
3939

40-
netIO, err := net.IOCounters(false)
40+
network, err := GetNetworkStat()
4141
if err != nil {
4242
logger.Error(err)
4343
return
4444
}
4545

46-
var network net.IOCountersStat
47-
if len(netIO) > 0 {
48-
network = netIO[0]
49-
}
50-
5146
return NodeStat{
5247
AvgLoad: loadAvg,
5348
CPUPercent: math.Min((cpuUserUsage+cpuSystemUsage)*100, 100),
5449
MemoryPercent: memory.Pressure,
5550
DiskPercent: diskStat.Percentage,
56-
Network: network,
51+
Network: *network,
5752
}
5853
}

0 commit comments

Comments
 (0)