1
2
3 package net
4
5 import (
6 "context"
7 "fmt"
8 "os/exec"
9 "regexp"
10 "strconv"
11 "strings"
12 "syscall"
13
14 "github.com/shirou/gopsutil/internal/common"
15 )
16
17 func parseNetstatI(output string) ([]IOCountersStat, error) {
18 lines := strings.Split(string(output), "\n")
19 ret := make([]IOCountersStat, 0, len(lines)-1)
20 exists := make([]string, 0, len(ret))
21
22
23 if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" {
24 return nil, fmt.Errorf("not a 'netstat -i' output")
25 }
26
27 for _, line := range lines[1:] {
28 values := strings.Fields(line)
29 if len(values) < 1 || values[0] == "Name" {
30 continue
31 }
32 if common.StringsHas(exists, values[0]) {
33
34 continue
35 }
36 exists = append(exists, values[0])
37
38 if len(values) < 9 {
39 continue
40 }
41
42 base := 1
43
44 if len(values) < 10 {
45 base = 0
46 }
47
48 parsed := make([]uint64, 0, 5)
49 vv := []string{
50 values[base+3],
51 values[base+4],
52 values[base+5],
53 values[base+6],
54 values[base+8],
55 }
56
57 for _, target := range vv {
58 if target == "-" {
59 parsed = append(parsed, 0)
60 continue
61 }
62
63 t, err := strconv.ParseUint(target, 10, 64)
64 if err != nil {
65 return nil, err
66 }
67 parsed = append(parsed, t)
68 }
69
70 n := IOCountersStat{
71 Name: values[0],
72 PacketsRecv: parsed[0],
73 Errin: parsed[1],
74 PacketsSent: parsed[2],
75 Errout: parsed[3],
76 Dropout: parsed[4],
77 }
78 ret = append(ret, n)
79 }
80 return ret, nil
81 }
82
83 func IOCounters(pernic bool) ([]IOCountersStat, error) {
84 return IOCountersWithContext(context.Background(), pernic)
85 }
86
87 func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
88 netstat, err := exec.LookPath("netstat")
89 if err != nil {
90 return nil, err
91 }
92 out, err := invoke.CommandWithContext(ctx, netstat, "-idn")
93 if err != nil {
94 return nil, err
95 }
96
97 iocounters, err := parseNetstatI(string(out))
98 if err != nil {
99 return nil, err
100 }
101 if pernic == false {
102 return getIOCountersAll(iocounters)
103 }
104 return iocounters, nil
105
106 }
107
108
109 func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
110 return IOCountersByFileWithContext(context.Background(), pernic, filename)
111 }
112
113 func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
114 return IOCounters(pernic)
115 }
116
117 func FilterCounters() ([]FilterStat, error) {
118 return FilterCountersWithContext(context.Background())
119 }
120
121 func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
122 return nil, common.ErrNotImplementedError
123 }
124
125 func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
126 return ConntrackStatsWithContext(context.Background(), percpu)
127 }
128
129 func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
130 return nil, common.ErrNotImplementedError
131 }
132
133 func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
134 return ProtoCountersWithContext(context.Background(), protocols)
135 }
136
137 func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
138 return nil, common.ErrNotImplementedError
139 }
140
141 func parseNetstatNetLine(line string) (ConnectionStat, error) {
142 f := strings.Fields(line)
143 if len(f) < 5 {
144 return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
145 }
146
147 var netType, netFamily uint32
148 switch f[0] {
149 case "tcp", "tcp4":
150 netType = syscall.SOCK_STREAM
151 netFamily = syscall.AF_INET
152 case "udp", "udp4":
153 netType = syscall.SOCK_DGRAM
154 netFamily = syscall.AF_INET
155 case "tcp6":
156 netType = syscall.SOCK_STREAM
157 netFamily = syscall.AF_INET6
158 case "udp6":
159 netType = syscall.SOCK_DGRAM
160 netFamily = syscall.AF_INET6
161 default:
162 return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
163 }
164
165 laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
166 if err != nil {
167 return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
168 }
169
170 n := ConnectionStat{
171 Fd: uint32(0),
172 Family: uint32(netFamily),
173 Type: uint32(netType),
174 Laddr: laddr,
175 Raddr: raddr,
176 Pid: int32(0),
177 }
178 if len(f) == 6 {
179 n.Status = f[5]
180 }
181
182 return n, nil
183 }
184
185 var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
186
187
188
189 func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
190 parse := func(l string) (Addr, error) {
191 matches := portMatch.FindStringSubmatch(l)
192 if matches == nil {
193 return Addr{}, fmt.Errorf("wrong addr, %s", l)
194 }
195 host := matches[1]
196 port := matches[2]
197 if host == "*" {
198 switch family {
199 case syscall.AF_INET:
200 host = "0.0.0.0"
201 case syscall.AF_INET6:
202 host = "::"
203 default:
204 return Addr{}, fmt.Errorf("unknown family, %d", family)
205 }
206 }
207 lport, err := strconv.Atoi(port)
208 if err != nil {
209 return Addr{}, err
210 }
211 return Addr{IP: host, Port: uint32(lport)}, nil
212 }
213
214 laddr, err = parse(local)
215 if remote != "*.*" {
216 raddr, err = parse(remote)
217 if err != nil {
218 return laddr, raddr, err
219 }
220 }
221
222 return laddr, raddr, err
223 }
224
225 func parseNetstatUnixLine(f []string) (ConnectionStat, error) {
226 if len(f) < 8 {
227 return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f))
228 }
229
230 var netType uint32
231
232 switch f[1] {
233 case "dgram":
234 netType = syscall.SOCK_DGRAM
235 case "stream":
236 netType = syscall.SOCK_STREAM
237 default:
238 return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1])
239 }
240
241
242 addr := ""
243 if len(f) == 9 {
244 addr = f[8]
245 }
246
247 c := ConnectionStat{
248 Fd: uint32(0),
249 Family: uint32(syscall.AF_UNIX),
250 Type: uint32(netType),
251 Laddr: Addr{
252 IP: addr,
253 },
254 Status: "NONE",
255 Pid: int32(0),
256 }
257
258 return c, nil
259 }
260
261
262
263 func hasCorrectInetProto(kind, proto string) bool {
264 switch kind {
265 case "all", "inet":
266 return true
267 case "unix":
268 return false
269 case "inet4":
270 return !strings.HasSuffix(proto, "6")
271 case "inet6":
272 return strings.HasSuffix(proto, "6")
273 case "tcp":
274 return proto == "tcp" || proto == "tcp4" || proto == "tcp6"
275 case "tcp4":
276 return proto == "tcp" || proto == "tcp4"
277 case "tcp6":
278 return proto == "tcp6"
279 case "udp":
280 return proto == "udp" || proto == "udp4" || proto == "udp6"
281 case "udp4":
282 return proto == "udp" || proto == "udp4"
283 case "udp6":
284 return proto == "udp6"
285 }
286 return false
287 }
288
289 func parseNetstatA(output string, kind string) ([]ConnectionStat, error) {
290 var ret []ConnectionStat
291 lines := strings.Split(string(output), "\n")
292
293 for _, line := range lines {
294 fields := strings.Fields(line)
295 if len(fields) < 1 {
296 continue
297 }
298
299 if strings.HasPrefix(fields[0], "f1") {
300
301 if len(fields) < 2 {
302
303 continue
304 }
305
306 c, err := parseNetstatUnixLine(fields)
307 if err != nil {
308 return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err)
309 }
310
311 ret = append(ret, c)
312
313 } else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") {
314
315 if !hasCorrectInetProto(kind, fields[0]) {
316 continue
317 }
318
319
320
321 if fields[3] == "*.*" {
322 continue
323 }
324
325 c, err := parseNetstatNetLine(line)
326 if err != nil {
327 return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err)
328 }
329
330 ret = append(ret, c)
331 } else {
332
333 continue
334 }
335 }
336
337 return ret, nil
338
339 }
340
341 func Connections(kind string) ([]ConnectionStat, error) {
342 return ConnectionsWithContext(context.Background(), kind)
343 }
344
345 func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
346
347 args := []string{"-na"}
348 switch strings.ToLower(kind) {
349 default:
350 fallthrough
351 case "":
352 kind = "all"
353 case "all":
354
355 case "inet", "inet4", "inet6":
356 args = append(args, "-finet")
357 case "tcp", "tcp4", "tcp6":
358 args = append(args, "-finet")
359 case "udp", "udp4", "udp6":
360 args = append(args, "-finet")
361 case "unix":
362 args = append(args, "-funix")
363 }
364
365 netstat, err := exec.LookPath("netstat")
366 if err != nil {
367 return nil, err
368 }
369 out, err := invoke.CommandWithContext(ctx, netstat, args...)
370
371 if err != nil {
372 return nil, err
373 }
374
375 ret, err := parseNetstatA(string(out), kind)
376 if err != nil {
377 return nil, err
378 }
379
380 return ret, nil
381
382 }
383
384 func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
385 return ConnectionsMaxWithContext(context.Background(), kind, max)
386 }
387
388 func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
389 return []ConnectionStat{}, common.ErrNotImplementedError
390 }
391
392
393
394
395 func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
396 return ConnectionsWithoutUidsWithContext(context.Background(), kind)
397 }
398
399 func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
400 return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
401 }
402
403 func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
404 return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
405 }
406
407 func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
408 return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
409 }
410
411 func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
412 return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
413 }
414
415 func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
416 return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
417 }
418
419 func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
420 return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
421 }
422
423 func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
424 return []ConnectionStat{}, common.ErrNotImplementedError
425 }
426
View as plain text