1
2
3
4
5
6
7
8
9
10
11
12
13
14 package procfs
15
16 import (
17 "bufio"
18 "bytes"
19 "encoding/hex"
20 "errors"
21 "fmt"
22 "io"
23 "net"
24 "os"
25 "strconv"
26 "strings"
27
28 "github.com/prometheus/procfs/internal/util"
29 )
30
31
32 type IPVSStats struct {
33
34 Connections uint64
35
36 IncomingPackets uint64
37
38 OutgoingPackets uint64
39
40 IncomingBytes uint64
41
42 OutgoingBytes uint64
43 }
44
45
46 type IPVSBackendStatus struct {
47
48 LocalAddress net.IP
49
50 RemoteAddress net.IP
51
52 LocalPort uint16
53
54 RemotePort uint16
55
56 LocalMark string
57
58 Proto string
59
60 ActiveConn uint64
61
62 InactConn uint64
63
64 Weight uint64
65 }
66
67
68 func (fs FS) IPVSStats() (IPVSStats, error) {
69 data, err := util.ReadFileNoStat(fs.proc.Path("net/ip_vs_stats"))
70 if err != nil {
71 return IPVSStats{}, err
72 }
73
74 return parseIPVSStats(bytes.NewReader(data))
75 }
76
77
78 func parseIPVSStats(r io.Reader) (IPVSStats, error) {
79 var (
80 statContent []byte
81 statLines []string
82 statFields []string
83 stats IPVSStats
84 )
85
86 statContent, err := io.ReadAll(r)
87 if err != nil {
88 return IPVSStats{}, err
89 }
90
91 statLines = strings.SplitN(string(statContent), "\n", 4)
92 if len(statLines) != 4 {
93 return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
94 }
95
96 statFields = strings.Fields(statLines[2])
97 if len(statFields) != 5 {
98 return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
99 }
100
101 stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
102 if err != nil {
103 return IPVSStats{}, err
104 }
105 stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
106 if err != nil {
107 return IPVSStats{}, err
108 }
109 stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
110 if err != nil {
111 return IPVSStats{}, err
112 }
113 stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
114 if err != nil {
115 return IPVSStats{}, err
116 }
117 stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
118 if err != nil {
119 return IPVSStats{}, err
120 }
121
122 return stats, nil
123 }
124
125
126 func (fs FS) IPVSBackendStatus() ([]IPVSBackendStatus, error) {
127 file, err := os.Open(fs.proc.Path("net/ip_vs"))
128 if err != nil {
129 return nil, err
130 }
131 defer file.Close()
132
133 return parseIPVSBackendStatus(file)
134 }
135
136 func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
137 var (
138 status []IPVSBackendStatus
139 scanner = bufio.NewScanner(file)
140 proto string
141 localMark string
142 localAddress net.IP
143 localPort uint16
144 err error
145 )
146
147 for scanner.Scan() {
148 fields := strings.Fields(scanner.Text())
149 if len(fields) == 0 {
150 continue
151 }
152 switch {
153 case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
154 continue
155 case fields[0] == "TCP" || fields[0] == "UDP":
156 if len(fields) < 2 {
157 continue
158 }
159 proto = fields[0]
160 localMark = ""
161 localAddress, localPort, err = parseIPPort(fields[1])
162 if err != nil {
163 return nil, err
164 }
165 case fields[0] == "FWM":
166 if len(fields) < 2 {
167 continue
168 }
169 proto = fields[0]
170 localMark = fields[1]
171 localAddress = nil
172 localPort = 0
173 case fields[0] == "->":
174 if len(fields) < 6 {
175 continue
176 }
177 remoteAddress, remotePort, err := parseIPPort(fields[1])
178 if err != nil {
179 return nil, err
180 }
181 weight, err := strconv.ParseUint(fields[3], 10, 64)
182 if err != nil {
183 return nil, err
184 }
185 activeConn, err := strconv.ParseUint(fields[4], 10, 64)
186 if err != nil {
187 return nil, err
188 }
189 inactConn, err := strconv.ParseUint(fields[5], 10, 64)
190 if err != nil {
191 return nil, err
192 }
193 status = append(status, IPVSBackendStatus{
194 LocalAddress: localAddress,
195 LocalPort: localPort,
196 LocalMark: localMark,
197 RemoteAddress: remoteAddress,
198 RemotePort: remotePort,
199 Proto: proto,
200 Weight: weight,
201 ActiveConn: activeConn,
202 InactConn: inactConn,
203 })
204 }
205 }
206 return status, nil
207 }
208
209 func parseIPPort(s string) (net.IP, uint16, error) {
210 var (
211 ip net.IP
212 err error
213 )
214
215 switch len(s) {
216 case 13:
217 ip, err = hex.DecodeString(s[0:8])
218 if err != nil {
219 return nil, 0, err
220 }
221 case 46:
222 ip = net.ParseIP(s[1:40])
223 if ip == nil {
224 return nil, 0, fmt.Errorf("%s: Invalid IPv6 addr %s: %w", ErrFileParse, s[1:40], err)
225 }
226 default:
227 return nil, 0, fmt.Errorf("%s: Unexpected IP:Port %s: %w", ErrFileParse, s, err)
228 }
229
230 portString := s[len(s)-4:]
231 if len(portString) != 4 {
232 return nil, 0,
233 fmt.Errorf("%s: Unexpected port string format %s: %w", ErrFileParse, portString, err)
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 }
242
View as plain text