github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/devlink_linux.go (about)

     1  package netlink
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  	"syscall"
     8  
     9  	"github.com/sagernet/netlink/nl"
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  // DevlinkDevEswitchAttr represents device's eswitch attributes
    14  type DevlinkDevEswitchAttr struct {
    15  	Mode       string
    16  	InlineMode string
    17  	EncapMode  string
    18  }
    19  
    20  // DevlinkDevAttrs represents device attributes
    21  type DevlinkDevAttrs struct {
    22  	Eswitch DevlinkDevEswitchAttr
    23  }
    24  
    25  // DevlinkDevice represents device and its attributes
    26  type DevlinkDevice struct {
    27  	BusName    string
    28  	DeviceName string
    29  	Attrs      DevlinkDevAttrs
    30  }
    31  
    32  // DevlinkPortFn represents port function and its attributes
    33  type DevlinkPortFn struct {
    34  	HwAddr  net.HardwareAddr
    35  	State   uint8
    36  	OpState uint8
    37  }
    38  
    39  // DevlinkPortFnSetAttrs represents attributes to set
    40  type DevlinkPortFnSetAttrs struct {
    41  	FnAttrs     DevlinkPortFn
    42  	HwAddrValid bool
    43  	StateValid  bool
    44  }
    45  
    46  // DevlinkPort represents port and its attributes
    47  type DevlinkPort struct {
    48  	BusName        string
    49  	DeviceName     string
    50  	PortIndex      uint32
    51  	PortType       uint16
    52  	NetdeviceName  string
    53  	NetdevIfIndex  uint32
    54  	RdmaDeviceName string
    55  	PortFlavour    uint16
    56  	Fn             *DevlinkPortFn
    57  }
    58  
    59  type DevLinkPortAddAttrs struct {
    60  	Controller      uint32
    61  	SfNumber        uint32
    62  	PortIndex       uint32
    63  	PfNumber        uint16
    64  	SfNumberValid   bool
    65  	PortIndexValid  bool
    66  	ControllerValid bool
    67  }
    68  
    69  // DevlinkDeviceInfo represents devlink info
    70  type DevlinkDeviceInfo struct {
    71  	Driver         string
    72  	SerialNumber   string
    73  	BoardID        string
    74  	FwApp          string
    75  	FwAppBoundleID string
    76  	FwAppName      string
    77  	FwBoundleID    string
    78  	FwMgmt         string
    79  	FwMgmtAPI      string
    80  	FwMgmtBuild    string
    81  	FwNetlist      string
    82  	FwNetlistBuild string
    83  	FwPsidAPI      string
    84  	FwUndi         string
    85  }
    86  
    87  func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
    88  	devices := make([]*DevlinkDevice, 0, len(msgs))
    89  	for _, m := range msgs {
    90  		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		dev := &DevlinkDevice{}
    95  		if err = dev.parseAttributes(attrs); err != nil {
    96  			return nil, err
    97  		}
    98  		devices = append(devices, dev)
    99  	}
   100  	return devices, nil
   101  }
   102  
   103  func eswitchStringToMode(modeName string) (uint16, error) {
   104  	if modeName == "legacy" {
   105  		return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil
   106  	} else if modeName == "switchdev" {
   107  		return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil
   108  	} else {
   109  		return 0xffff, fmt.Errorf("invalid switchdev mode")
   110  	}
   111  }
   112  
   113  func parseEswitchMode(mode uint16) string {
   114  	var eswitchMode = map[uint16]string{
   115  		nl.DEVLINK_ESWITCH_MODE_LEGACY:    "legacy",
   116  		nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev",
   117  	}
   118  	if eswitchMode[mode] == "" {
   119  		return "unknown"
   120  	} else {
   121  		return eswitchMode[mode]
   122  	}
   123  }
   124  
   125  func parseEswitchInlineMode(inlinemode uint8) string {
   126  	var eswitchInlineMode = map[uint8]string{
   127  		nl.DEVLINK_ESWITCH_INLINE_MODE_NONE:      "none",
   128  		nl.DEVLINK_ESWITCH_INLINE_MODE_LINK:      "link",
   129  		nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK:   "network",
   130  		nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport",
   131  	}
   132  	if eswitchInlineMode[inlinemode] == "" {
   133  		return "unknown"
   134  	} else {
   135  		return eswitchInlineMode[inlinemode]
   136  	}
   137  }
   138  
   139  func parseEswitchEncapMode(encapmode uint8) string {
   140  	var eswitchEncapMode = map[uint8]string{
   141  		nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE:  "disable",
   142  		nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable",
   143  	}
   144  	if eswitchEncapMode[encapmode] == "" {
   145  		return "unknown"
   146  	} else {
   147  		return eswitchEncapMode[encapmode]
   148  	}
   149  }
   150  
   151  func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
   152  	for _, a := range attrs {
   153  		switch a.Attr.Type {
   154  		case nl.DEVLINK_ATTR_BUS_NAME:
   155  			d.BusName = string(a.Value[:len(a.Value)-1])
   156  		case nl.DEVLINK_ATTR_DEV_NAME:
   157  			d.DeviceName = string(a.Value[:len(a.Value)-1])
   158  		case nl.DEVLINK_ATTR_ESWITCH_MODE:
   159  			d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
   160  		case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
   161  			d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0]))
   162  		case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE:
   163  			d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0]))
   164  		}
   165  	}
   166  	return nil
   167  }
   168  
   169  func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) {
   170  	m := msgs[0]
   171  	attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   172  	if err != nil {
   173  		return
   174  	}
   175  	dev.parseAttributes(attrs)
   176  }
   177  
   178  func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
   179  	msg := &nl.Genlmsg{
   180  		Command: nl.DEVLINK_CMD_ESWITCH_GET,
   181  		Version: nl.GENL_DEVLINK_VERSION,
   182  	}
   183  	req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
   184  	req.AddData(msg)
   185  
   186  	b := make([]byte, len(dev.BusName)+1)
   187  	copy(b, dev.BusName)
   188  	data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
   189  	req.AddData(data)
   190  
   191  	b = make([]byte, len(dev.DeviceName)+1)
   192  	copy(b, dev.DeviceName)
   193  	data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
   194  	req.AddData(data)
   195  
   196  	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
   197  	if err != nil {
   198  		return
   199  	}
   200  	dev.parseEswitchAttrs(msgs)
   201  }
   202  
   203  // DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
   204  // otherwise returns an error code.
   205  func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
   206  	f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	msg := &nl.Genlmsg{
   211  		Command: nl.DEVLINK_CMD_GET,
   212  		Version: nl.GENL_DEVLINK_VERSION,
   213  	}
   214  	req := h.newNetlinkRequest(int(f.ID),
   215  		unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
   216  	req.AddData(msg)
   217  	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	devices, err := parseDevLinkDeviceList(msgs)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	for _, d := range devices {
   226  		h.getEswitchAttrs(f, d)
   227  	}
   228  	return devices, nil
   229  }
   230  
   231  // DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
   232  // otherwise returns an error code.
   233  func DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
   234  	return pkgHandle.DevLinkGetDeviceList()
   235  }
   236  
   237  func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) {
   238  	m := msgs[0]
   239  	attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  	dev := &DevlinkDevice{}
   244  	if err = dev.parseAttributes(attrs); err != nil {
   245  		return nil, err
   246  	}
   247  	return dev, nil
   248  }
   249  
   250  func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) {
   251  	f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
   252  	if err != nil {
   253  		return nil, nil, err
   254  	}
   255  
   256  	msg := &nl.Genlmsg{
   257  		Command: cmd,
   258  		Version: nl.GENL_DEVLINK_VERSION,
   259  	}
   260  	req := h.newNetlinkRequest(int(f.ID),
   261  		unix.NLM_F_REQUEST|unix.NLM_F_ACK)
   262  	req.AddData(msg)
   263  
   264  	b := make([]byte, len(bus)+1)
   265  	copy(b, bus)
   266  	data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
   267  	req.AddData(data)
   268  
   269  	b = make([]byte, len(device)+1)
   270  	copy(b, device)
   271  	data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
   272  	req.AddData(data)
   273  
   274  	return f, req, nil
   275  }
   276  
   277  // DevlinkGetDeviceByName provides a pointer to devlink device and nil error,
   278  // otherwise returns an error code.
   279  func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) {
   280  	f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	dev, err := parseDevlinkDevice(respmsg)
   290  	if err == nil {
   291  		h.getEswitchAttrs(f, dev)
   292  	}
   293  	return dev, err
   294  }
   295  
   296  // DevlinkGetDeviceByName provides a pointer to devlink device and nil error,
   297  // otherwise returns an error code.
   298  func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) {
   299  	return pkgHandle.DevLinkGetDeviceByName(Bus, Device)
   300  }
   301  
   302  // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or
   303  // returns an error code.
   304  // Equivalent to: `devlink dev eswitch set $dev mode switchdev`
   305  // Equivalent to: `devlink dev eswitch set $dev mode legacy`
   306  func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
   307  	mode, err := eswitchStringToMode(NewMode)
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName)
   313  	if err != nil {
   314  		return err
   315  	}
   316  
   317  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode)))
   318  
   319  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   320  	return err
   321  }
   322  
   323  // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or
   324  // returns an error code.
   325  // Equivalent to: `devlink dev eswitch set $dev mode switchdev`
   326  // Equivalent to: `devlink dev eswitch set $dev mode legacy`
   327  func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
   328  	return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode)
   329  }
   330  
   331  func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
   332  	for _, a := range attrs {
   333  		switch a.Attr.Type {
   334  		case nl.DEVLINK_ATTR_BUS_NAME:
   335  			port.BusName = string(a.Value[:len(a.Value)-1])
   336  		case nl.DEVLINK_ATTR_DEV_NAME:
   337  			port.DeviceName = string(a.Value[:len(a.Value)-1])
   338  		case nl.DEVLINK_ATTR_PORT_INDEX:
   339  			port.PortIndex = native.Uint32(a.Value)
   340  		case nl.DEVLINK_ATTR_PORT_TYPE:
   341  			port.PortType = native.Uint16(a.Value)
   342  		case nl.DEVLINK_ATTR_PORT_NETDEV_NAME:
   343  			port.NetdeviceName = string(a.Value[:len(a.Value)-1])
   344  		case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX:
   345  			port.NetdevIfIndex = native.Uint32(a.Value)
   346  		case nl.DEVLINK_ATTR_PORT_IBDEV_NAME:
   347  			port.RdmaDeviceName = string(a.Value[:len(a.Value)-1])
   348  		case nl.DEVLINK_ATTR_PORT_FLAVOUR:
   349  			port.PortFlavour = native.Uint16(a.Value)
   350  		case nl.DEVLINK_ATTR_PORT_FUNCTION:
   351  			port.Fn = &DevlinkPortFn{}
   352  			for nested := range nl.ParseAttributes(a.Value) {
   353  				switch nested.Type {
   354  				case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR:
   355  					port.Fn.HwAddr = nested.Value[:]
   356  				case nl.DEVLINK_PORT_FN_ATTR_STATE:
   357  					port.Fn.State = uint8(nested.Value[0])
   358  				case nl.DEVLINK_PORT_FN_ATTR_OPSTATE:
   359  					port.Fn.OpState = uint8(nested.Value[0])
   360  				}
   361  			}
   362  		}
   363  	}
   364  	return nil
   365  }
   366  
   367  func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) {
   368  	ports := make([]*DevlinkPort, 0, len(msgs))
   369  	for _, m := range msgs {
   370  		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   371  		if err != nil {
   372  			return nil, err
   373  		}
   374  		port := &DevlinkPort{}
   375  		if err = port.parseAttributes(attrs); err != nil {
   376  			return nil, err
   377  		}
   378  		ports = append(ports, port)
   379  	}
   380  	return ports, nil
   381  }
   382  
   383  // DevLinkGetPortList provides a pointer to devlink ports and nil error,
   384  // otherwise returns an error code.
   385  func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
   386  	f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  	msg := &nl.Genlmsg{
   391  		Command: nl.DEVLINK_CMD_PORT_GET,
   392  		Version: nl.GENL_DEVLINK_VERSION,
   393  	}
   394  	req := h.newNetlinkRequest(int(f.ID),
   395  		unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
   396  	req.AddData(msg)
   397  	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  	ports, err := parseDevLinkAllPortList(msgs)
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  	return ports, nil
   406  }
   407  
   408  // DevLinkGetPortList provides a pointer to devlink ports and nil error,
   409  // otherwise returns an error code.
   410  func DevLinkGetAllPortList() ([]*DevlinkPort, error) {
   411  	return pkgHandle.DevLinkGetAllPortList()
   412  }
   413  
   414  func parseDevlinkPortMsg(msgs [][]byte) (*DevlinkPort, error) {
   415  	m := msgs[0]
   416  	attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   417  	if err != nil {
   418  		return nil, err
   419  	}
   420  	port := &DevlinkPort{}
   421  	if err = port.parseAttributes(attrs); err != nil {
   422  		return nil, err
   423  	}
   424  	return port, nil
   425  }
   426  
   427  // DevLinkGetPortByIndexprovides a pointer to devlink device and nil error,
   428  // otherwise returns an error code.
   429  func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
   430  
   431  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_GET, Bus, Device)
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
   437  
   438  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   439  	if err != nil {
   440  		return nil, err
   441  	}
   442  	port, err := parseDevlinkPortMsg(respmsg)
   443  	return port, err
   444  }
   445  
   446  // DevLinkGetPortByIndex provides a pointer to devlink portand nil error,
   447  // otherwise returns an error code.
   448  func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
   449  	return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex)
   450  }
   451  
   452  // DevLinkPortAdd adds a devlink port and returns a port on success
   453  // otherwise returns nil port and an error code.
   454  func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
   455  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device)
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  
   460  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour)))
   461  
   462  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber)))
   463  	if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid {
   464  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber)))
   465  	}
   466  	if Attrs.PortIndexValid {
   467  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex)))
   468  	}
   469  	if Attrs.ControllerValid {
   470  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller)))
   471  	}
   472  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   473  	if err != nil {
   474  		return nil, err
   475  	}
   476  	port, err := parseDevlinkPortMsg(respmsg)
   477  	return port, err
   478  }
   479  
   480  // DevLinkPortAdd adds a devlink port and returns a port on success
   481  // otherwise returns nil port and an error code.
   482  func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
   483  	return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs)
   484  }
   485  
   486  // DevLinkPortDel deletes a devlink port and returns success or error code.
   487  func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
   488  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device)
   489  	if err != nil {
   490  		return err
   491  	}
   492  
   493  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
   494  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   495  	return err
   496  }
   497  
   498  // DevLinkPortDel deletes a devlink port and returns success or error code.
   499  func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
   500  	return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex)
   501  }
   502  
   503  // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
   504  // It returns 0 on success or error code.
   505  func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
   506  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device)
   507  	if err != nil {
   508  		return err
   509  	}
   510  
   511  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
   512  
   513  	fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil)
   514  
   515  	if FnAttrs.HwAddrValid {
   516  		fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr))
   517  	}
   518  
   519  	if FnAttrs.StateValid {
   520  		fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State))
   521  	}
   522  	req.AddData(fnAttr)
   523  
   524  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   525  	return err
   526  }
   527  
   528  // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
   529  // It returns 0 on success or error code.
   530  func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
   531  	return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs)
   532  }
   533  
   534  // devlinkInfoGetter is function that is responsible for getting devlink info message
   535  // this is introduced for test purpose
   536  type devlinkInfoGetter func(bus, device string) ([]byte, error)
   537  
   538  // DevlinkGetDeviceInfoByName returns devlink info for selected device,
   539  // otherwise returns an error code.
   540  // Equivalent to: `devlink dev info $dev`
   541  func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) {
   542  	info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg)
   543  	if err != nil {
   544  		return nil, err
   545  	}
   546  
   547  	return parseInfoData(info), nil
   548  }
   549  
   550  // DevlinkGetDeviceInfoByName returns devlink info for selected device,
   551  // otherwise returns an error code.
   552  // Equivalent to: `devlink dev info $dev`
   553  func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) {
   554  	return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg)
   555  }
   556  
   557  // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
   558  // otherwise returns an error code.
   559  // Equivalent to: `devlink dev info $dev`
   560  func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) {
   561  	response, err := getInfoMsg(Bus, Device)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  
   566  	info, err := parseInfoMsg(response)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  
   571  	return info, nil
   572  }
   573  
   574  // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
   575  // otherwise returns an error code.
   576  // Equivalent to: `devlink dev info $dev`
   577  func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) {
   578  	return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg)
   579  }
   580  
   581  // GetDevlinkInfo returns devlink info for target device,
   582  // otherwise returns an error code.
   583  func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) {
   584  	return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
   585  }
   586  
   587  // GetDevlinkInfoAsMap returns devlink info for target device as a map,
   588  // otherwise returns an error code.
   589  func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) {
   590  	return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
   591  }
   592  
   593  func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) {
   594  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device)
   595  	if err != nil {
   596  		return nil, err
   597  	}
   598  
   599  	response, err := req.Execute(unix.NETLINK_GENERIC, 0)
   600  	if err != nil {
   601  		return nil, err
   602  	}
   603  
   604  	if len(response) < 1 {
   605  		return nil, fmt.Errorf("getDevlinkInfoMsg: message too short")
   606  	}
   607  
   608  	return response[0], nil
   609  }
   610  
   611  func parseInfoMsg(msg []byte) (map[string]string, error) {
   612  	if len(msg) < nl.SizeofGenlmsg {
   613  		return nil, fmt.Errorf("parseInfoMsg: message too short")
   614  	}
   615  
   616  	info := make(map[string]string)
   617  	err := collectInfoData(msg[nl.SizeofGenlmsg:], info)
   618  
   619  	if err != nil {
   620  		return nil, err
   621  	}
   622  
   623  	return info, nil
   624  }
   625  
   626  func collectInfoData(msg []byte, data map[string]string) error {
   627  	attrs, err := nl.ParseRouteAttr(msg)
   628  	if err != nil {
   629  		return err
   630  	}
   631  
   632  	for _, attr := range attrs {
   633  		switch attr.Attr.Type {
   634  		case nl.DEVLINK_ATTR_INFO_DRIVER_NAME:
   635  			data["driver"] = parseInfoValue(attr.Value)
   636  		case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER:
   637  			data["serialNumber"] = parseInfoValue(attr.Value)
   638  		case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED,
   639  			nl.DEVLINK_ATTR_INFO_VERSION_STORED:
   640  			key, value, err := getNestedInfoData(attr.Value)
   641  			if err != nil {
   642  				return err
   643  			}
   644  			data[key] = value
   645  		}
   646  	}
   647  
   648  	if len(data) == 0 {
   649  		return fmt.Errorf("collectInfoData: could not read attributes")
   650  	}
   651  
   652  	return nil
   653  }
   654  
   655  func getNestedInfoData(msg []byte) (string, string, error) {
   656  	nestedAttrs, err := nl.ParseRouteAttr(msg)
   657  
   658  	var key, value string
   659  
   660  	if err != nil {
   661  		return "", "", err
   662  	}
   663  
   664  	if len(nestedAttrs) != 2 {
   665  		return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure")
   666  	}
   667  
   668  	for _, nestedAttr := range nestedAttrs {
   669  		switch nestedAttr.Attr.Type {
   670  		case nl.DEVLINK_ATTR_INFO_VERSION_NAME:
   671  			key = parseInfoValue(nestedAttr.Value)
   672  		case nl.DEVLINK_ATTR_INFO_VERSION_VALUE:
   673  			value = parseInfoValue(nestedAttr.Value)
   674  		}
   675  	}
   676  
   677  	if key == "" {
   678  		return "", "", fmt.Errorf("getNestedInfoData: key not found")
   679  	}
   680  
   681  	if value == "" {
   682  		return "", "", fmt.Errorf("getNestedInfoData: value not found")
   683  	}
   684  
   685  	return key, value, nil
   686  }
   687  
   688  func parseInfoData(data map[string]string) *DevlinkDeviceInfo {
   689  	info := new(DevlinkDeviceInfo)
   690  	for key, value := range data {
   691  		switch key {
   692  		case "driver":
   693  			info.Driver = value
   694  		case "serialNumber":
   695  			info.SerialNumber = value
   696  		case "board.id":
   697  			info.BoardID = value
   698  		case "fw.app":
   699  			info.FwApp = value
   700  		case "fw.app.bundle_id":
   701  			info.FwAppBoundleID = value
   702  		case "fw.app.name":
   703  			info.FwAppName = value
   704  		case "fw.bundle_id":
   705  			info.FwBoundleID = value
   706  		case "fw.mgmt":
   707  			info.FwMgmt = value
   708  		case "fw.mgmt.api":
   709  			info.FwMgmtAPI = value
   710  		case "fw.mgmt.build":
   711  			info.FwMgmtBuild = value
   712  		case "fw.netlist":
   713  			info.FwNetlist = value
   714  		case "fw.netlist.build":
   715  			info.FwNetlistBuild = value
   716  		case "fw.psid.api":
   717  			info.FwPsidAPI = value
   718  		case "fw.undi":
   719  			info.FwUndi = value
   720  		}
   721  	}
   722  	return info
   723  }
   724  
   725  func parseInfoValue(value []byte) string {
   726  	v := strings.ReplaceAll(string(value), "\x00", "")
   727  	return strings.TrimSpace(v)
   728  }