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
14// +build !windows
15
16package sysfs
17
18import (
19	"fmt"
20	"io/ioutil"
21	"os"
22	"path/filepath"
23
24	"github.com/prometheus/procfs/internal/util"
25)
26
27const netclassPath = "class/net"
28
29// NetClassIface contains info from files in /sys/class/net/<iface>
30// for single interface (iface).
31type NetClassIface struct {
32	Name             string // Interface name
33	AddrAssignType   *int64 // /sys/class/net/<iface>/addr_assign_type
34	AddrLen          *int64 // /sys/class/net/<iface>/addr_len
35	Address          string // /sys/class/net/<iface>/address
36	Broadcast        string // /sys/class/net/<iface>/broadcast
37	Carrier          *int64 // /sys/class/net/<iface>/carrier
38	CarrierChanges   *int64 // /sys/class/net/<iface>/carrier_changes
39	CarrierUpCount   *int64 // /sys/class/net/<iface>/carrier_up_count
40	CarrierDownCount *int64 // /sys/class/net/<iface>/carrier_down_count
41	DevID            *int64 // /sys/class/net/<iface>/dev_id
42	Dormant          *int64 // /sys/class/net/<iface>/dormant
43	Duplex           string // /sys/class/net/<iface>/duplex
44	Flags            *int64 // /sys/class/net/<iface>/flags
45	IfAlias          string // /sys/class/net/<iface>/ifalias
46	IfIndex          *int64 // /sys/class/net/<iface>/ifindex
47	IfLink           *int64 // /sys/class/net/<iface>/iflink
48	LinkMode         *int64 // /sys/class/net/<iface>/link_mode
49	MTU              *int64 // /sys/class/net/<iface>/mtu
50	NameAssignType   *int64 // /sys/class/net/<iface>/name_assign_type
51	NetDevGroup      *int64 // /sys/class/net/<iface>/netdev_group
52	OperState        string // /sys/class/net/<iface>/operstate
53	PhysPortID       string // /sys/class/net/<iface>/phys_port_id
54	PhysPortName     string // /sys/class/net/<iface>/phys_port_name
55	PhysSwitchID     string // /sys/class/net/<iface>/phys_switch_id
56	Speed            *int64 // /sys/class/net/<iface>/speed
57	TxQueueLen       *int64 // /sys/class/net/<iface>/tx_queue_len
58	Type             *int64 // /sys/class/net/<iface>/type
59}
60
61// NetClass is collection of info for every interface (iface) in /sys/class/net. The map keys
62// are interface (iface) names.
63type NetClass map[string]NetClassIface
64
65// NetClassDevices scans /sys/class/net for devices and returns them as a list of names.
66func (fs FS) NetClassDevices() ([]string, error) {
67	var res []string
68	path := fs.sys.Path(netclassPath)
69
70	devices, err := ioutil.ReadDir(path)
71	if err != nil {
72		return res, fmt.Errorf("cannot access dir %q: %w", path, err)
73	}
74
75	for _, deviceDir := range devices {
76		if deviceDir.Mode().IsRegular() {
77			continue
78		}
79		res = append(res, deviceDir.Name())
80	}
81
82	return res, nil
83}
84
85// NetClass returns info for all net interfaces (iface) read from /sys/class/net/<iface>.
86func (fs FS) NetClass() (NetClass, error) {
87	devices, err := fs.NetClassDevices()
88	if err != nil {
89		return nil, err
90	}
91
92	path := fs.sys.Path(netclassPath)
93	netClass := NetClass{}
94	for _, deviceDir := range devices {
95		interfaceClass, err := netClass.parseNetClassIface(filepath.Join(path, deviceDir))
96		if err != nil {
97			return nil, err
98		}
99		interfaceClass.Name = deviceDir
100		netClass[deviceDir] = *interfaceClass
101	}
102	return netClass, nil
103}
104
105// parseNetClassIface scans predefined files in /sys/class/net/<iface>
106// directory and gets their contents.
107func (nc NetClass) parseNetClassIface(devicePath string) (*NetClassIface, error) {
108	interfaceClass := NetClassIface{}
109
110	files, err := ioutil.ReadDir(devicePath)
111	if err != nil {
112		return nil, err
113	}
114
115	for _, f := range files {
116		if !f.Mode().IsRegular() {
117			continue
118		}
119		name := filepath.Join(devicePath, f.Name())
120		value, err := util.SysReadFile(name)
121		if err != nil {
122			if os.IsNotExist(err) || os.IsPermission(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
123				continue
124			}
125			return nil, fmt.Errorf("failed to read file %q: %w", name, err)
126		}
127		vp := util.NewValueParser(value)
128		switch f.Name() {
129		case "addr_assign_type":
130			interfaceClass.AddrAssignType = vp.PInt64()
131		case "addr_len":
132			interfaceClass.AddrLen = vp.PInt64()
133		case "address":
134			interfaceClass.Address = value
135		case "broadcast":
136			interfaceClass.Broadcast = value
137		case "carrier":
138			interfaceClass.Carrier = vp.PInt64()
139		case "carrier_changes":
140			interfaceClass.CarrierChanges = vp.PInt64()
141		case "carrier_up_count":
142			interfaceClass.CarrierUpCount = vp.PInt64()
143		case "carrier_down_count":
144			interfaceClass.CarrierDownCount = vp.PInt64()
145		case "dev_id":
146			interfaceClass.DevID = vp.PInt64()
147		case "dormant":
148			interfaceClass.Dormant = vp.PInt64()
149		case "duplex":
150			interfaceClass.Duplex = value
151		case "flags":
152			interfaceClass.Flags = vp.PInt64()
153		case "ifalias":
154			interfaceClass.IfAlias = value
155		case "ifindex":
156			interfaceClass.IfIndex = vp.PInt64()
157		case "iflink":
158			interfaceClass.IfLink = vp.PInt64()
159		case "link_mode":
160			interfaceClass.LinkMode = vp.PInt64()
161		case "mtu":
162			interfaceClass.MTU = vp.PInt64()
163		case "name_assign_type":
164			interfaceClass.NameAssignType = vp.PInt64()
165		case "netdev_group":
166			interfaceClass.NetDevGroup = vp.PInt64()
167		case "operstate":
168			interfaceClass.OperState = value
169		case "phys_port_id":
170			interfaceClass.PhysPortID = value
171		case "phys_port_name":
172			interfaceClass.PhysPortName = value
173		case "phys_switch_id":
174			interfaceClass.PhysSwitchID = value
175		case "speed":
176			interfaceClass.Speed = vp.PInt64()
177		case "tx_queue_len":
178			interfaceClass.TxQueueLen = vp.PInt64()
179		case "type":
180			interfaceClass.Type = vp.PInt64()
181		}
182	}
183	return &interfaceClass, nil
184
185}
186