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