| package netdump |
| |
| import ( |
| "errors" |
| "fmt" |
| "net" |
| "runtime" |
| "unsafe" |
| |
| "golang.org/x/sys/unix" |
| ) |
| |
| // @linux//include/uapi/linux:if.h |
| type ifreq struct { |
| ifname [16]byte |
| data uintptr |
| } |
| |
| // @linux//include/uapi/linux:ethtool.h ethtool_perm_addr |
| type ethtoolPermAddr struct { |
| Cmd uint32 |
| Size uint32 |
| // Make this an array for memory layout reasons (see |
| // comment on the kernel struct) |
| Data [32]byte |
| } |
| |
| var errNoPermenentHWAddr = errors.New("no permanent hardware address available") |
| |
| func isAllZeroes(data []byte) bool { |
| for _, b := range data { |
| if b != 0 { |
| return false |
| } |
| } |
| return true |
| } |
| |
| // Get permanent hardware address on Linux kernels older than 5.6. On newer |
| // kernels this is available via normal netlink. Returns errNoPermanentHWAddr |
| // in case no such address is available. |
| func getPermanentHWAddrLegacy(ifName string) (net.HardwareAddr, error) { |
| fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) |
| if err != nil { |
| fd, err = unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_GENERIC) |
| if err != nil { |
| return nil, err |
| } |
| } |
| defer unix.Close(fd) |
| var data ethtoolPermAddr |
| data.Cmd = unix.ETHTOOL_GPERMADDR |
| data.Size = uint32(len(data.Data)) |
| var req ifreq |
| copy(req.ifname[:], ifName) |
| // See //metropolis/pkg/nvme:cmd_linux.go RawCommand notice on the safety |
| // of this. |
| req.data = uintptr(unsafe.Pointer(&data)) |
| for { |
| _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req))) |
| if err == unix.EINTR { |
| continue |
| } |
| if err != 0 { |
| return nil, fmt.Errorf("ioctl(SIOETHTOOL) failed: %w", err) |
| } |
| break |
| } |
| runtime.KeepAlive(req) |
| runtime.KeepAlive(data) |
| // This kernel API is rather old and can indicate the absence of a permanent |
| // hardware MAC in two ways: a size of zero (in case the driver does not |
| // implement a permanent hardware address at all) or an all-zero value in |
| // case the driver has support for returning one but hasn't populated it. |
| if data.Size == 0 || isAllZeroes(data.Data[:data.Size]) { |
| return nil, errNoPermenentHWAddr |
| } |
| return net.HardwareAddr(data.Data[:data.Size]), nil |
| } |