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