ipvs.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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 local firewall mark
  34. LocalMark string
  35. // The transport protocol (TCP, UDP).
  36. Proto string
  37. // The remote (real) IP address.
  38. RemoteAddress net.IP
  39. // The remote (real) port.
  40. RemotePort uint16
  41. // The current number of active connections for this virtual/real address pair.
  42. ActiveConn uint64
  43. // The current number of inactive connections for this virtual/real address pair.
  44. InactConn uint64
  45. // The current weight of this virtual/real address pair.
  46. Weight uint64
  47. }
  48. // NewIPVSStats reads the IPVS statistics.
  49. func NewIPVSStats() (IPVSStats, error) {
  50. fs, err := NewFS(DefaultMountPoint)
  51. if err != nil {
  52. return IPVSStats{}, err
  53. }
  54. return fs.NewIPVSStats()
  55. }
  56. // NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
  57. func (fs FS) NewIPVSStats() (IPVSStats, error) {
  58. file, err := os.Open(fs.Path("net/ip_vs_stats"))
  59. if err != nil {
  60. return IPVSStats{}, err
  61. }
  62. defer file.Close()
  63. return parseIPVSStats(file)
  64. }
  65. // parseIPVSStats performs the actual parsing of `ip_vs_stats`.
  66. func parseIPVSStats(file io.Reader) (IPVSStats, error) {
  67. var (
  68. statContent []byte
  69. statLines []string
  70. statFields []string
  71. stats IPVSStats
  72. )
  73. statContent, err := ioutil.ReadAll(file)
  74. if err != nil {
  75. return IPVSStats{}, err
  76. }
  77. statLines = strings.SplitN(string(statContent), "\n", 4)
  78. if len(statLines) != 4 {
  79. return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
  80. }
  81. statFields = strings.Fields(statLines[2])
  82. if len(statFields) != 5 {
  83. return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
  84. }
  85. stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
  86. if err != nil {
  87. return IPVSStats{}, err
  88. }
  89. stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
  90. if err != nil {
  91. return IPVSStats{}, err
  92. }
  93. stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
  94. if err != nil {
  95. return IPVSStats{}, err
  96. }
  97. stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
  98. if err != nil {
  99. return IPVSStats{}, err
  100. }
  101. stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
  102. if err != nil {
  103. return IPVSStats{}, err
  104. }
  105. return stats, nil
  106. }
  107. // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
  108. func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
  109. fs, err := NewFS(DefaultMountPoint)
  110. if err != nil {
  111. return []IPVSBackendStatus{}, err
  112. }
  113. return fs.NewIPVSBackendStatus()
  114. }
  115. // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
  116. func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
  117. file, err := os.Open(fs.Path("net/ip_vs"))
  118. if err != nil {
  119. return nil, err
  120. }
  121. defer file.Close()
  122. return parseIPVSBackendStatus(file)
  123. }
  124. func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
  125. var (
  126. status []IPVSBackendStatus
  127. scanner = bufio.NewScanner(file)
  128. proto string
  129. localMark string
  130. localAddress net.IP
  131. localPort uint16
  132. err error
  133. )
  134. for scanner.Scan() {
  135. fields := strings.Fields(string(scanner.Text()))
  136. if len(fields) == 0 {
  137. continue
  138. }
  139. switch {
  140. case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
  141. continue
  142. case fields[0] == "TCP" || fields[0] == "UDP":
  143. if len(fields) < 2 {
  144. continue
  145. }
  146. proto = fields[0]
  147. localMark = ""
  148. localAddress, localPort, err = parseIPPort(fields[1])
  149. if err != nil {
  150. return nil, err
  151. }
  152. case fields[0] == "FWM":
  153. if len(fields) < 2 {
  154. continue
  155. }
  156. proto = fields[0]
  157. localMark = fields[1]
  158. localAddress = nil
  159. localPort = 0
  160. case fields[0] == "->":
  161. if len(fields) < 6 {
  162. continue
  163. }
  164. remoteAddress, remotePort, err := parseIPPort(fields[1])
  165. if err != nil {
  166. return nil, err
  167. }
  168. weight, err := strconv.ParseUint(fields[3], 10, 64)
  169. if err != nil {
  170. return nil, err
  171. }
  172. activeConn, err := strconv.ParseUint(fields[4], 10, 64)
  173. if err != nil {
  174. return nil, err
  175. }
  176. inactConn, err := strconv.ParseUint(fields[5], 10, 64)
  177. if err != nil {
  178. return nil, err
  179. }
  180. status = append(status, IPVSBackendStatus{
  181. LocalAddress: localAddress,
  182. LocalPort: localPort,
  183. LocalMark: localMark,
  184. RemoteAddress: remoteAddress,
  185. RemotePort: remotePort,
  186. Proto: proto,
  187. Weight: weight,
  188. ActiveConn: activeConn,
  189. InactConn: inactConn,
  190. })
  191. }
  192. }
  193. return status, nil
  194. }
  195. func parseIPPort(s string) (net.IP, uint16, error) {
  196. var (
  197. ip net.IP
  198. err error
  199. )
  200. switch len(s) {
  201. case 13:
  202. ip, err = hex.DecodeString(s[0:8])
  203. if err != nil {
  204. return nil, 0, err
  205. }
  206. case 46:
  207. ip = net.ParseIP(s[1:40])
  208. if ip == nil {
  209. return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40])
  210. }
  211. default:
  212. return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s)
  213. }
  214. portString := s[len(s)-4:]
  215. if len(portString) != 4 {
  216. return nil, 0, fmt.Errorf("unexpected port string format: %s", portString)
  217. }
  218. port, err := strconv.ParseUint(portString, 16, 16)
  219. if err != nil {
  220. return nil, 0, err
  221. }
  222. return ip, uint16(port), nil
  223. }