123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- package analytic
- import (
- stdnet "net"
- "strings"
- "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
- }
- // Skip common virtual interfaces by name pattern
- if isVirtualInterface(iface.Name) {
- continue
- }
- // Check if this is a physical network interface
- if isPhysicalInterface(iface.Name) && len(iface.HardwareAddr) > 0 {
- externalInterfaces[iface.Name] = true
- 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
- }
- // isVirtualInterface checks if the interface is a virtual one based on name patterns
- func isVirtualInterface(name string) bool {
- // Common virtual interface name patterns
- virtualPatterns := []string{
- "veth", "virbr", "vnet", "vmnet", "vboxnet", "docker",
- "br-", "bridge", "tun", "tap", "bond", "dummy",
- "vpn", "ipsec", "gre", "sit", "vlan", "virt",
- "wg", "vmk", "ib", "vxlan", "geneve", "ovs",
- "hyperv", "hyper-v", "awdl", "llw", "utun",
- "vpn", "zt", "zerotier", "wireguard",
- }
- for _, pattern := range virtualPatterns {
- if strings.Contains(strings.ToLower(name), pattern) {
- return true
- }
- }
- return false
- }
- // isPhysicalInterface checks if the interface is a physical network interface
- // including server, cloud VM, and container physical interfaces
- func isPhysicalInterface(name string) bool {
- // Common prefixes for physical network interfaces across different platforms
- physicalPrefixes := []string{
- "eth", // Common Linux Ethernet interface
- "en", // macOS and some Linux
- "ens", // Predictable network interface names in systemd
- "enp", // Predictable network interface names in systemd (PCI)
- "eno", // Predictable network interface names in systemd (on-board)
- "wlan", // Wireless interfaces
- "wifi", // Some wireless interfaces
- "wl", // Shortened wireless interfaces
- "bond", // Bonded interfaces
- "em", // Some server network interfaces
- "p", // Some specialized network cards
- "lan", // Some network interfaces
- }
- // Check for exact matches for common primary interfaces
- if name == "eth0" || name == "en0" || name == "em0" {
- return true
- }
- // Check for common physical interface patterns
- for _, prefix := range physicalPrefixes {
- if strings.HasPrefix(strings.ToLower(name), prefix) {
- // Check if the remaining part is numeric or empty
- suffix := strings.TrimPrefix(strings.ToLower(name), prefix)
- if suffix == "" || isNumericSuffix(suffix) {
- return true
- }
- }
- }
- return false
- }
- // isNumericSuffix checks if a string is a numeric suffix or starts with a number
- func isNumericSuffix(s string) bool {
- if len(s) == 0 {
- return false
- }
- // Check if the first character is a digit
- if s[0] >= '0' && s[0] <= '9' {
- return true
- }
- return false
- }
- // 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
- }
|