1
0

netstat.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package netstat
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "net/http"
  6. "time"
  7. "github.com/shirou/gopsutil/v4/net"
  8. "imuslab.com/zoraxy/mod/info/logger"
  9. "imuslab.com/zoraxy/mod/utils"
  10. )
  11. // Float stat store the change of RX and TX
  12. type FlowStat struct {
  13. RX int64
  14. TX int64
  15. }
  16. // A new type of FloatStat that save the raw value from rx tx
  17. type RawFlowStat struct {
  18. RX int64
  19. TX int64
  20. }
  21. type NetStatBuffers struct {
  22. StatRecordCount int //No. of record number to keep
  23. PreviousStat *RawFlowStat //The value of the last instance of netstats
  24. Stats []*FlowStat //Statistic of the flow
  25. StopChan chan bool //Channel to stop the ticker
  26. EventTicker *time.Ticker //Ticker for event logging
  27. logger *logger.Logger
  28. }
  29. // Get a new network statistic buffers
  30. func NewNetStatBuffer(recordCount int, systemWideLogger *logger.Logger) (*NetStatBuffers, error) {
  31. //Flood fill the stats with 0
  32. initialStats := []*FlowStat{}
  33. for i := 0; i < recordCount; i++ {
  34. initialStats = append(initialStats, &FlowStat{
  35. RX: 0,
  36. TX: 0,
  37. })
  38. }
  39. //Setup a timer to get the value from NIC accumulation stats
  40. ticker := time.NewTicker(time.Second)
  41. //Setup a stop channel
  42. stopCh := make(chan bool)
  43. currnetNetSpec := RawFlowStat{
  44. RX: 0,
  45. TX: 0,
  46. }
  47. thisNetBuffer := NetStatBuffers{
  48. StatRecordCount: recordCount,
  49. PreviousStat: &currnetNetSpec,
  50. Stats: initialStats,
  51. StopChan: stopCh,
  52. EventTicker: ticker,
  53. logger: systemWideLogger,
  54. }
  55. //Get the initial measurements of netstats
  56. rx, tx, err := thisNetBuffer.GetNetworkInterfaceStats()
  57. if err != nil {
  58. systemWideLogger.PrintAndLog("netstat", "Unable to get NIC stats: ", err)
  59. }
  60. retryCount := 0
  61. for rx == 0 && tx == 0 && retryCount < 10 {
  62. //Strange. Retry
  63. systemWideLogger.PrintAndLog("netstat", "NIC stats return all 0. Retrying...", nil)
  64. rx, tx, err = thisNetBuffer.GetNetworkInterfaceStats()
  65. if err != nil {
  66. systemWideLogger.PrintAndLog("netstat", "Unable to get NIC stats: ", err)
  67. }
  68. retryCount++
  69. }
  70. thisNetBuffer.PreviousStat = &RawFlowStat{
  71. RX: rx,
  72. TX: tx,
  73. }
  74. // Update the buffer every second
  75. go func(n *NetStatBuffers) {
  76. for {
  77. select {
  78. case <-n.StopChan:
  79. systemWideLogger.PrintAndLog("netstat", "Netstats listener stopped", nil)
  80. return
  81. case <-ticker.C:
  82. if n.PreviousStat.RX == 0 && n.PreviousStat.TX == 0 {
  83. //Initiation state is still not done. Ignore request
  84. systemWideLogger.PrintAndLog("netstat", "No initial states. Waiting", nil)
  85. return
  86. }
  87. // Get the latest network interface stats
  88. rx, tx, err := thisNetBuffer.GetNetworkInterfaceStats()
  89. if err != nil {
  90. // Log the error, but don't stop the buffer
  91. systemWideLogger.PrintAndLog("netstat", "Failed to get network interface stats", err)
  92. continue
  93. }
  94. //Calculate the difference between this and last values
  95. drx := rx - n.PreviousStat.RX
  96. dtx := tx - n.PreviousStat.TX
  97. // Push the new stats to the buffer
  98. newStat := &FlowStat{
  99. RX: drx,
  100. TX: dtx,
  101. }
  102. //Set current rx tx as the previous rxtx
  103. n.PreviousStat = &RawFlowStat{
  104. RX: rx,
  105. TX: tx,
  106. }
  107. newStats := n.Stats[1:]
  108. newStats = append(newStats, newStat)
  109. n.Stats = newStats
  110. }
  111. }
  112. }(&thisNetBuffer)
  113. return &thisNetBuffer, nil
  114. }
  115. func (n *NetStatBuffers) HandleGetBufferedNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
  116. arr, _ := utils.GetPara(r, "array")
  117. if arr == "true" {
  118. //Restructure it into array
  119. rx := []int{}
  120. tx := []int{}
  121. for _, state := range n.Stats {
  122. rx = append(rx, int(state.RX))
  123. tx = append(tx, int(state.TX))
  124. }
  125. type info struct {
  126. Rx []int
  127. Tx []int
  128. }
  129. js, _ := json.Marshal(info{
  130. Rx: rx,
  131. Tx: tx,
  132. })
  133. utils.SendJSONResponse(w, string(js))
  134. } else {
  135. js, _ := json.Marshal(n.Stats)
  136. utils.SendJSONResponse(w, string(js))
  137. }
  138. }
  139. func (n *NetStatBuffers) Close() {
  140. //Fixed issue #394 for stopping netstat listener on platforms not supported platforms
  141. if n.StopChan != nil {
  142. n.StopChan <- true
  143. time.Sleep(300 * time.Millisecond)
  144. }
  145. if n.EventTicker != nil {
  146. n.EventTicker.Stop()
  147. }
  148. }
  149. func (n *NetStatBuffers) HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
  150. rx, tx, err := n.GetNetworkInterfaceStats()
  151. if err != nil {
  152. utils.SendErrorResponse(w, err.Error())
  153. return
  154. }
  155. currnetNetSpec := struct {
  156. RX int64
  157. TX int64
  158. }{
  159. rx,
  160. tx,
  161. }
  162. js, _ := json.Marshal(currnetNetSpec)
  163. utils.SendJSONResponse(w, string(js))
  164. }
  165. // Get network interface stats, return accumulated rx bits, tx bits and error if any
  166. func (n *NetStatBuffers) GetNetworkInterfaceStats() (int64, int64, error) {
  167. // Get aggregated network I/O stats for all interfaces
  168. counters, err := net.IOCounters(false)
  169. if err != nil {
  170. return 0, 0, err
  171. }
  172. if len(counters) == 0 {
  173. return 0, 0, errors.New("no network interfaces found")
  174. }
  175. var totalRx, totalTx uint64
  176. for _, counter := range counters {
  177. totalRx += counter.BytesRecv
  178. totalTx += counter.BytesSent
  179. }
  180. // Convert bytes to bits with overflow check
  181. const maxInt64 = int64(^uint64(0) >> 1)
  182. if totalRx*8 > uint64(maxInt64) || totalTx*8 > uint64(maxInt64) {
  183. return 0, 0, errors.New("overflow detected when converting uint64 to int64")
  184. }
  185. return int64(totalRx * 8), int64(totalTx * 8), nil
  186. }