|
@@ -0,0 +1,220 @@
|
|
|
+package analytic
|
|
|
+
|
|
|
+import (
|
|
|
+ stdnet "net"
|
|
|
+
|
|
|
+ "github.com/shirou/gopsutil/v4/net"
|
|
|
+ "github.com/uozi-tech/cosy/logger"
|
|
|
+)
|
|
|
+
|
|
|
+func GetNetworkStat() (data *net.IOCountersStat, err error) {
|
|
|
+ networkStats, err := net.IOCounters(true)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if len(networkStats) == 0 {
|
|
|
+ return &net.IOCountersStat{}, nil
|
|
|
+ }
|
|
|
+ // Get all network interfaces
|
|
|
+ interfaces, err := stdnet.Interfaces()
|
|
|
+ if err != nil {
|
|
|
+ logger.Error(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var (
|
|
|
+ totalBytesRecv uint64
|
|
|
+ totalBytesSent uint64
|
|
|
+ totalPacketsRecv uint64
|
|
|
+ totalPacketsSent uint64
|
|
|
+ totalErrIn uint64
|
|
|
+ totalErrOut uint64
|
|
|
+ totalDropIn uint64
|
|
|
+ totalDropOut uint64
|
|
|
+ totalFifoIn uint64
|
|
|
+ totalFifoOut uint64
|
|
|
+ )
|
|
|
+
|
|
|
+ // Create a map of external interface names
|
|
|
+ externalInterfaces := make(map[string]bool)
|
|
|
+
|
|
|
+ // Identify external interfaces
|
|
|
+ for _, iface := range interfaces {
|
|
|
+ // Skip down or loopback interfaces
|
|
|
+ if iface.Flags&stdnet.FlagUp == 0 ||
|
|
|
+ iface.Flags&stdnet.FlagLoopback != 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get addresses for this interface
|
|
|
+ addrs, err := iface.Addrs()
|
|
|
+ if err != nil {
|
|
|
+ logger.Error(err)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip interfaces without addresses
|
|
|
+ if len(addrs) == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check for non-private IP addresses
|
|
|
+ for _, addr := range addrs {
|
|
|
+ ip, ipNet, err := stdnet.ParseCIDR(addr.String())
|
|
|
+ if err != nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip virtual, local, multicast, and special purpose IPs
|
|
|
+ if !isRealExternalIP(ip, ipNet) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ externalInterfaces[iface.Name] = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Accumulate stats only from external interfaces
|
|
|
+ for _, stat := range networkStats {
|
|
|
+ if externalInterfaces[stat.Name] {
|
|
|
+ totalBytesRecv += stat.BytesRecv
|
|
|
+ totalBytesSent += stat.BytesSent
|
|
|
+ totalPacketsRecv += stat.PacketsRecv
|
|
|
+ totalPacketsSent += stat.PacketsSent
|
|
|
+ totalErrIn += stat.Errin
|
|
|
+ totalErrOut += stat.Errout
|
|
|
+ totalDropIn += stat.Dropin
|
|
|
+ totalDropOut += stat.Dropout
|
|
|
+ totalFifoIn += stat.Fifoin
|
|
|
+ totalFifoOut += stat.Fifoout
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return &net.IOCountersStat{
|
|
|
+ Name: "analytic.network",
|
|
|
+ BytesRecv: totalBytesRecv,
|
|
|
+ BytesSent: totalBytesSent,
|
|
|
+ PacketsRecv: totalPacketsRecv,
|
|
|
+ PacketsSent: totalPacketsSent,
|
|
|
+ Errin: totalErrIn,
|
|
|
+ Errout: totalErrOut,
|
|
|
+ Dropin: totalDropIn,
|
|
|
+ Dropout: totalDropOut,
|
|
|
+ Fifoin: totalFifoIn,
|
|
|
+ Fifoout: totalFifoOut,
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+// isRealExternalIP checks if an IP is a genuine external (public) IP
|
|
|
+func isRealExternalIP(ip stdnet.IP, ipNet *stdnet.IPNet) bool {
|
|
|
+ // Skip if it's not a global unicast address
|
|
|
+ if !ip.IsGlobalUnicast() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip private IPs
|
|
|
+ if ip.IsPrivate() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip link-local addresses
|
|
|
+ if ip.IsLinkLocalUnicast() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip loopback
|
|
|
+ if ip.IsLoopback() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip multicast
|
|
|
+ if ip.IsMulticast() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check for special reserved ranges
|
|
|
+ if isReservedIP(ip) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+// isReservedIP checks if an IP belongs to special reserved ranges
|
|
|
+func isReservedIP(ip stdnet.IP) bool {
|
|
|
+ // Handle IPv4
|
|
|
+ if ip4 := ip.To4(); ip4 != nil {
|
|
|
+ // TEST-NET-1: 192.0.2.0/24 (RFC 5737)
|
|
|
+ if ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // TEST-NET-2: 198.51.100.0/24 (RFC 5737)
|
|
|
+ if ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // TEST-NET-3: 203.0.113.0/24 (RFC 5737)
|
|
|
+ if ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Benchmark tests: 198.18.0.0/15 (includes 198.19.0.0/16) (RFC 2544)
|
|
|
+ if ip4[0] == 198 && (ip4[1] == 18 || ip4[1] == 19) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Documentation: 240.0.0.0/4 (RFC 1112)
|
|
|
+ if ip4[0] >= 240 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // CGNAT: 100.64.0.0/10 (RFC 6598)
|
|
|
+ if ip4[0] == 100 && (ip4[1]&0xC0) == 64 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ } else if ip.To16() != nil {
|
|
|
+ // Documentation prefix (2001:db8::/32) - RFC 3849
|
|
|
+ if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x0d && ip[3] == 0xb8 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Unique Local Addresses (fc00::/7) - RFC 4193
|
|
|
+ if (ip[0] & 0xfe) == 0xfc {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6to4 relay (2002::/16) - RFC 3056
|
|
|
+ if ip[0] == 0x20 && ip[1] == 0x02 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Teredo tunneling (2001:0::/32) - RFC 4380
|
|
|
+ if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && ip[3] == 0x00 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Deprecated site-local addresses (fec0::/10) - RFC 3879
|
|
|
+ if (ip[0]&0xff) == 0xfe && (ip[1]&0xc0) == 0xc0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Old 6bone addresses (3ffe::/16) - Deprecated
|
|
|
+ if ip[0] == 0x3f && ip[1] == 0xfe {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // ORCHID addresses (2001:10::/28) - RFC 4843
|
|
|
+ if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x10 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // ORCHID v2 addresses (2001:20::/28) - RFC 7343
|
|
|
+ if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x20 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false
|
|
|
+}
|