netstat.go 5.0 KB

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