diff --git a/devlink_linux.go b/devlink_linux.go index c5a2dac..19a0ca7 100644 --- a/devlink_linux.go +++ b/devlink_linux.go @@ -46,15 +46,21 @@ type DevlinkPortFnSetAttrs struct { // DevlinkPort represents port and its attributes type DevlinkPort struct { - BusName string - DeviceName string - PortIndex uint32 - PortType uint16 - NetdeviceName string - NetdevIfIndex uint32 - RdmaDeviceName string - PortFlavour uint16 - Fn *DevlinkPortFn + BusName string + DeviceName string + PortIndex uint32 + PortType uint16 + NetdeviceName string + NetdevIfIndex uint32 + RdmaDeviceName string + PortFlavour uint16 + Fn *DevlinkPortFn + PortNumber *uint32 + PfNumber *uint16 + VfNumber *uint16 + SfNumber *uint32 + ControllerNumber *uint32 + External *bool } type DevLinkPortAddAttrs struct { @@ -629,6 +635,24 @@ func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error port.Fn.OpState = uint8(nested.Value[0]) } } + case nl.DEVLINK_ATTR_PORT_NUMBER: + val := native.Uint32(a.Value) + port.PortNumber = &val + case nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER: + val := native.Uint16(a.Value) + port.PfNumber = &val + case nl.DEVLINK_ATTR_PORT_PCI_VF_NUMBER: + val := native.Uint16(a.Value) + port.VfNumber = &val + case nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER: + val := native.Uint32(a.Value) + port.SfNumber = &val + case nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER: + val := native.Uint32(a.Value) + port.ControllerNumber = &val + case nl.DEVLINK_ATTR_PORT_EXTERNAL: + val := uint8(a.Value[0]) != 0 + port.External = &val } } return nil diff --git a/devlink_test.go b/devlink_test.go index 4f44b49..25c7f80 100644 --- a/devlink_test.go +++ b/devlink_test.go @@ -5,12 +5,17 @@ package netlink import ( "flag" + "fmt" "math/rand" "net" "os" "strconv" + "strings" + "syscall" "testing" + "github.com/stretchr/testify/assert" + "github.com/vishvananda/netlink/nl" ) @@ -49,6 +54,61 @@ func TestDevLinkSetEswitchMode(t *testing.T) { } } +func logPort(t *testing.T, port *DevlinkPort) { + type field struct { + key string + value string + } + + fields := []field{} + + fields = append(fields, field{key: "bus", value: port.BusName}) + fields = append(fields, field{key: "device", value: port.DeviceName}) + fields = append(fields, field{key: "port_index", value: strconv.Itoa(int(port.PortIndex))}) + fields = append(fields, field{key: "port_type", value: strconv.Itoa(int(port.PortType))}) + fields = append(fields, field{key: "port_flavour", value: strconv.Itoa(int(port.PortFlavour))}) + fields = append(fields, field{key: "netdev_name", value: port.NetdeviceName}) + fields = append(fields, field{key: "netdev_index", value: strconv.Itoa(int(port.NetdevIfIndex))}) + fields = append(fields, field{key: "rdma_dev_name", value: port.RdmaDeviceName}) + + if port.Fn != nil { + fields = append(fields, field{key: "hw_addr", value: port.Fn.HwAddr.String()}) + fields = append(fields, field{key: "state", value: strconv.Itoa(int(port.Fn.State))}) + fields = append(fields, field{key: "op_state", value: strconv.Itoa(int(port.Fn.OpState))}) + } + + if port.PortNumber != nil { + fields = append(fields, field{key: "port_number", value: strconv.Itoa(int(*port.PortNumber))}) + } + + if port.PfNumber != nil { + fields = append(fields, field{key: "pf_number", value: strconv.Itoa(int(*port.PfNumber))}) + } + + if port.VfNumber != nil { + fields = append(fields, field{key: "vf_number", value: strconv.Itoa(int(*port.VfNumber))}) + } + + if port.SfNumber != nil { + fields = append(fields, field{key: "sf_number", value: strconv.Itoa(int(*port.SfNumber))}) + } + + if port.ControllerNumber != nil { + fields = append(fields, field{key: "controller_number", value: strconv.Itoa(int(*port.ControllerNumber))}) + } + + if port.External != nil { + fields = append(fields, field{key: "external", value: strconv.FormatBool(*port.External)}) + } + + fieldsStr := []string{} + for _, field := range fields { + fieldsStr = append(fieldsStr, fmt.Sprintf("%s=%s", field.key, field.value)) + } + + t.Log(strings.Join(fieldsStr, " ")) +} + func TestDevLinkGetAllPortList(t *testing.T) { minKernelRequired(t, 5, 4) ports, err := DevLinkGetAllPortList() @@ -57,7 +117,7 @@ func TestDevLinkGetAllPortList(t *testing.T) { } t.Log("devlink port count = ", len(ports)) for _, port := range ports { - t.Log(*port) + logPort(t, port) } } @@ -401,3 +461,211 @@ func validateDeviceParams(t *testing.T, p *DevlinkParam) { } } } + +func testGetDevlinkPortCommonAttrs() []*nl.RtAttr { + nlAttrs := []*nl.RtAttr{} + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, nl.ZeroTerminated("pci")), + nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, nl.ZeroTerminated("0000:08:00.0")), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(131071)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_TYPE, nl.Uint16Attr(nl.DEVLINK_PORT_TYPE_ETH)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NETDEV_NAME, nl.ZeroTerminated("eth0")), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX, nl.Uint32Attr(5)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_IBDEV_NAME, nl.ZeroTerminated("rdma0")), + ) + + return nlAttrs +} + +func testAddDevlinkPortPhysicalAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NUMBER, nl.Uint32Attr(1)), + ) + + return nlAttrs +} + +func testAddDevlinkPortPfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_PF)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(1)), + ) + + return nlAttrs +} + +func testAddDevlinkPortVfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_VF)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(0)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_VF_NUMBER, nl.Uint16Attr(4)), + ) + + nlAttrs = testAddDevlinkPortFnAttrs(nlAttrs) + return nlAttrs +} + +func testAddDevlinkPortSfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_SF)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(0)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(123)), + ) + + nlAttrs = testAddDevlinkPortFnAttrs(nlAttrs) + return nlAttrs +} + +func testNlAttrsToNetlinkRouteAttrs(nlAttrs []*nl.RtAttr) []syscall.NetlinkRouteAttr { + attrs := []syscall.NetlinkRouteAttr{} + for _, attr := range nlAttrs { + attrs = append(attrs, syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(attr.RtAttr), Value: attr.Data}) + } + return attrs +} + +func testAddDevlinkPortFnAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + hwAddr, _ := net.ParseMAC("00:11:22:33:44:55") + hwAddrAttr := nl.NewRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(hwAddr)) + raw := hwAddrAttr.Serialize() + + nlAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION, raw) + return append(nlAttrs, nlAttr) +} + +func testAddDevlinkPortControllerAttrs(nlAttrs []*nl.RtAttr, controllerNumber uint32, external bool) []*nl.RtAttr { + extVal := uint8(0) + if external { + extVal = 1 + } + + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(controllerNumber)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_EXTERNAL, nl.Uint8Attr(extVal)), + ) + + return nlAttrs +} +func testAssertCommonAttrs(t *testing.T, port *DevlinkPort) { + assert.Equal(t, "pci", port.BusName) + assert.Equal(t, "0000:08:00.0", port.DeviceName) + assert.Equal(t, uint32(131071), port.PortIndex) + assert.Equal(t, uint16(nl.DEVLINK_PORT_TYPE_ETH), port.PortType) + assert.Equal(t, "eth0", port.NetdeviceName) + assert.Equal(t, uint32(5), port.NetdevIfIndex) + assert.Equal(t, "rdma0", port.RdmaDeviceName) +} + +func TestDevlinkPortParseAttributes(t *testing.T) { + t.Run("flavor physical", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortPhysicalAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + assert.NoError(t, err) + + testAssertCommonAttrs(t, port) + assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL), port.PortFlavour) + assert.Equal(t, uint32(1), *port.PortNumber) + + assert.Nil(t, port.Fn) + assert.Nil(t, port.PfNumber) + assert.Nil(t, port.VfNumber) + assert.Nil(t, port.SfNumber) + assert.Nil(t, port.ControllerNumber) + assert.Nil(t, port.External) + }) + + t.Run("flavor pcipf", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortPfAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + assert.NoError(t, err) + + testAssertCommonAttrs(t, port) + assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_PF), port.PortFlavour) + assert.Equal(t, uint16(1), *port.PfNumber) + + assert.Nil(t, port.Fn) + assert.Nil(t, port.PortNumber) + assert.Nil(t, port.VfNumber) + assert.Nil(t, port.SfNumber) + assert.Nil(t, port.ControllerNumber) + assert.Nil(t, port.External) + }) + t.Run("flavor pcivf", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + assert.NoError(t, err) + + testAssertCommonAttrs(t, port) + assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_VF), port.PortFlavour) + assert.Equal(t, uint16(0), *port.PfNumber) + assert.Equal(t, uint16(4), *port.VfNumber) + assert.Equal(t, "00:11:22:33:44:55", port.Fn.HwAddr.String()) + + assert.Nil(t, port.PortNumber) + assert.Nil(t, port.SfNumber) + assert.Nil(t, port.ControllerNumber) + assert.Nil(t, port.External) + }) + + t.Run("flavor pcisf", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortSfAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + assert.NoError(t, err) + + testAssertCommonAttrs(t, port) + assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_SF), port.PortFlavour) + assert.Equal(t, uint16(0), *port.PfNumber) + assert.Equal(t, uint32(123), *port.SfNumber) + assert.Equal(t, "00:11:22:33:44:55", port.Fn.HwAddr.String()) + + assert.Nil(t, port.PortNumber) + assert.Nil(t, port.VfNumber) + assert.Nil(t, port.ControllerNumber) + assert.Nil(t, port.External) + }) + + t.Run("port with controller - external false", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) + nlAttrs = testAddDevlinkPortControllerAttrs(nlAttrs, 0, false) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + assert.NoError(t, err) + + assert.Equal(t, uint32(0), *port.ControllerNumber) + assert.Equal(t, false, *port.External) + }) + + t.Run("port with controller - external true", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) + nlAttrs = testAddDevlinkPortControllerAttrs(nlAttrs, 1, true) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + assert.NoError(t, err) + + assert.Equal(t, uint32(1), *port.ControllerNumber) + assert.Equal(t, true, *port.External) + }) +} diff --git a/go.mod b/go.mod index 17278ed..5da3c46 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,13 @@ module github.com/vishvananda/netlink go 1.23 require ( + github.com/stretchr/testify v1.10.0 github.com/vishvananda/netns v0.0.5 golang.org/x/sys v0.10.0 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 0c17e26..d859e4d 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,14 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/nl/devlink_linux.go b/nl/devlink_linux.go index eb98772..940dd70 100644 --- a/nl/devlink_linux.go +++ b/nl/devlink_linux.go @@ -50,7 +50,8 @@ const ( DEVLINK_ATTR_RESOURCE_OCC = 74 /* u64 */ DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID = 75 /* u64 */ DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS = 76 /* u64 */ - DEVLINK_ATTR_PORT_FLAVOUR = 77 + DEVLINK_ATTR_PORT_FLAVOUR = 77 /* u16 */ + DEVLINK_ATTR_PORT_NUMBER = 78 /* u32 */ DEVLINK_ATTR_INFO_DRIVER_NAME = 98 DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99 DEVLINK_ATTR_INFO_VERSION_FIXED = 100 @@ -58,10 +59,12 @@ const ( DEVLINK_ATTR_INFO_VERSION_STORED = 102 DEVLINK_ATTR_INFO_VERSION_NAME = 103 DEVLINK_ATTR_INFO_VERSION_VALUE = 104 - DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127 - DEVLINK_ATTR_PORT_FUNCTION = 145 - DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150 - DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164 + DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127 /* u16 */ + DEVLINK_ATTR_PORT_PCI_VF_NUMBER = 128 /* u16 */ + DEVLINK_ATTR_PORT_FUNCTION = 145 /* nested */ + DEVLINK_ATTR_PORT_EXTERNAL = 149 /* u8 */ + DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150 /* u32 */ + DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164 /* u32 */ ) const (