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