net/dump: init
This adds netdump, a library which dumps the network configuration of a
Linux host via netlink and generates a Net specification from it.
Change-Id: I78007df1da4fbf24ba9128eb2e966df0dea9274e
Reviewed-on: https://review.monogon.dev/c/monogon/+/1231
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Leopold Schabel <leo@monogon.tech>
diff --git a/net/dump/hwaddr_compat.go b/net/dump/hwaddr_compat.go
new file mode 100644
index 0000000..1e79e36
--- /dev/null
+++ b/net/dump/hwaddr_compat.go
@@ -0,0 +1,79 @@
+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
+}