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