[lxc-devel] [lxd/master] ethtool: resources/ethtool: implement ETHTOOL_GLINKSETTINGS

brauner on Github lxc-bot at linuxcontainers.org
Thu May 7 14:37:48 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200507/b06a5c89/attachment.bin>
-------------- next part --------------
From 34b5ea2314c2e1a9df4abc0911bbc1c1a77aaa5d Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Wed, 6 May 2020 09:33:56 +0200
Subject: [PATCH 1/2] ethtool: add ethtoolGset() helper

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/resources/network_ethtool.go | 88 ++++++++++++++++++--------------
 1 file changed, 49 insertions(+), 39 deletions(-)

diff --git a/lxd/resources/network_ethtool.go b/lxd/resources/network_ethtool.go
index ce3d00fb52..c910045f06 100644
--- a/lxd/resources/network_ethtool.go
+++ b/lxd/resources/network_ethtool.go
@@ -144,51 +144,14 @@ func ethtoolAddCardInfo(name string, info *api.ResourcesNetworkCard) error {
 	return nil
 }
 
-func ethtoolAddPortInfo(info *api.ResourcesNetworkCardPort) error {
-	// Open FD
-	ethtoolFd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
-	if err != nil {
-		return errors.Wrap(err, "Failed to open IPPROTO_IP socket")
-	}
-	defer unix.Close(ethtoolFd)
-
-	// Prepare the request struct
-	req := ethtoolReq{}
-	copy(req.name[:], []byte(info.ID))
-
-	// Try to get MAC address
-	ethPermaddr := ethtoolPermAddr{
-		cmd:  0x00000020,
-		size: 32,
-	}
-	req.data = uintptr(unsafe.Pointer(&ethPermaddr))
-
-	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
-	if errno == 0 {
-		hwaddr := net.HardwareAddr(ethPermaddr.data[0:ethPermaddr.size])
-		info.Address = hwaddr.String()
-	}
-
-	// Link state
-	ethGlink := ethtoolValue{
-		cmd: 0x0000000a,
-	}
-	req.data = uintptr(unsafe.Pointer(&ethGlink))
-
-	_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
-	if errno != 0 {
-		return errors.Wrap(unix.Errno(errno), "Failed to ETHTOOL_GLINK")
-	}
-
-	info.LinkDetected = ethGlink.data == 1
-
+func ethtoolGset(ethtoolFd int, req *ethtoolReq, info *api.ResourcesNetworkCardPort) error {
 	// Interface info
 	ethCmd := ethtoolCmd{
 		cmd: 0x00000001,
 	}
 	req.data = uintptr(unsafe.Pointer(&ethCmd))
 
-	_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
+	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
 	if errno != 0 {
 		return errors.Wrap(unix.Errno(errno), "Failed to ETHTOOL_GSET")
 	}
@@ -255,3 +218,50 @@ func ethtoolAddPortInfo(info *api.ResourcesNetworkCardPort) error {
 
 	return nil
 }
+
+func ethtoolAddPortInfo(info *api.ResourcesNetworkCardPort) error {
+	// Open FD
+	ethtoolFd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
+	if err != nil {
+		return errors.Wrap(err, "Failed to open IPPROTO_IP socket")
+	}
+	defer unix.Close(ethtoolFd)
+
+	// Prepare the request struct
+	req := ethtoolReq{}
+	copy(req.name[:], []byte(info.ID))
+
+	// Try to get MAC address
+	ethPermaddr := ethtoolPermAddr{
+		cmd:  0x00000020,
+		size: 32,
+	}
+	req.data = uintptr(unsafe.Pointer(&ethPermaddr))
+
+	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
+	if errno == 0 {
+		hwaddr := net.HardwareAddr(ethPermaddr.data[0:ethPermaddr.size])
+		info.Address = hwaddr.String()
+	}
+
+	// Link state
+	ethGlink := ethtoolValue{
+		cmd: 0x0000000a,
+	}
+	req.data = uintptr(unsafe.Pointer(&ethGlink))
+
+	_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
+	if errno != 0 {
+		return errors.Wrap(unix.Errno(errno), "Failed to ETHTOOL_GLINK")
+	}
+
+	info.LinkDetected = ethGlink.data == 1
+
+	// Interface info
+	err = ethtoolGset(ethtoolFd, &req, info)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

From ca655d798a128b623de650ffa655f443ab8e84c8 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 7 May 2020 16:36:02 +0200
Subject: [PATCH 2/2] resources/ethtool: implement ETHTOOL_GLINKSETTINGS

Closes: #7307.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/resources/network_ethtool.go | 131 ++++++++++++++++++++++++++++++-
 lxd/resources/utils.go           |   4 +
 2 files changed, 132 insertions(+), 3 deletions(-)

diff --git a/lxd/resources/network_ethtool.go b/lxd/resources/network_ethtool.go
index c910045f06..35f9738a26 100644
--- a/lxd/resources/network_ethtool.go
+++ b/lxd/resources/network_ethtool.go
@@ -117,6 +117,34 @@ type ethtoolValue struct {
 	data uint32
 }
 
+const EthtoolLinkModeMaskMaxKernelNu32 = 127 // SCHAR_MAX
+type ethtoolLinkSettings struct {
+	cmd                 uint32
+	speed               uint32
+	duplex              uint8
+	port                uint8
+	phyAddress          uint8
+	autoneg             uint8
+	mdioSupport         uint8
+	ethTpMdix           uint8
+	ethTpMdixCtrl       uint8
+	linkModeMasksNwords int8
+	transceiver         uint8
+	reserved1           [3]uint8
+	reserved            [7]uint32
+	linkModeMasks       [0]uint32
+	linkModeData        [3 * EthtoolLinkModeMaskMaxKernelNu32]uint32
+	// __u32 map_supported[link_mode_masks_nwords];
+	// __u32 map_advertising[link_mode_masks_nwords];
+	// __u32 map_lp_advertising[link_mode_masks_nwords];
+}
+
+type ethtoolLinkModeMaps struct {
+	mapSupported     []uint32
+	mapAdvertising   []uint32
+	mapLpAdvertising []uint32
+}
+
 func ethtoolAddCardInfo(name string, info *api.ResourcesNetworkCard) error {
 	// Open FD
 	ethtoolFd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
@@ -151,7 +179,7 @@ func ethtoolGset(ethtoolFd int, req *ethtoolReq, info *api.ResourcesNetworkCardP
 	}
 	req.data = uintptr(unsafe.Pointer(&ethCmd))
 
-	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(&req)))
+	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(req)))
 	if errno != 0 {
 		return errors.Wrap(unix.Errno(errno), "Failed to ETHTOOL_GSET")
 	}
@@ -219,6 +247,103 @@ func ethtoolGset(ethtoolFd int, req *ethtoolReq, info *api.ResourcesNetworkCardP
 	return nil
 }
 
+func ethtoolLink(ethtoolFd int, req *ethtoolReq, info *api.ResourcesNetworkCardPort) error {
+	// Interface info
+	ethLinkSettings := ethtoolLinkSettings{
+		cmd: 0x0000004c,
+	}
+	req.data = uintptr(unsafe.Pointer(&ethLinkSettings))
+
+	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(req)))
+	if errno != 0 {
+		return errors.Wrap(unix.Errno(errno), "Failed to ETHTOOL_GLINKSETTINGS")
+	}
+
+	if ethLinkSettings.linkModeMasksNwords >= 0 || ethLinkSettings.cmd != 0x0000004c {
+		return errors.Wrap(unix.Errno(unix.EINVAL), "Failed to ETHTOOL_GLINKSETTINGS")
+	}
+
+	/* got the real ecmd.req.link_mode_masks_nwords,
+	 * now send the real request
+	 */
+	ethLinkSettings.cmd = 0x0000004c
+	ethLinkSettings.linkModeMasksNwords = -ethLinkSettings.linkModeMasksNwords
+	_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(ethtoolFd), unix.SIOCETHTOOL, uintptr(unsafe.Pointer(req)))
+	if errno != 0 {
+		return errors.Wrap(unix.Errno(errno), "Failed to ETHTOOL_GLINKSETTINGS")
+	}
+
+	if ethLinkSettings.linkModeMasksNwords <= 0 || ethLinkSettings.cmd != 0x0000004c {
+		return errors.Wrap(unix.Errno(unix.EINVAL), "Failed to ETHTOOL_GLINKSETTINGS")
+	}
+
+	ethLinkModeMap := ethtoolLinkModeMaps{}
+	ethLinkModeMap.mapSupported = append(ethLinkModeMap.mapSupported, ethLinkSettings.linkModeData[:4*ethLinkSettings.linkModeMasksNwords]...)
+	offset := ethLinkSettings.linkModeMasksNwords
+	ethLinkModeMap.mapAdvertising = append(ethLinkModeMap.mapAdvertising, ethLinkSettings.linkModeData[offset:4*ethLinkSettings.linkModeMasksNwords]...)
+	offset += ethLinkSettings.linkModeMasksNwords
+	ethLinkModeMap.mapLpAdvertising = append(ethLinkModeMap.mapLpAdvertising, ethLinkSettings.linkModeData[offset:4*ethLinkSettings.linkModeMasksNwords]...)
+
+	// Link negotiation
+	info.AutoNegotiation = ethLinkSettings.autoneg == 1
+
+	if info.LinkDetected {
+		// Link duplex
+		if ethLinkSettings.duplex == 0x00 {
+			info.LinkDuplex = "half"
+		} else if ethLinkSettings.duplex == 0x01 {
+			info.LinkDuplex = "full"
+		}
+
+		// Link speed
+		info.LinkSpeed = uint64(ethLinkSettings.speed)
+	}
+
+	// Transceiver
+	if ethLinkSettings.transceiver == 0x00 {
+		info.TransceiverType = "internal"
+	} else if ethLinkSettings.transceiver == 0x01 {
+		info.TransceiverType = "external"
+	}
+
+	// Port
+	if ethLinkSettings.port == 0x00 {
+		info.PortType = "twisted pair"
+	} else if ethLinkSettings.port == 0x01 {
+		info.PortType = "AUI"
+	} else if ethLinkSettings.port == 0x02 {
+		info.PortType = "media-independent"
+	} else if ethLinkSettings.port == 0x03 {
+		info.PortType = "fibre"
+	} else if ethLinkSettings.port == 0x04 {
+		info.PortType = "BNC"
+	} else if ethLinkSettings.port == 0x05 {
+		info.PortType = "direct attach"
+	} else if ethLinkSettings.port == 0xef {
+		info.PortType = "none"
+	} else if ethLinkSettings.port == 0xff {
+		info.PortType = "other"
+	}
+
+	// Supported modes
+	info.SupportedModes = []string{}
+	for _, mode := range ethtoolModes {
+		if hasBitField(ethLinkModeMap.mapSupported, mode.bit) {
+			info.SupportedModes = append(info.SupportedModes, mode.name)
+		}
+	}
+
+	// Supported ports
+	info.SupportedPorts = []string{}
+	for _, port := range ethtoolPorts {
+		if hasBitField(ethLinkModeMap.mapSupported, port.bit) {
+			info.SupportedPorts = append(info.SupportedPorts, port.name)
+		}
+	}
+
+	return nil
+}
+
 func ethtoolAddPortInfo(info *api.ResourcesNetworkCardPort) error {
 	// Open FD
 	ethtoolFd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
@@ -258,9 +383,9 @@ func ethtoolAddPortInfo(info *api.ResourcesNetworkCardPort) error {
 	info.LinkDetected = ethGlink.data == 1
 
 	// Interface info
-	err = ethtoolGset(ethtoolFd, &req, info)
+	err = ethtoolLink(ethtoolFd, &req, info)
 	if err != nil {
-		return err
+		return ethtoolGset(ethtoolFd, &req, info)
 	}
 
 	return nil
diff --git a/lxd/resources/utils.go b/lxd/resources/utils.go
index 49019e185e..8290b5c6ab 100644
--- a/lxd/resources/utils.go
+++ b/lxd/resources/utils.go
@@ -88,3 +88,7 @@ func hasBit(n uint32, pos uint) bool {
 	val := n & (1 << pos)
 	return (val > 0)
 }
+
+func hasBitField(n []uint32, bit uint) bool {
+	return (n[bit/32] & (1 << (bit % 32))) != 0
+}


More information about the lxc-devel mailing list