blob: 9cd99a8f99e947521ce52bc8d446057c770fd2fa [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)
Lorenz Brunb4f50242023-11-14 21:51:07 +010051
52 var ioctlPins runtime.Pinner
53 defer ioctlPins.Unpin()
54
Lorenz Brun87339502023-03-07 15:49:42 +010055 var data ethtoolPermAddr
56 data.Cmd = unix.ETHTOOL_GPERMADDR
57 data.Size = uint32(len(data.Data))
Lorenz Brunb4f50242023-11-14 21:51:07 +010058
Lorenz Brun87339502023-03-07 15:49:42 +010059 var req ifreq
60 copy(req.ifname[:], ifName)
Lorenz Brunb4f50242023-11-14 21:51:07 +010061 ioctlPins.Pin(&data)
Lorenz Brun87339502023-03-07 15:49:42 +010062 req.data = uintptr(unsafe.Pointer(&data))
63 for {
64 _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
65 if err == unix.EINTR {
66 continue
67 }
68 if err != 0 {
69 return nil, fmt.Errorf("ioctl(SIOETHTOOL) failed: %w", err)
70 }
71 break
72 }
73 runtime.KeepAlive(req)
74 runtime.KeepAlive(data)
75 // This kernel API is rather old and can indicate the absence of a permanent
76 // hardware MAC in two ways: a size of zero (in case the driver does not
77 // implement a permanent hardware address at all) or an all-zero value in
78 // case the driver has support for returning one but hasn't populated it.
79 if data.Size == 0 || isAllZeroes(data.Data[:data.Size]) {
80 return nil, errNoPermenentHWAddr
81 }
82 return net.HardwareAddr(data.Data[:data.Size]), nil
83}