You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

242 lines
5.9 KiB

  1. // Copyright 2018 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package procfs
  14. import (
  15. "bufio"
  16. "bytes"
  17. "encoding/hex"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "net"
  23. "os"
  24. "strconv"
  25. "strings"
  26. "github.com/prometheus/procfs/internal/util"
  27. )
  28. // IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
  29. type IPVSStats struct {
  30. // Total count of connections.
  31. Connections uint64
  32. // Total incoming packages processed.
  33. IncomingPackets uint64
  34. // Total outgoing packages processed.
  35. OutgoingPackets uint64
  36. // Total incoming traffic.
  37. IncomingBytes uint64
  38. // Total outgoing traffic.
  39. OutgoingBytes uint64
  40. }
  41. // IPVSBackendStatus holds current metrics of one virtual / real address pair.
  42. type IPVSBackendStatus struct {
  43. // The local (virtual) IP address.
  44. LocalAddress net.IP
  45. // The remote (real) IP address.
  46. RemoteAddress net.IP
  47. // The local (virtual) port.
  48. LocalPort uint16
  49. // The remote (real) port.
  50. RemotePort uint16
  51. // The local firewall mark
  52. LocalMark string
  53. // The transport protocol (TCP, UDP).
  54. Proto string
  55. // The current number of active connections for this virtual/real address pair.
  56. ActiveConn uint64
  57. // The current number of inactive connections for this virtual/real address pair.
  58. InactConn uint64
  59. // The current weight of this virtual/real address pair.
  60. Weight uint64
  61. }
  62. // IPVSStats reads the IPVS statistics from the specified `proc` filesystem.
  63. func (fs FS) IPVSStats() (IPVSStats, error) {
  64. data, err := util.ReadFileNoStat(fs.proc.Path("net/ip_vs_stats"))
  65. if err != nil {
  66. return IPVSStats{}, err
  67. }
  68. return parseIPVSStats(bytes.NewReader(data))
  69. }
  70. // parseIPVSStats performs the actual parsing of `ip_vs_stats`.
  71. func parseIPVSStats(r io.Reader) (IPVSStats, error) {
  72. var (
  73. statContent []byte
  74. statLines []string
  75. statFields []string
  76. stats IPVSStats
  77. )
  78. statContent, err := ioutil.ReadAll(r)
  79. if err != nil {
  80. return IPVSStats{}, err
  81. }
  82. statLines = strings.SplitN(string(statContent), "\n", 4)
  83. if len(statLines) != 4 {
  84. return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
  85. }
  86. statFields = strings.Fields(statLines[2])
  87. if len(statFields) != 5 {
  88. return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
  89. }
  90. stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
  91. if err != nil {
  92. return IPVSStats{}, err
  93. }
  94. stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
  95. if err != nil {
  96. return IPVSStats{}, err
  97. }
  98. stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
  99. if err != nil {
  100. return IPVSStats{}, err
  101. }
  102. stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
  103. if err != nil {
  104. return IPVSStats{}, err
  105. }
  106. stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
  107. if err != nil {
  108. return IPVSStats{}, err
  109. }
  110. return stats, nil
  111. }
  112. // IPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
  113. func (fs FS) IPVSBackendStatus() ([]IPVSBackendStatus, error) {
  114. file, err := os.Open(fs.proc.Path("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. localMark string
  127. localAddress net.IP
  128. localPort uint16
  129. err error
  130. )
  131. for scanner.Scan() {
  132. fields := strings.Fields(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. localMark = ""
  145. localAddress, localPort, err = parseIPPort(fields[1])
  146. if err != nil {
  147. return nil, err
  148. }
  149. case fields[0] == "FWM":
  150. if len(fields) < 2 {
  151. continue
  152. }
  153. proto = fields[0]
  154. localMark = fields[1]
  155. localAddress = nil
  156. localPort = 0
  157. case fields[0] == "->":
  158. if len(fields) < 6 {
  159. continue
  160. }
  161. remoteAddress, remotePort, err := parseIPPort(fields[1])
  162. if err != nil {
  163. return nil, err
  164. }
  165. weight, err := strconv.ParseUint(fields[3], 10, 64)
  166. if err != nil {
  167. return nil, err
  168. }
  169. activeConn, err := strconv.ParseUint(fields[4], 10, 64)
  170. if err != nil {
  171. return nil, err
  172. }
  173. inactConn, err := strconv.ParseUint(fields[5], 10, 64)
  174. if err != nil {
  175. return nil, err
  176. }
  177. status = append(status, IPVSBackendStatus{
  178. LocalAddress: localAddress,
  179. LocalPort: localPort,
  180. LocalMark: localMark,
  181. RemoteAddress: remoteAddress,
  182. RemotePort: remotePort,
  183. Proto: proto,
  184. Weight: weight,
  185. ActiveConn: activeConn,
  186. InactConn: inactConn,
  187. })
  188. }
  189. }
  190. return status, nil
  191. }
  192. func parseIPPort(s string) (net.IP, uint16, error) {
  193. var (
  194. ip net.IP
  195. err error
  196. )
  197. switch len(s) {
  198. case 13:
  199. ip, err = hex.DecodeString(s[0:8])
  200. if err != nil {
  201. return nil, 0, err
  202. }
  203. case 46:
  204. ip = net.ParseIP(s[1:40])
  205. if ip == nil {
  206. return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40])
  207. }
  208. default:
  209. return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s)
  210. }
  211. portString := s[len(s)-4:]
  212. if len(portString) != 4 {
  213. return nil, 0, fmt.Errorf("unexpected port string format: %s", portString)
  214. }
  215. port, err := strconv.ParseUint(portString, 16, 16)
  216. if err != nil {
  217. return nil, 0, err
  218. }
  219. return ip, uint16(port), nil
  220. }