ipvs.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package procfs
  2. import (
  3. "bufio"
  4. "encoding/hex"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net"
  10. "strconv"
  11. "strings"
  12. )
  13. // IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
  14. type IPVSStats struct {
  15. // Total count of connections.
  16. Connections uint64
  17. // Total incoming packages processed.
  18. IncomingPackets uint64
  19. // Total outgoing packages processed.
  20. OutgoingPackets uint64
  21. // Total incoming traffic.
  22. IncomingBytes uint64
  23. // Total outgoing traffic.
  24. OutgoingBytes uint64
  25. }
  26. // IPVSBackendStatus holds current metrics of one virtual / real address pair.
  27. type IPVSBackendStatus struct {
  28. // The local (virtual) IP address.
  29. LocalAddress net.IP
  30. // The local (virtual) port.
  31. LocalPort uint16
  32. // The transport protocol (TCP, UDP).
  33. Proto string
  34. // The remote (real) IP address.
  35. RemoteAddress net.IP
  36. // The remote (real) port.
  37. RemotePort uint16
  38. // The current number of active connections for this virtual/real address pair.
  39. ActiveConn uint64
  40. // The current number of inactive connections for this virtual/real address pair.
  41. InactConn uint64
  42. // The current weight of this virtual/real address pair.
  43. Weight uint64
  44. }
  45. // NewIPVSStats reads the IPVS statistics.
  46. func NewIPVSStats() (IPVSStats, error) {
  47. fs, err := NewFS(DefaultMountPoint)
  48. if err != nil {
  49. return IPVSStats{}, err
  50. }
  51. return fs.NewIPVSStats()
  52. }
  53. // NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
  54. func (fs FS) NewIPVSStats() (IPVSStats, error) {
  55. file, err := fs.open("net/ip_vs_stats")
  56. if err != nil {
  57. return IPVSStats{}, err
  58. }
  59. defer file.Close()
  60. return parseIPVSStats(file)
  61. }
  62. // parseIPVSStats performs the actual parsing of `ip_vs_stats`.
  63. func parseIPVSStats(file io.Reader) (IPVSStats, error) {
  64. var (
  65. statContent []byte
  66. statLines []string
  67. statFields []string
  68. stats IPVSStats
  69. )
  70. statContent, err := ioutil.ReadAll(file)
  71. if err != nil {
  72. return IPVSStats{}, err
  73. }
  74. statLines = strings.SplitN(string(statContent), "\n", 4)
  75. if len(statLines) != 4 {
  76. return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
  77. }
  78. statFields = strings.Fields(statLines[2])
  79. if len(statFields) != 5 {
  80. return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
  81. }
  82. stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
  83. if err != nil {
  84. return IPVSStats{}, err
  85. }
  86. stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
  87. if err != nil {
  88. return IPVSStats{}, err
  89. }
  90. stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
  91. if err != nil {
  92. return IPVSStats{}, err
  93. }
  94. stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
  95. if err != nil {
  96. return IPVSStats{}, err
  97. }
  98. stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
  99. if err != nil {
  100. return IPVSStats{}, err
  101. }
  102. return stats, nil
  103. }
  104. // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
  105. func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
  106. fs, err := NewFS(DefaultMountPoint)
  107. if err != nil {
  108. return []IPVSBackendStatus{}, err
  109. }
  110. return fs.NewIPVSBackendStatus()
  111. }
  112. // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
  113. func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
  114. file, err := fs.open("net/ip_vs")
  115. if err != nil {
  116. return nil, err
  117. }
  118. defer file.Close()
  119. return parseIPVSBackendStatus(file)
  120. }
  121. func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
  122. var (
  123. status []IPVSBackendStatus
  124. scanner = bufio.NewScanner(file)
  125. proto string
  126. localAddress net.IP
  127. localPort uint16
  128. err error
  129. )
  130. for scanner.Scan() {
  131. fields := strings.Fields(string(scanner.Text()))
  132. if len(fields) == 0 {
  133. continue
  134. }
  135. switch {
  136. case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
  137. continue
  138. case fields[0] == "TCP" || fields[0] == "UDP":
  139. if len(fields) < 2 {
  140. continue
  141. }
  142. proto = fields[0]
  143. localAddress, localPort, err = parseIPPort(fields[1])
  144. if err != nil {
  145. return nil, err
  146. }
  147. case fields[0] == "->":
  148. if len(fields) < 6 {
  149. continue
  150. }
  151. remoteAddress, remotePort, err := parseIPPort(fields[1])
  152. if err != nil {
  153. return nil, err
  154. }
  155. weight, err := strconv.ParseUint(fields[3], 10, 64)
  156. if err != nil {
  157. return nil, err
  158. }
  159. activeConn, err := strconv.ParseUint(fields[4], 10, 64)
  160. if err != nil {
  161. return nil, err
  162. }
  163. inactConn, err := strconv.ParseUint(fields[5], 10, 64)
  164. if err != nil {
  165. return nil, err
  166. }
  167. status = append(status, IPVSBackendStatus{
  168. LocalAddress: localAddress,
  169. LocalPort: localPort,
  170. RemoteAddress: remoteAddress,
  171. RemotePort: remotePort,
  172. Proto: proto,
  173. Weight: weight,
  174. ActiveConn: activeConn,
  175. InactConn: inactConn,
  176. })
  177. }
  178. }
  179. return status, nil
  180. }
  181. func parseIPPort(s string) (net.IP, uint16, error) {
  182. tmp := strings.SplitN(s, ":", 2)
  183. if len(tmp) != 2 {
  184. return nil, 0, fmt.Errorf("invalid IP:Port: %s", s)
  185. }
  186. if len(tmp[0]) != 8 && len(tmp[0]) != 32 {
  187. return nil, 0, fmt.Errorf("invalid IP: %s", tmp[0])
  188. }
  189. ip, err := hex.DecodeString(tmp[0])
  190. if err != nil {
  191. return nil, 0, err
  192. }
  193. port, err := strconv.ParseUint(tmp[1], 16, 16)
  194. if err != nil {
  195. return nil, 0, err
  196. }
  197. return ip, uint16(port), nil
  198. }