6
6
package net
7
7
8
8
import (
9
- "bufio"
10
- "bytes"
11
- "fmt"
12
9
"os"
13
- "os/exec"
14
10
"path/filepath"
15
11
"strings"
16
12
13
+ "github.com/safchain/ethtool"
14
+
17
15
"github.com/jaypipes/ghw/pkg/context"
18
16
"github.com/jaypipes/ghw/pkg/linuxpath"
19
- "github.com/jaypipes/ghw/pkg/util"
20
- )
21
-
22
- const (
23
- warnEthtoolNotInstalled = `ethtool not installed. Cannot grab NIC capabilities`
24
17
)
25
18
26
19
func (i * Info ) load () error {
@@ -37,14 +30,6 @@ func nics(ctx *context.Context) []*NIC {
37
30
return nics
38
31
}
39
32
40
- etAvailable := ctx .EnableTools
41
- if etAvailable {
42
- if etInstalled := ethtoolInstalled (); ! etInstalled {
43
- ctx .Warn (warnEthtoolNotInstalled )
44
- etAvailable = false
45
- }
46
- }
47
-
48
33
for _ , file := range files {
49
34
filename := file .Name ()
50
35
// Ignore loopback...
@@ -66,15 +51,10 @@ func nics(ctx *context.Context) []*NIC {
66
51
67
52
mac := netDeviceMacAddress (paths , filename )
68
53
nic .MacAddress = mac
69
- nic .MACAddress = mac
70
- if etAvailable {
71
- nic .netDeviceParseEthtool (ctx , filename )
72
- } else {
73
- nic .Capabilities = []* NICCapability {}
74
- // Sets NIC struct fields from data in SysFs
75
- nic .setNicAttrSysFs (paths , filename )
76
- }
77
-
54
+ // Get speed and duplex from /sys/class/net/$DEVICE/ directory
55
+ nic .Speed = readFile (filepath .Join (paths .SysClassNet , filename , "speed" ))
56
+ nic .Duplex = readFile (filepath .Join (paths .SysClassNet , filename , "duplex" ))
57
+ nic .Capabilities = netDeviceCapabilities (ctx , filename )
78
58
nic .PCIAddress = netDevicePCIAddress (paths .SysClassNet , filename )
79
59
80
60
nics = append (nics , nic )
@@ -103,99 +83,30 @@ func netDeviceMacAddress(paths *linuxpath.Paths, dev string) string {
103
83
return strings .TrimSpace (string (contents ))
104
84
}
105
85
106
- func ethtoolInstalled () bool {
107
- _ , err := exec .LookPath ("ethtool" )
108
- return err == nil
109
- }
110
-
111
- func (n * NIC ) netDeviceParseEthtool (ctx * context.Context , dev string ) {
112
- var out bytes.Buffer
113
- path , _ := exec .LookPath ("ethtool" )
114
-
115
- // Get auto-negotiation and pause-frame-use capabilities from "ethtool" (with no options)
116
- // Populate Speed, Duplex, SupportedLinkModes, SupportedPorts, SupportedFECModes,
117
- // AdvertisedLinkModes, and AdvertisedFECModes attributes from "ethtool" output.
118
- cmd := exec .Command (path , dev )
119
- cmd .Stdout = & out
120
- err := cmd .Run ()
121
- if err == nil {
122
- m := parseNicAttrEthtool (& out )
123
- n .Capabilities = append (n .Capabilities , autoNegCap (m ))
124
- n .Capabilities = append (n .Capabilities , pauseFrameUseCap (m ))
86
+ func netDeviceCapabilities (ctx * context.Context , dev string ) []* NICCapability {
87
+ caps := []* NICCapability {}
125
88
126
- // Update NIC Attributes with ethtool output
127
- n .Speed = strings .Join (m ["Speed" ], "" )
128
- n .Duplex = strings .Join (m ["Duplex" ], "" )
129
- n .SupportedLinkModes = m ["Supported link modes" ]
130
- n .SupportedPorts = m ["Supported ports" ]
131
- n .SupportedFECModes = m ["Supported FEC modes" ]
132
- n .AdvertisedLinkModes = m ["Advertised link modes" ]
133
- n .AdvertisedFECModes = m ["Advertised FEC modes" ]
134
- } else {
135
- msg := fmt .Sprintf ("could not grab NIC link info for %s: %s" , dev , err )
136
- ctx .Warn (msg )
89
+ ethHandle , err := ethtool .NewEthtool ()
90
+ if err != nil {
91
+ ctx .Warn ("failed to create ethtool instance: %v" , err )
92
+ return caps
137
93
}
94
+ defer ethHandle .Close ()
138
95
139
- // Get all other capabilities from "ethtool -k"
140
- cmd = exec .Command (path , "-k" , dev )
141
- cmd .Stdout = & out
142
- err = cmd .Run ()
143
- if err == nil {
144
- // The out variable will now contain something that looks like the
145
- // following.
146
- //
147
- // Features for enp58s0f1:
148
- // rx-checksumming: on
149
- // tx-checksumming: off
150
- // tx-checksum-ipv4: off
151
- // tx-checksum-ip-generic: off [fixed]
152
- // tx-checksum-ipv6: off
153
- // tx-checksum-fcoe-crc: off [fixed]
154
- // tx-checksum-sctp: off [fixed]
155
- // scatter-gather: off
156
- // tx-scatter-gather: off
157
- // tx-scatter-gather-fraglist: off [fixed]
158
- // tcp-segmentation-offload: off
159
- // tx-tcp-segmentation: off
160
- // tx-tcp-ecn-segmentation: off [fixed]
161
- // tx-tcp-mangleid-segmentation: off
162
- // tx-tcp6-segmentation: off
163
- // < snipped >
164
- scanner := bufio .NewScanner (& out )
165
- // Skip the first line...
166
- scanner .Scan ()
167
- for scanner .Scan () {
168
- line := strings .TrimPrefix (scanner .Text (), "\t " )
169
- n .Capabilities = append (n .Capabilities , netParseEthtoolFeature (line ))
170
- }
171
-
172
- } else {
173
- msg := fmt .Sprintf ("could not grab NIC capabilities for %s: %s" , dev , err )
174
- ctx .Warn (msg )
96
+ feats , err := ethHandle .FeaturesWithState (dev )
97
+ if err != nil {
98
+ ctx .Warn ("failed to get ethtool features state for %s: %v" , dev , err )
99
+ return caps
175
100
}
176
101
177
- }
178
-
179
- // netParseEthtoolFeature parses a line from the ethtool -k output and returns
180
- // a NICCapability.
181
- //
182
- // The supplied line will look like the following:
183
- //
184
- // tx-checksum-ip-generic: off [fixed]
185
- //
186
- // [fixed] indicates that the feature may not be turned on/off. Note: it makes
187
- // no difference whether a privileged user runs `ethtool -k` when determining
188
- // whether [fixed] appears for a feature.
189
- func netParseEthtoolFeature (line string ) * NICCapability {
190
- parts := strings .Fields (line )
191
- cap := strings .TrimSuffix (parts [0 ], ":" )
192
- enabled := parts [1 ] == "on"
193
- fixed := len (parts ) == 3 && parts [2 ] == "[fixed]"
194
- return & NICCapability {
195
- Name : cap ,
196
- IsEnabled : enabled ,
197
- CanEnable : ! fixed ,
102
+ for key , state := range feats {
103
+ caps = append (caps , & NICCapability {
104
+ Name : key ,
105
+ IsEnabled : state .Active ,
106
+ CanEnable : state .Available ,
107
+ })
198
108
}
109
+ return caps
199
110
}
200
111
201
112
func netDevicePCIAddress (netDevDir , netDevName string ) * string {
@@ -249,110 +160,10 @@ func netDevicePCIAddress(netDevDir, netDevName string) *string {
249
160
return & pciAddr
250
161
}
251
162
252
- func (nic * NIC ) setNicAttrSysFs (paths * linuxpath.Paths , dev string ) {
253
- // Get speed and duplex from /sys/class/net/$DEVICE/ directory
254
- nic .Speed = readFile (filepath .Join (paths .SysClassNet , dev , "speed" ))
255
- nic .Duplex = readFile (filepath .Join (paths .SysClassNet , dev , "duplex" ))
256
- }
257
-
258
163
func readFile (path string ) string {
259
164
contents , err := os .ReadFile (path )
260
165
if err != nil {
261
166
return ""
262
167
}
263
168
return strings .TrimSpace (string (contents ))
264
169
}
265
-
266
- func autoNegCap (m map [string ][]string ) * NICCapability {
267
- autoNegotiation := NICCapability {Name : "auto-negotiation" , IsEnabled : false , CanEnable : false }
268
-
269
- an , anErr := util .ParseBool (strings .Join (m ["Auto-negotiation" ], "" ))
270
- aan , aanErr := util .ParseBool (strings .Join (m ["Advertised auto-negotiation" ], "" ))
271
- if an && aan && aanErr == nil && anErr == nil {
272
- autoNegotiation .IsEnabled = true
273
- }
274
-
275
- san , err := util .ParseBool (strings .Join (m ["Supports auto-negotiation" ], "" ))
276
- if san && err == nil {
277
- autoNegotiation .CanEnable = true
278
- }
279
-
280
- return & autoNegotiation
281
- }
282
-
283
- func pauseFrameUseCap (m map [string ][]string ) * NICCapability {
284
- pauseFrameUse := NICCapability {Name : "pause-frame-use" , IsEnabled : false , CanEnable : false }
285
-
286
- apfu , err := util .ParseBool (strings .Join (m ["Advertised pause frame use" ], "" ))
287
- if apfu && err == nil {
288
- pauseFrameUse .IsEnabled = true
289
- }
290
-
291
- spfu , err := util .ParseBool (strings .Join (m ["Supports pause frame use" ], "" ))
292
- if spfu && err == nil {
293
- pauseFrameUse .CanEnable = true
294
- }
295
-
296
- return & pauseFrameUse
297
- }
298
-
299
- func parseNicAttrEthtool (out * bytes.Buffer ) map [string ][]string {
300
- // The out variable will now contain something that looks like the
301
- // following.
302
- //
303
- //Settings for eth0:
304
- // Supported ports: [ TP ]
305
- // Supported link modes: 10baseT/Half 10baseT/Full
306
- // 100baseT/Half 100baseT/Full
307
- // 1000baseT/Full
308
- // Supported pause frame use: No
309
- // Supports auto-negotiation: Yes
310
- // Supported FEC modes: Not reported
311
- // Advertised link modes: 10baseT/Half 10baseT/Full
312
- // 100baseT/Half 100baseT/Full
313
- // 1000baseT/Full
314
- // Advertised pause frame use: No
315
- // Advertised auto-negotiation: Yes
316
- // Advertised FEC modes: Not reported
317
- // Speed: 1000Mb/s
318
- // Duplex: Full
319
- // Auto-negotiation: on
320
- // Port: Twisted Pair
321
- // PHYAD: 1
322
- // Transceiver: internal
323
- // MDI-X: off (auto)
324
- // Supports Wake-on: pumbg
325
- // Wake-on: d
326
- // Current message level: 0x00000007 (7)
327
- // drv probe link
328
- // Link detected: yes
329
-
330
- scanner := bufio .NewScanner (out )
331
- // Skip the first line
332
- scanner .Scan ()
333
- m := make (map [string ][]string )
334
- var name string
335
- for scanner .Scan () {
336
- var fields []string
337
- if strings .Contains (scanner .Text (), ":" ) {
338
- line := strings .Split (scanner .Text (), ":" )
339
- name = strings .TrimSpace (line [0 ])
340
- str := strings .Trim (strings .TrimSpace (line [1 ]), "[]" )
341
- switch str {
342
- case
343
- "Not reported" ,
344
- "Unknown" :
345
- continue
346
- }
347
- fields = strings .Fields (str )
348
- } else {
349
- fields = strings .Fields (strings .Trim (strings .TrimSpace (scanner .Text ()), "[]" ))
350
- }
351
-
352
- for _ , f := range fields {
353
- m [name ] = append (m [name ], strings .TrimSpace (f ))
354
- }
355
- }
356
-
357
- return m
358
- }
0 commit comments