ipvs.go 5.3 KB

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