network.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. package analytic
  2. import (
  3. stdnet "net"
  4. "strings"
  5. "github.com/shirou/gopsutil/v4/net"
  6. "github.com/uozi-tech/cosy/logger"
  7. )
  8. func GetNetworkStat() (data *net.IOCountersStat, err error) {
  9. networkStats, err := net.IOCounters(true)
  10. if err != nil {
  11. return
  12. }
  13. if len(networkStats) == 0 {
  14. return &net.IOCountersStat{}, nil
  15. }
  16. // Get all network interfaces
  17. interfaces, err := stdnet.Interfaces()
  18. if err != nil {
  19. logger.Error(err)
  20. return
  21. }
  22. var (
  23. totalBytesRecv uint64
  24. totalBytesSent uint64
  25. totalPacketsRecv uint64
  26. totalPacketsSent uint64
  27. totalErrIn uint64
  28. totalErrOut uint64
  29. totalDropIn uint64
  30. totalDropOut uint64
  31. totalFifoIn uint64
  32. totalFifoOut uint64
  33. )
  34. // Create a map of external interface names
  35. externalInterfaces := make(map[string]bool)
  36. // Identify external interfaces
  37. for _, iface := range interfaces {
  38. // Skip down or loopback interfaces
  39. if iface.Flags&stdnet.FlagUp == 0 ||
  40. iface.Flags&stdnet.FlagLoopback != 0 {
  41. continue
  42. }
  43. // Skip common virtual interfaces by name pattern
  44. if isVirtualInterface(iface.Name) {
  45. continue
  46. }
  47. // Check if this is a physical network interface
  48. if isPhysicalInterface(iface.Name) && len(iface.HardwareAddr) > 0 {
  49. externalInterfaces[iface.Name] = true
  50. continue
  51. }
  52. // Get addresses for this interface
  53. addrs, err := iface.Addrs()
  54. if err != nil {
  55. logger.Error(err)
  56. continue
  57. }
  58. // Skip interfaces without addresses
  59. if len(addrs) == 0 {
  60. continue
  61. }
  62. // Check for non-private IP addresses
  63. for _, addr := range addrs {
  64. ip, ipNet, err := stdnet.ParseCIDR(addr.String())
  65. if err != nil {
  66. continue
  67. }
  68. // Skip virtual, local, multicast, and special purpose IPs
  69. if !isRealExternalIP(ip, ipNet) {
  70. continue
  71. }
  72. externalInterfaces[iface.Name] = true
  73. break
  74. }
  75. }
  76. // Accumulate stats only from external interfaces
  77. for _, stat := range networkStats {
  78. if externalInterfaces[stat.Name] {
  79. totalBytesRecv += stat.BytesRecv
  80. totalBytesSent += stat.BytesSent
  81. totalPacketsRecv += stat.PacketsRecv
  82. totalPacketsSent += stat.PacketsSent
  83. totalErrIn += stat.Errin
  84. totalErrOut += stat.Errout
  85. totalDropIn += stat.Dropin
  86. totalDropOut += stat.Dropout
  87. totalFifoIn += stat.Fifoin
  88. totalFifoOut += stat.Fifoout
  89. }
  90. }
  91. return &net.IOCountersStat{
  92. Name: "analytic.network",
  93. BytesRecv: totalBytesRecv,
  94. BytesSent: totalBytesSent,
  95. PacketsRecv: totalPacketsRecv,
  96. PacketsSent: totalPacketsSent,
  97. Errin: totalErrIn,
  98. Errout: totalErrOut,
  99. Dropin: totalDropIn,
  100. Dropout: totalDropOut,
  101. Fifoin: totalFifoIn,
  102. Fifoout: totalFifoOut,
  103. }, nil
  104. }
  105. // isVirtualInterface checks if the interface is a virtual one based on name patterns
  106. func isVirtualInterface(name string) bool {
  107. // Common virtual interface name patterns
  108. virtualPatterns := []string{
  109. "veth", "virbr", "vnet", "vmnet", "vboxnet", "docker",
  110. "br-", "bridge", "tun", "tap", "bond", "dummy",
  111. "vpn", "ipsec", "gre", "sit", "vlan", "virt",
  112. "wg", "vmk", "ib", "vxlan", "geneve", "ovs",
  113. "hyperv", "hyper-v", "awdl", "llw", "utun",
  114. "vpn", "zt", "zerotier", "wireguard",
  115. }
  116. for _, pattern := range virtualPatterns {
  117. if strings.Contains(strings.ToLower(name), pattern) {
  118. return true
  119. }
  120. }
  121. return false
  122. }
  123. // isPhysicalInterface checks if the interface is a physical network interface
  124. // including server, cloud VM, and container physical interfaces
  125. func isPhysicalInterface(name string) bool {
  126. // Common prefixes for physical network interfaces across different platforms
  127. physicalPrefixes := []string{
  128. "eth", // Common Linux Ethernet interface
  129. "en", // macOS and some Linux
  130. "ens", // Predictable network interface names in systemd
  131. "enp", // Predictable network interface names in systemd (PCI)
  132. "eno", // Predictable network interface names in systemd (on-board)
  133. "wlan", // Wireless interfaces
  134. "wifi", // Some wireless interfaces
  135. "wl", // Shortened wireless interfaces
  136. "bond", // Bonded interfaces
  137. "em", // Some server network interfaces
  138. "p", // Some specialized network cards
  139. "lan", // Some network interfaces
  140. }
  141. // Check for exact matches for common primary interfaces
  142. if name == "eth0" || name == "en0" || name == "em0" {
  143. return true
  144. }
  145. // Check for common physical interface patterns
  146. for _, prefix := range physicalPrefixes {
  147. if strings.HasPrefix(strings.ToLower(name), prefix) {
  148. // Check if the remaining part is numeric or empty
  149. suffix := strings.TrimPrefix(strings.ToLower(name), prefix)
  150. if suffix == "" || isNumericSuffix(suffix) {
  151. return true
  152. }
  153. }
  154. }
  155. return false
  156. }
  157. // isNumericSuffix checks if a string is a numeric suffix or starts with a number
  158. func isNumericSuffix(s string) bool {
  159. if len(s) == 0 {
  160. return false
  161. }
  162. // Check if the first character is a digit
  163. if s[0] >= '0' && s[0] <= '9' {
  164. return true
  165. }
  166. return false
  167. }
  168. // isRealExternalIP checks if an IP is a genuine external (public) IP
  169. func isRealExternalIP(ip stdnet.IP, ipNet *stdnet.IPNet) bool {
  170. // Skip if it's not a global unicast address
  171. if !ip.IsGlobalUnicast() {
  172. return false
  173. }
  174. // Skip private IPs
  175. if ip.IsPrivate() {
  176. return false
  177. }
  178. // Skip link-local addresses
  179. if ip.IsLinkLocalUnicast() {
  180. return false
  181. }
  182. // Skip loopback
  183. if ip.IsLoopback() {
  184. return false
  185. }
  186. // Skip multicast
  187. if ip.IsMulticast() {
  188. return false
  189. }
  190. // Check for special reserved ranges
  191. if isReservedIP(ip) {
  192. return false
  193. }
  194. return true
  195. }
  196. // isReservedIP checks if an IP belongs to special reserved ranges
  197. func isReservedIP(ip stdnet.IP) bool {
  198. // Handle IPv4
  199. if ip4 := ip.To4(); ip4 != nil {
  200. // TEST-NET-1: 192.0.2.0/24 (RFC 5737)
  201. if ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2 {
  202. return true
  203. }
  204. // TEST-NET-2: 198.51.100.0/24 (RFC 5737)
  205. if ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100 {
  206. return true
  207. }
  208. // TEST-NET-3: 203.0.113.0/24 (RFC 5737)
  209. if ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113 {
  210. return true
  211. }
  212. // Benchmark tests: 198.18.0.0/15 (includes 198.19.0.0/16) (RFC 2544)
  213. if ip4[0] == 198 && (ip4[1] == 18 || ip4[1] == 19) {
  214. return true
  215. }
  216. // Documentation: 240.0.0.0/4 (RFC 1112)
  217. if ip4[0] >= 240 {
  218. return true
  219. }
  220. // CGNAT: 100.64.0.0/10 (RFC 6598)
  221. if ip4[0] == 100 && (ip4[1]&0xC0) == 64 {
  222. return true
  223. }
  224. } else if ip.To16() != nil {
  225. // Documentation prefix (2001:db8::/32) - RFC 3849
  226. if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x0d && ip[3] == 0xb8 {
  227. return true
  228. }
  229. // Unique Local Addresses (fc00::/7) - RFC 4193
  230. if (ip[0] & 0xfe) == 0xfc {
  231. return true
  232. }
  233. // 6to4 relay (2002::/16) - RFC 3056
  234. if ip[0] == 0x20 && ip[1] == 0x02 {
  235. return true
  236. }
  237. // Teredo tunneling (2001:0::/32) - RFC 4380
  238. if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && ip[3] == 0x00 {
  239. return true
  240. }
  241. // Deprecated site-local addresses (fec0::/10) - RFC 3879
  242. if (ip[0]&0xff) == 0xfe && (ip[1]&0xc0) == 0xc0 {
  243. return true
  244. }
  245. // Old 6bone addresses (3ffe::/16) - Deprecated
  246. if ip[0] == 0x3f && ip[1] == 0xfe {
  247. return true
  248. }
  249. // ORCHID addresses (2001:10::/28) - RFC 4843
  250. if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x10 {
  251. return true
  252. }
  253. // ORCHID v2 addresses (2001:20::/28) - RFC 7343
  254. if ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x00 && (ip[3]&0xf0) == 0x20 {
  255. return true
  256. }
  257. }
  258. return false
  259. }