1// 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"
18	"errors"
19	"os"
20	"sort"
21	"strconv"
22	"strings"
23)
24
25// NetDevLine is single line parsed from /proc/net/dev or /proc/[pid]/net/dev.
26type NetDevLine struct {
27	Name         string `json:"name"`          // The name of the interface.
28	RxBytes      uint64 `json:"rx_bytes"`      // Cumulative count of bytes received.
29	RxPackets    uint64 `json:"rx_packets"`    // Cumulative count of packets received.
30	RxErrors     uint64 `json:"rx_errors"`     // Cumulative count of receive errors encountered.
31	RxDropped    uint64 `json:"rx_dropped"`    // Cumulative count of packets dropped while receiving.
32	RxFIFO       uint64 `json:"rx_fifo"`       // Cumulative count of FIFO buffer errors.
33	RxFrame      uint64 `json:"rx_frame"`      // Cumulative count of packet framing errors.
34	RxCompressed uint64 `json:"rx_compressed"` // Cumulative count of compressed packets received by the device driver.
35	RxMulticast  uint64 `json:"rx_multicast"`  // Cumulative count of multicast frames received by the device driver.
36	TxBytes      uint64 `json:"tx_bytes"`      // Cumulative count of bytes transmitted.
37	TxPackets    uint64 `json:"tx_packets"`    // Cumulative count of packets transmitted.
38	TxErrors     uint64 `json:"tx_errors"`     // Cumulative count of transmit errors encountered.
39	TxDropped    uint64 `json:"tx_dropped"`    // Cumulative count of packets dropped while transmitting.
40	TxFIFO       uint64 `json:"tx_fifo"`       // Cumulative count of FIFO buffer errors.
41	TxCollisions uint64 `json:"tx_collisions"` // Cumulative count of collisions detected on the interface.
42	TxCarrier    uint64 `json:"tx_carrier"`    // Cumulative count of carrier losses detected by the device driver.
43	TxCompressed uint64 `json:"tx_compressed"` // Cumulative count of compressed packets transmitted by the device driver.
44}
45
46// NetDev is parsed from /proc/net/dev or /proc/[pid]/net/dev. The map keys
47// are interface names.
48type NetDev map[string]NetDevLine
49
50// NetDev returns kernel/system statistics read from /proc/net/dev.
51func (fs FS) NetDev() (NetDev, error) {
52	return newNetDev(fs.proc.Path("net/dev"))
53}
54
55// NetDev returns kernel/system statistics read from /proc/[pid]/net/dev.
56func (p Proc) NetDev() (NetDev, error) {
57	return newNetDev(p.path("net/dev"))
58}
59
60// newNetDev creates a new NetDev from the contents of the given file.
61func newNetDev(file string) (NetDev, error) {
62	f, err := os.Open(file)
63	if err != nil {
64		return NetDev{}, err
65	}
66	defer f.Close()
67
68	netDev := NetDev{}
69	s := bufio.NewScanner(f)
70	for n := 0; s.Scan(); n++ {
71		// Skip the 2 header lines.
72		if n < 2 {
73			continue
74		}
75
76		line, err := netDev.parseLine(s.Text())
77		if err != nil {
78			return netDev, err
79		}
80
81		netDev[line.Name] = *line
82	}
83
84	return netDev, s.Err()
85}
86
87// parseLine parses a single line from the /proc/net/dev file. Header lines
88// must be filtered prior to calling this method.
89func (netDev NetDev) parseLine(rawLine string) (*NetDevLine, error) {
90	parts := strings.SplitN(rawLine, ":", 2)
91	if len(parts) != 2 {
92		return nil, errors.New("invalid net/dev line, missing colon")
93	}
94	fields := strings.Fields(strings.TrimSpace(parts[1]))
95
96	var err error
97	line := &NetDevLine{}
98
99	// Interface Name
100	line.Name = strings.TrimSpace(parts[0])
101	if line.Name == "" {
102		return nil, errors.New("invalid net/dev line, empty interface name")
103	}
104
105	// RX
106	line.RxBytes, err = strconv.ParseUint(fields[0], 10, 64)
107	if err != nil {
108		return nil, err
109	}
110	line.RxPackets, err = strconv.ParseUint(fields[1], 10, 64)
111	if err != nil {
112		return nil, err
113	}
114	line.RxErrors, err = strconv.ParseUint(fields[2], 10, 64)
115	if err != nil {
116		return nil, err
117	}
118	line.RxDropped, err = strconv.ParseUint(fields[3], 10, 64)
119	if err != nil {
120		return nil, err
121	}
122	line.RxFIFO, err = strconv.ParseUint(fields[4], 10, 64)
123	if err != nil {
124		return nil, err
125	}
126	line.RxFrame, err = strconv.ParseUint(fields[5], 10, 64)
127	if err != nil {
128		return nil, err
129	}
130	line.RxCompressed, err = strconv.ParseUint(fields[6], 10, 64)
131	if err != nil {
132		return nil, err
133	}
134	line.RxMulticast, err = strconv.ParseUint(fields[7], 10, 64)
135	if err != nil {
136		return nil, err
137	}
138
139	// TX
140	line.TxBytes, err = strconv.ParseUint(fields[8], 10, 64)
141	if err != nil {
142		return nil, err
143	}
144	line.TxPackets, err = strconv.ParseUint(fields[9], 10, 64)
145	if err != nil {
146		return nil, err
147	}
148	line.TxErrors, err = strconv.ParseUint(fields[10], 10, 64)
149	if err != nil {
150		return nil, err
151	}
152	line.TxDropped, err = strconv.ParseUint(fields[11], 10, 64)
153	if err != nil {
154		return nil, err
155	}
156	line.TxFIFO, err = strconv.ParseUint(fields[12], 10, 64)
157	if err != nil {
158		return nil, err
159	}
160	line.TxCollisions, err = strconv.ParseUint(fields[13], 10, 64)
161	if err != nil {
162		return nil, err
163	}
164	line.TxCarrier, err = strconv.ParseUint(fields[14], 10, 64)
165	if err != nil {
166		return nil, err
167	}
168	line.TxCompressed, err = strconv.ParseUint(fields[15], 10, 64)
169	if err != nil {
170		return nil, err
171	}
172
173	return line, nil
174}
175
176// Total aggregates the values across interfaces and returns a new NetDevLine.
177// The Name field will be a sorted comma separated list of interface names.
178func (netDev NetDev) Total() NetDevLine {
179	total := NetDevLine{}
180
181	names := make([]string, 0, len(netDev))
182	for _, ifc := range netDev {
183		names = append(names, ifc.Name)
184		total.RxBytes += ifc.RxBytes
185		total.RxPackets += ifc.RxPackets
186		total.RxErrors += ifc.RxErrors
187		total.RxDropped += ifc.RxDropped
188		total.RxFIFO += ifc.RxFIFO
189		total.RxFrame += ifc.RxFrame
190		total.RxCompressed += ifc.RxCompressed
191		total.RxMulticast += ifc.RxMulticast
192		total.TxBytes += ifc.TxBytes
193		total.TxPackets += ifc.TxPackets
194		total.TxErrors += ifc.TxErrors
195		total.TxDropped += ifc.TxDropped
196		total.TxFIFO += ifc.TxFIFO
197		total.TxCollisions += ifc.TxCollisions
198		total.TxCarrier += ifc.TxCarrier
199		total.TxCompressed += ifc.TxCompressed
200	}
201	sort.Strings(names)
202	total.Name = strings.Join(names, ", ")
203
204	return total
205}
206