blob: 1e79e367e7ff8c1777741920e0cca1b465359bb4 [file] [log] [blame]
Lorenz Brun87339502023-03-07 15:49:42 +01001package netdump
2
3import (
4 "errors"
5 "fmt"
6 "net"
7 "runtime"
8 "unsafe"
9
10 "golang.org/x/sys/unix"
11)
12
13// @linux//include/uapi/linux:if.h
14type ifreq struct {
15 ifname [16]byte
16 data uintptr
17}
18
19// @linux//include/uapi/linux:ethtool.h ethtool_perm_addr
20type ethtoolPermAddr struct {
21 Cmd uint32
22 Size uint32
23 // Make this an array for memory layout reasons (see
24 // comment on the kernel struct)
25 Data [32]byte
26}
27
28var errNoPermenentHWAddr = errors.New("no permanent hardware address available")
29
30func isAllZeroes(data []byte) bool {
31 for _, b := range data {
32 if b != 0 {
33 return false
34 }
35 }
36 return true
37}
38
39// Get permanent hardware address on Linux kernels older than 5.6. On newer
40// kernels this is available via normal netlink. Returns errNoPermanentHWAddr
41// in case no such address is available.
42func getPermanentHWAddrLegacy(ifName string) (net.HardwareAddr, error) {
43 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
44 if err != nil {
45 fd, err = unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_GENERIC)
46 if err != nil {
47 return nil, err
48 }
49 }
50 defer unix.Close(fd)
51 var data ethtoolPermAddr
52 data.Cmd = unix.ETHTOOL_GPERMADDR
53 data.Size = uint32(len(data.Data))
54 var req ifreq
55 copy(req.ifname[:], ifName)
56 // See //metropolis/pkg/nvme:cmd_linux.go RawCommand notice on the safety
57 // of this.
58 req.data = uintptr(unsafe.Pointer(&data))
59 for {
60 _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
61 if err == unix.EINTR {
62 continue
63 }
64 if err != 0 {
65 return nil, fmt.Errorf("ioctl(SIOETHTOOL) failed: %w", err)
66 }
67 break
68 }
69 runtime.KeepAlive(req)
70 runtime.KeepAlive(data)
71 // This kernel API is rather old and can indicate the absence of a permanent
72 // hardware MAC in two ways: a size of zero (in case the driver does not
73 // implement a permanent hardware address at all) or an all-zero value in
74 // case the driver has support for returning one but hasn't populated it.
75 if data.Size == 0 || isAllZeroes(data.Data[:data.Size]) {
76 return nil, errNoPermenentHWAddr
77 }
78 return net.HardwareAddr(data.Data[:data.Size]), nil
79}