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 var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
18
19 func ParseNetstat(output string, mode string,
20 iocs map[string]IOCountersStat) error {
21 lines := strings.Split(output, "\n")
22
23 exists := make([]string, 0, len(lines)-1)
24
25 columns := 6
26 if mode == "ind" {
27 columns = 10
28 }
29 for _, line := range lines {
30 values := strings.Fields(line)
31 if len(values) < 1 || values[0] == "Name" {
32 continue
33 }
34 if common.StringsHas(exists, values[0]) {
35
36 continue
37 }
38
39 if len(values) < columns {
40 continue
41 }
42 base := 1
43
44 if len(values) < columns {
45 base = 0
46 }
47
48 parsed := make([]uint64, 0, 8)
49 var vv []string
50 if mode == "inb" {
51 vv = []string{
52 values[base+3],
53 values[base+4],
54 }
55 } else {
56 vv = []string{
57 values[base+3],
58 values[base+4],
59 values[base+5],
60 values[base+6],
61 values[base+8],
62 }
63 }
64 for _, target := range vv {
65 if target == "-" {
66 parsed = append(parsed, 0)
67 continue
68 }
69
70 t, err := strconv.ParseUint(target, 10, 64)
71 if err != nil {
72 return err
73 }
74 parsed = append(parsed, t)
75 }
76 exists = append(exists, values[0])
77
78 n, present := iocs[values[0]]
79 if !present {
80 n = IOCountersStat{Name: values[0]}
81 }
82 if mode == "inb" {
83 n.BytesRecv = parsed[0]
84 n.BytesSent = parsed[1]
85 } else {
86 n.PacketsRecv = parsed[0]
87 n.Errin = parsed[1]
88 n.PacketsSent = parsed[2]
89 n.Errout = parsed[3]
90 n.Dropin = parsed[4]
91 n.Dropout = parsed[4]
92 }
93
94 iocs[n.Name] = n
95 }
96 return nil
97 }
98
99 func IOCounters(pernic bool) ([]IOCountersStat, error) {
100 return IOCountersWithContext(context.Background(), pernic)
101 }
102
103 func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
104 netstat, err := exec.LookPath("netstat")
105 if err != nil {
106 return nil, err
107 }
108 out, err := invoke.CommandWithContext(ctx, netstat, "-inb")
109 if err != nil {
110 return nil, err
111 }
112 out2, err := invoke.CommandWithContext(ctx, netstat, "-ind")
113 if err != nil {
114 return nil, err
115 }
116 iocs := make(map[string]IOCountersStat)
117
118 lines := strings.Split(string(out), "\n")
119 ret := make([]IOCountersStat, 0, len(lines)-1)
120
121 err = ParseNetstat(string(out), "inb", iocs)
122 if err != nil {
123 return nil, err
124 }
125 err = ParseNetstat(string(out2), "ind", iocs)
126 if err != nil {
127 return nil, err
128 }
129
130 for _, ioc := range iocs {
131 ret = append(ret, ioc)
132 }
133
134 if pernic == false {
135 return getIOCountersAll(ret)
136 }
137
138 return ret, nil
139 }
140
141
142 func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
143 return IOCountersByFileWithContext(context.Background(), pernic, filename)
144 }
145
146 func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
147 return IOCounters(pernic)
148 }
149
150 func FilterCounters() ([]FilterStat, error) {
151 return FilterCountersWithContext(context.Background())
152 }
153
154 func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
155 return nil, common.ErrNotImplementedError
156 }
157
158 func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
159 return ConntrackStatsWithContext(context.Background(), percpu)
160 }
161
162 func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
163 return nil, common.ErrNotImplementedError
164 }
165
166
167
168
169
170 func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
171 return ProtoCountersWithContext(context.Background(), protocols)
172 }
173
174 func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
175 return nil, common.ErrNotImplementedError
176 }
177
178 func parseNetstatLine(line string) (ConnectionStat, error) {
179 f := strings.Fields(line)
180 if len(f) < 5 {
181 return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
182 }
183
184 var netType, netFamily uint32
185 switch f[0] {
186 case "tcp":
187 netType = syscall.SOCK_STREAM
188 netFamily = syscall.AF_INET
189 case "udp":
190 netType = syscall.SOCK_DGRAM
191 netFamily = syscall.AF_INET
192 case "tcp6":
193 netType = syscall.SOCK_STREAM
194 netFamily = syscall.AF_INET6
195 case "udp6":
196 netType = syscall.SOCK_DGRAM
197 netFamily = syscall.AF_INET6
198 default:
199 return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
200 }
201
202 laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
203 if err != nil {
204 return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
205 }
206
207 n := ConnectionStat{
208 Fd: uint32(0),
209 Family: uint32(netFamily),
210 Type: uint32(netType),
211 Laddr: laddr,
212 Raddr: raddr,
213 Pid: int32(0),
214 }
215 if len(f) == 6 {
216 n.Status = f[5]
217 }
218
219 return n, nil
220 }
221
222 func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
223 parse := func(l string) (Addr, error) {
224 matches := portMatch.FindStringSubmatch(l)
225 if matches == nil {
226 return Addr{}, fmt.Errorf("wrong addr, %s", l)
227 }
228 host := matches[1]
229 port := matches[2]
230 if host == "*" {
231 switch family {
232 case syscall.AF_INET:
233 host = "0.0.0.0"
234 case syscall.AF_INET6:
235 host = "::"
236 default:
237 return Addr{}, fmt.Errorf("unknown family, %d", family)
238 }
239 }
240 lport, err := strconv.Atoi(port)
241 if err != nil {
242 return Addr{}, err
243 }
244 return Addr{IP: host, Port: uint32(lport)}, nil
245 }
246
247 laddr, err = parse(local)
248 if remote != "*.*" {
249 raddr, err = parse(remote)
250 if err != nil {
251 return laddr, raddr, err
252 }
253 }
254
255 return laddr, raddr, err
256 }
257
258
259 func Connections(kind string) ([]ConnectionStat, error) {
260 return ConnectionsWithContext(context.Background(), kind)
261 }
262
263 func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
264 var ret []ConnectionStat
265
266 args := []string{"-na"}
267 switch strings.ToLower(kind) {
268 default:
269 fallthrough
270 case "":
271 fallthrough
272 case "all":
273 fallthrough
274 case "inet":
275
276 case "inet4":
277 args = append(args, "-finet")
278 case "inet6":
279 args = append(args, "-finet6")
280 case "tcp":
281 args = append(args, "-ptcp")
282 case "tcp4":
283 args = append(args, "-ptcp", "-finet")
284 case "tcp6":
285 args = append(args, "-ptcp", "-finet6")
286 case "udp":
287 args = append(args, "-pudp")
288 case "udp4":
289 args = append(args, "-pudp", "-finet")
290 case "udp6":
291 args = append(args, "-pudp", "-finet6")
292 case "unix":
293 return ret, common.ErrNotImplementedError
294 }
295
296 netstat, err := exec.LookPath("netstat")
297 if err != nil {
298 return nil, err
299 }
300 out, err := invoke.CommandWithContext(ctx, netstat, args...)
301
302 if err != nil {
303 return nil, err
304 }
305 lines := strings.Split(string(out), "\n")
306 for _, line := range lines {
307 if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) {
308 continue
309 }
310 n, err := parseNetstatLine(line)
311 if err != nil {
312 continue
313 }
314
315 ret = append(ret, n)
316 }
317
318 return ret, nil
319 }
320
View as plain text