1
2
3
4
5
6
7
8
9
10
11
12
13
14 package procfs
15
16 import (
17 "bufio"
18 "encoding/hex"
19 "fmt"
20 "io"
21 "net"
22 "os"
23 "strconv"
24 "strings"
25 )
26
27 const (
28
29
30
31
32
33
34 readLimit = 4294967296
35 )
36
37
38 type (
39
40 NetIPSocket []*netIPSocketLine
41
42
43
44
45 NetIPSocketSummary struct {
46
47 TxQueueLength uint64
48
49 RxQueueLength uint64
50
51
52 UsedSockets uint64
53
54 Drops *uint64
55 }
56
57
58
59
60
61 netIPSocketLine struct {
62 Sl uint64
63 LocalAddr net.IP
64 LocalPort uint64
65 RemAddr net.IP
66 RemPort uint64
67 St uint64
68 TxQueue uint64
69 RxQueue uint64
70 UID uint64
71 Inode uint64
72 Drops *uint64
73 }
74 )
75
76 func newNetIPSocket(file string) (NetIPSocket, error) {
77 f, err := os.Open(file)
78 if err != nil {
79 return nil, err
80 }
81 defer f.Close()
82
83 var netIPSocket NetIPSocket
84 isUDP := strings.Contains(file, "udp")
85
86 lr := io.LimitReader(f, readLimit)
87 s := bufio.NewScanner(lr)
88 s.Scan()
89 for s.Scan() {
90 fields := strings.Fields(s.Text())
91 line, err := parseNetIPSocketLine(fields, isUDP)
92 if err != nil {
93 return nil, err
94 }
95 netIPSocket = append(netIPSocket, line)
96 }
97 if err := s.Err(); err != nil {
98 return nil, err
99 }
100 return netIPSocket, nil
101 }
102
103
104 func newNetIPSocketSummary(file string) (*NetIPSocketSummary, error) {
105 f, err := os.Open(file)
106 if err != nil {
107 return nil, err
108 }
109 defer f.Close()
110
111 var netIPSocketSummary NetIPSocketSummary
112 var udpPacketDrops uint64
113 isUDP := strings.Contains(file, "udp")
114
115 lr := io.LimitReader(f, readLimit)
116 s := bufio.NewScanner(lr)
117 s.Scan()
118 for s.Scan() {
119 fields := strings.Fields(s.Text())
120 line, err := parseNetIPSocketLine(fields, isUDP)
121 if err != nil {
122 return nil, err
123 }
124 netIPSocketSummary.TxQueueLength += line.TxQueue
125 netIPSocketSummary.RxQueueLength += line.RxQueue
126 netIPSocketSummary.UsedSockets++
127 if isUDP {
128 udpPacketDrops += *line.Drops
129 netIPSocketSummary.Drops = &udpPacketDrops
130 }
131 }
132 if err := s.Err(); err != nil {
133 return nil, err
134 }
135 return &netIPSocketSummary, nil
136 }
137
138
139
140 func parseIP(hexIP string) (net.IP, error) {
141 var byteIP []byte
142 byteIP, err := hex.DecodeString(hexIP)
143 if err != nil {
144 return nil, fmt.Errorf("%s: Cannot parse socket field in %q: %w", ErrFileParse, hexIP, err)
145 }
146 switch len(byteIP) {
147 case 4:
148 return net.IP{byteIP[3], byteIP[2], byteIP[1], byteIP[0]}, nil
149 case 16:
150 i := net.IP{
151 byteIP[3], byteIP[2], byteIP[1], byteIP[0],
152 byteIP[7], byteIP[6], byteIP[5], byteIP[4],
153 byteIP[11], byteIP[10], byteIP[9], byteIP[8],
154 byteIP[15], byteIP[14], byteIP[13], byteIP[12],
155 }
156 return i, nil
157 default:
158 return nil, fmt.Errorf("%s: Unable to parse IP %s: %w", ErrFileParse, hexIP, nil)
159 }
160 }
161
162
163 func parseNetIPSocketLine(fields []string, isUDP bool) (*netIPSocketLine, error) {
164 line := &netIPSocketLine{}
165 if len(fields) < 10 {
166 return nil, fmt.Errorf(
167 "%w: Less than 10 columns found %q",
168 ErrFileParse,
169 strings.Join(fields, " "),
170 )
171 }
172 var err error
173
174
175 s := strings.Split(fields[0], ":")
176 if len(s) != 2 {
177 return nil, fmt.Errorf("%w: Unable to parse sl field in line %q", ErrFileParse, fields[0])
178 }
179
180 if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil {
181 return nil, fmt.Errorf("%s: Unable to parse sl field in %q: %w", ErrFileParse, line.Sl, err)
182 }
183
184 l := strings.Split(fields[1], ":")
185 if len(l) != 2 {
186 return nil, fmt.Errorf("%w: Unable to parse local_address field in %q", ErrFileParse, fields[1])
187 }
188 if line.LocalAddr, err = parseIP(l[0]); err != nil {
189 return nil, err
190 }
191 if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil {
192 return nil, fmt.Errorf("%s: Unable to parse local_address port value line %q: %w", ErrFileParse, line.LocalPort, err)
193 }
194
195
196 r := strings.Split(fields[2], ":")
197 if len(r) != 2 {
198 return nil, fmt.Errorf("%w: Unable to parse rem_address field in %q", ErrFileParse, fields[1])
199 }
200 if line.RemAddr, err = parseIP(r[0]); err != nil {
201 return nil, err
202 }
203 if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil {
204 return nil, fmt.Errorf("%s: Cannot parse rem_address port value in %q: %w", ErrFileParse, line.RemPort, err)
205 }
206
207
208 if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil {
209 return nil, fmt.Errorf("%s: Cannot parse st value in %q: %w", ErrFileParse, line.St, err)
210 }
211
212
213 q := strings.Split(fields[4], ":")
214 if len(q) != 2 {
215 return nil, fmt.Errorf(
216 "%w: Missing colon for tx/rx queues in socket line %q",
217 ErrFileParse,
218 fields[4],
219 )
220 }
221 if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil {
222 return nil, fmt.Errorf("%s: Cannot parse tx_queue value in %q: %w", ErrFileParse, line.TxQueue, err)
223 }
224 if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil {
225 return nil, fmt.Errorf("%s: Cannot parse trx_queue value in %q: %w", ErrFileParse, line.RxQueue, err)
226 }
227
228
229 if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil {
230 return nil, fmt.Errorf("%s: Cannot parse UID value in %q: %w", ErrFileParse, line.UID, err)
231 }
232
233
234 if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil {
235 return nil, fmt.Errorf("%s: Cannot parse inode value in %q: %w", ErrFileParse, line.Inode, err)
236 }
237
238
239 if isUDP {
240 drops, err := strconv.ParseUint(fields[12], 0, 64)
241 if err != nil {
242 return nil, fmt.Errorf("%s: Cannot parse drops value in %q: %w", ErrFileParse, drops, err)
243 }
244 line.Drops = &drops
245 }
246
247 return line, nil
248 }
249
View as plain text