github.com/vishvananda/netlink@v1.3.1/devlink_linux.go (about)

     1  package netlink
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"strings"
     8  	"syscall"
     9  
    10  	"github.com/vishvananda/netlink/nl"
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  // DevlinkDevEswitchAttr represents device's eswitch attributes
    15  type DevlinkDevEswitchAttr struct {
    16  	Mode       string
    17  	InlineMode string
    18  	EncapMode  string
    19  }
    20  
    21  // DevlinkDevAttrs represents device attributes
    22  type DevlinkDevAttrs struct {
    23  	Eswitch DevlinkDevEswitchAttr
    24  }
    25  
    26  // DevlinkDevice represents device and its attributes
    27  type DevlinkDevice struct {
    28  	BusName    string
    29  	DeviceName string
    30  	Attrs      DevlinkDevAttrs
    31  }
    32  
    33  // DevlinkPortFn represents port function and its attributes
    34  type DevlinkPortFn struct {
    35  	HwAddr  net.HardwareAddr
    36  	State   uint8
    37  	OpState uint8
    38  }
    39  
    40  // DevlinkPortFnSetAttrs represents attributes to set
    41  type DevlinkPortFnSetAttrs struct {
    42  	FnAttrs     DevlinkPortFn
    43  	HwAddrValid bool
    44  	StateValid  bool
    45  }
    46  
    47  // DevlinkPort represents port and its attributes
    48  type DevlinkPort struct {
    49  	BusName        string
    50  	DeviceName     string
    51  	PortIndex      uint32
    52  	PortType       uint16
    53  	NetdeviceName  string
    54  	NetdevIfIndex  uint32
    55  	RdmaDeviceName string
    56  	PortFlavour    uint16
    57  	Fn             *DevlinkPortFn
    58  }
    59  
    60  type DevLinkPortAddAttrs struct {
    61  	Controller      uint32
    62  	SfNumber        uint32
    63  	PortIndex       uint32
    64  	PfNumber        uint16
    65  	SfNumberValid   bool
    66  	PortIndexValid  bool
    67  	ControllerValid bool
    68  }
    69  
    70  // DevlinkDeviceInfo represents devlink info
    71  type DevlinkDeviceInfo struct {
    72  	Driver         string
    73  	SerialNumber   string
    74  	BoardID        string
    75  	FwApp          string
    76  	FwAppBoundleID string
    77  	FwAppName      string
    78  	FwBoundleID    string
    79  	FwMgmt         string
    80  	FwMgmtAPI      string
    81  	FwMgmtBuild    string
    82  	FwNetlist      string
    83  	FwNetlistBuild string
    84  	FwPsidAPI      string
    85  	FwUndi         string
    86  }
    87  
    88  // DevlinkResource represents a device resource
    89  type DevlinkResource struct {
    90  	Name            string
    91  	ID              uint64
    92  	Size            uint64
    93  	SizeNew         uint64
    94  	SizeMin         uint64
    95  	SizeMax         uint64
    96  	SizeGranularity uint64
    97  	PendingChange   bool
    98  	Unit            uint8
    99  	SizeValid       bool
   100  	OCCValid        bool
   101  	OCCSize         uint64
   102  	Parent          *DevlinkResource
   103  	Children        []DevlinkResource
   104  }
   105  
   106  // parseAttributes parses provided Netlink Attributes and populates DevlinkResource, returns error if occured
   107  func (dlr *DevlinkResource) parseAttributes(attrs map[uint16]syscall.NetlinkRouteAttr) error {
   108  	var attr syscall.NetlinkRouteAttr
   109  	var ok bool
   110  
   111  	// mandatory attributes
   112  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_ID]
   113  	if !ok {
   114  		return fmt.Errorf("missing resource id")
   115  	}
   116  	dlr.ID = native.Uint64(attr.Value)
   117  
   118  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_NAME]
   119  	if !ok {
   120  		return fmt.Errorf("missing resource name")
   121  	}
   122  	dlr.Name = nl.BytesToString(attr.Value)
   123  
   124  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE]
   125  	if !ok {
   126  		return fmt.Errorf("missing resource size")
   127  	}
   128  	dlr.Size = native.Uint64(attr.Value)
   129  
   130  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_GRAN]
   131  	if !ok {
   132  		return fmt.Errorf("missing resource size granularity")
   133  	}
   134  	dlr.SizeGranularity = native.Uint64(attr.Value)
   135  
   136  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_UNIT]
   137  	if !ok {
   138  		return fmt.Errorf("missing resource unit")
   139  	}
   140  	dlr.Unit = uint8(attr.Value[0])
   141  
   142  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_MIN]
   143  	if !ok {
   144  		return fmt.Errorf("missing resource size min")
   145  	}
   146  	dlr.SizeMin = native.Uint64(attr.Value)
   147  
   148  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_MAX]
   149  	if !ok {
   150  		return fmt.Errorf("missing resource size max")
   151  	}
   152  	dlr.SizeMax = native.Uint64(attr.Value)
   153  
   154  	// optional attributes
   155  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_OCC]
   156  	if ok {
   157  		dlr.OCCSize = native.Uint64(attr.Value)
   158  		dlr.OCCValid = true
   159  	}
   160  
   161  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_VALID]
   162  	if ok {
   163  		dlr.SizeValid = uint8(attr.Value[0]) != 0
   164  	}
   165  
   166  	dlr.SizeNew = dlr.Size
   167  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_NEW]
   168  	if ok {
   169  		dlr.SizeNew = native.Uint64(attr.Value)
   170  	}
   171  
   172  	dlr.PendingChange = dlr.Size != dlr.SizeNew
   173  
   174  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_LIST]
   175  	if ok {
   176  		// handle nested resoruces recursively
   177  		subResources, err := nl.ParseRouteAttr(attr.Value)
   178  		if err != nil {
   179  			return err
   180  		}
   181  
   182  		for _, subresource := range subResources {
   183  			resource := DevlinkResource{Parent: dlr}
   184  			attrs, err := nl.ParseRouteAttrAsMap(subresource.Value)
   185  			if err != nil {
   186  				return err
   187  			}
   188  			err = resource.parseAttributes(attrs)
   189  			if err != nil {
   190  				return fmt.Errorf("failed to parse child resource, parent:%s. %w", dlr.Name, err)
   191  			}
   192  			dlr.Children = append(dlr.Children, resource)
   193  		}
   194  	}
   195  	return nil
   196  }
   197  
   198  // DevlinkResources represents all devlink resources of a devlink device
   199  type DevlinkResources struct {
   200  	Bus       string
   201  	Device    string
   202  	Resources []DevlinkResource
   203  }
   204  
   205  // parseAttributes parses provided Netlink Attributes and populates DevlinkResources, returns error if occured
   206  func (dlrs *DevlinkResources) parseAttributes(attrs map[uint16]syscall.NetlinkRouteAttr) error {
   207  	var attr syscall.NetlinkRouteAttr
   208  	var ok bool
   209  
   210  	// Bus
   211  	attr, ok = attrs[nl.DEVLINK_ATTR_BUS_NAME]
   212  	if !ok {
   213  		return fmt.Errorf("missing bus name")
   214  	}
   215  	dlrs.Bus = nl.BytesToString(attr.Value)
   216  
   217  	// Device
   218  	attr, ok = attrs[nl.DEVLINK_ATTR_DEV_NAME]
   219  	if !ok {
   220  		return fmt.Errorf("missing device name")
   221  	}
   222  	dlrs.Device = nl.BytesToString(attr.Value)
   223  
   224  	// Resource List
   225  	attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_LIST]
   226  	if !ok {
   227  		return fmt.Errorf("missing resource list")
   228  	}
   229  
   230  	resourceAttrs, err := nl.ParseRouteAttr(attr.Value)
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	for _, resourceAttr := range resourceAttrs {
   236  		resource := DevlinkResource{}
   237  		attrs, err := nl.ParseRouteAttrAsMap(resourceAttr.Value)
   238  		if err != nil {
   239  			return err
   240  		}
   241  		err = resource.parseAttributes(attrs)
   242  		if err != nil {
   243  			return fmt.Errorf("failed to parse root resoruces, %w", err)
   244  		}
   245  		dlrs.Resources = append(dlrs.Resources, resource)
   246  	}
   247  
   248  	return nil
   249  }
   250  
   251  // DevlinkParam represents parameter of the device
   252  type DevlinkParam struct {
   253  	Name      string
   254  	IsGeneric bool
   255  	Type      uint8 // possible values are in nl.DEVLINK_PARAM_TYPE_* constants
   256  	Values    []DevlinkParamValue
   257  }
   258  
   259  // DevlinkParamValue contains values of the parameter
   260  // Data field contains specific type which can be casted by unsing info from the DevlinkParam.Type field
   261  type DevlinkParamValue struct {
   262  	rawData []byte
   263  	Data    interface{}
   264  	CMODE   uint8 // possible values are in nl.DEVLINK_PARAM_CMODE_* constants
   265  }
   266  
   267  // parseAttributes parses provided Netlink Attributes and populates DevlinkParam, returns error if occured
   268  func (dlp *DevlinkParam) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
   269  	var valuesList [][]syscall.NetlinkRouteAttr
   270  	for _, attr := range attrs {
   271  		switch attr.Attr.Type {
   272  		case nl.DEVLINK_ATTR_PARAM:
   273  			nattrs, err := nl.ParseRouteAttr(attr.Value)
   274  			if err != nil {
   275  				return err
   276  			}
   277  			for _, nattr := range nattrs {
   278  				switch nattr.Attr.Type {
   279  				case nl.DEVLINK_ATTR_PARAM_NAME:
   280  					dlp.Name = nl.BytesToString(nattr.Value)
   281  				case nl.DEVLINK_ATTR_PARAM_GENERIC:
   282  					dlp.IsGeneric = true
   283  				case nl.DEVLINK_ATTR_PARAM_TYPE:
   284  					if len(nattr.Value) == 1 {
   285  						dlp.Type = nattr.Value[0]
   286  					}
   287  				case nl.DEVLINK_ATTR_PARAM_VALUES_LIST:
   288  					nnattrs, err := nl.ParseRouteAttr(nattr.Value)
   289  					if err != nil {
   290  						return err
   291  					}
   292  					valuesList = append(valuesList, nnattrs)
   293  				}
   294  			}
   295  		}
   296  	}
   297  	for _, valAttr := range valuesList {
   298  		v := DevlinkParamValue{}
   299  		if err := v.parseAttributes(valAttr, dlp.Type); err != nil {
   300  			return err
   301  		}
   302  		dlp.Values = append(dlp.Values, v)
   303  	}
   304  	return nil
   305  }
   306  
   307  func (dlpv *DevlinkParamValue) parseAttributes(attrs []syscall.NetlinkRouteAttr, paramType uint8) error {
   308  	for _, attr := range attrs {
   309  		nattrs, err := nl.ParseRouteAttr(attr.Value)
   310  		if err != nil {
   311  			return err
   312  		}
   313  		var rawData []byte
   314  		for _, nattr := range nattrs {
   315  			switch nattr.Attr.Type {
   316  			case nl.DEVLINK_ATTR_PARAM_VALUE_DATA:
   317  				rawData = nattr.Value
   318  			case nl.DEVLINK_ATTR_PARAM_VALUE_CMODE:
   319  				if len(nattr.Value) == 1 {
   320  					dlpv.CMODE = nattr.Value[0]
   321  				}
   322  			}
   323  		}
   324  		switch paramType {
   325  		case nl.DEVLINK_PARAM_TYPE_U8:
   326  			dlpv.Data = uint8(0)
   327  			if rawData != nil && len(rawData) == 1 {
   328  				dlpv.Data = uint8(rawData[0])
   329  			}
   330  		case nl.DEVLINK_PARAM_TYPE_U16:
   331  			dlpv.Data = uint16(0)
   332  			if rawData != nil {
   333  				dlpv.Data = native.Uint16(rawData)
   334  			}
   335  		case nl.DEVLINK_PARAM_TYPE_U32:
   336  			dlpv.Data = uint32(0)
   337  			if rawData != nil {
   338  				dlpv.Data = native.Uint32(rawData)
   339  			}
   340  		case nl.DEVLINK_PARAM_TYPE_STRING:
   341  			dlpv.Data = ""
   342  			if rawData != nil {
   343  				dlpv.Data = nl.BytesToString(rawData)
   344  			}
   345  		case nl.DEVLINK_PARAM_TYPE_BOOL:
   346  			dlpv.Data = rawData != nil
   347  		}
   348  	}
   349  	return nil
   350  }
   351  
   352  func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
   353  	devices := make([]*DevlinkDevice, 0, len(msgs))
   354  	for _, m := range msgs {
   355  		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   356  		if err != nil {
   357  			return nil, err
   358  		}
   359  		dev := &DevlinkDevice{}
   360  		if err = dev.parseAttributes(attrs); err != nil {
   361  			return nil, err
   362  		}
   363  		devices = append(devices, dev)
   364  	}
   365  	return devices, nil
   366  }
   367  
   368  func eswitchStringToMode(modeName string) (uint16, error) {
   369  	if modeName == "legacy" {
   370  		return nl.DEVLINK_ESWITCH_MODE_LEGACY, nil
   371  	} else if modeName == "switchdev" {
   372  		return nl.DEVLINK_ESWITCH_MODE_SWITCHDEV, nil
   373  	} else {
   374  		return 0xffff, fmt.Errorf("invalid switchdev mode")
   375  	}
   376  }
   377  
   378  func parseEswitchMode(mode uint16) string {
   379  	var eswitchMode = map[uint16]string{
   380  		nl.DEVLINK_ESWITCH_MODE_LEGACY:    "legacy",
   381  		nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev",
   382  	}
   383  	if eswitchMode[mode] == "" {
   384  		return "unknown"
   385  	} else {
   386  		return eswitchMode[mode]
   387  	}
   388  }
   389  
   390  func parseEswitchInlineMode(inlinemode uint8) string {
   391  	var eswitchInlineMode = map[uint8]string{
   392  		nl.DEVLINK_ESWITCH_INLINE_MODE_NONE:      "none",
   393  		nl.DEVLINK_ESWITCH_INLINE_MODE_LINK:      "link",
   394  		nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK:   "network",
   395  		nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport",
   396  	}
   397  	if eswitchInlineMode[inlinemode] == "" {
   398  		return "unknown"
   399  	} else {
   400  		return eswitchInlineMode[inlinemode]
   401  	}
   402  }
   403  
   404  func parseEswitchEncapMode(encapmode uint8) string {
   405  	var eswitchEncapMode = map[uint8]string{
   406  		nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE:  "disable",
   407  		nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable",
   408  	}
   409  	if eswitchEncapMode[encapmode] == "" {
   410  		return "unknown"
   411  	} else {
   412  		return eswitchEncapMode[encapmode]
   413  	}
   414  }
   415  
   416  func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
   417  	for _, a := range attrs {
   418  		switch a.Attr.Type {
   419  		case nl.DEVLINK_ATTR_BUS_NAME:
   420  			d.BusName = string(a.Value[:len(a.Value)-1])
   421  		case nl.DEVLINK_ATTR_DEV_NAME:
   422  			d.DeviceName = string(a.Value[:len(a.Value)-1])
   423  		case nl.DEVLINK_ATTR_ESWITCH_MODE:
   424  			d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
   425  		case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
   426  			d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0]))
   427  		case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE:
   428  			d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0]))
   429  		}
   430  	}
   431  	return nil
   432  }
   433  
   434  func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) {
   435  	m := msgs[0]
   436  	attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   437  	if err != nil {
   438  		return
   439  	}
   440  	dev.parseAttributes(attrs)
   441  }
   442  
   443  func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
   444  	msg := &nl.Genlmsg{
   445  		Command: nl.DEVLINK_CMD_ESWITCH_GET,
   446  		Version: nl.GENL_DEVLINK_VERSION,
   447  	}
   448  	req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
   449  	req.AddData(msg)
   450  
   451  	b := make([]byte, len(dev.BusName)+1)
   452  	copy(b, dev.BusName)
   453  	data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
   454  	req.AddData(data)
   455  
   456  	b = make([]byte, len(dev.DeviceName)+1)
   457  	copy(b, dev.DeviceName)
   458  	data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
   459  	req.AddData(data)
   460  
   461  	msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
   462  	if err != nil {
   463  		return
   464  	}
   465  	dev.parseEswitchAttrs(msgs)
   466  }
   467  
   468  // DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
   469  // otherwise returns an error code.
   470  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   471  // or incomplete.
   472  func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
   473  	f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
   474  	if err != nil {
   475  		return nil, err
   476  	}
   477  	msg := &nl.Genlmsg{
   478  		Command: nl.DEVLINK_CMD_GET,
   479  		Version: nl.GENL_DEVLINK_VERSION,
   480  	}
   481  	req := h.newNetlinkRequest(int(f.ID),
   482  		unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
   483  	req.AddData(msg)
   484  	msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
   485  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   486  		return nil, executeErr
   487  	}
   488  	devices, err := parseDevLinkDeviceList(msgs)
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  	for _, d := range devices {
   493  		h.getEswitchAttrs(f, d)
   494  	}
   495  	return devices, executeErr
   496  }
   497  
   498  // DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
   499  // otherwise returns an error code.
   500  //
   501  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   502  // or incomplete.
   503  func DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
   504  	return pkgHandle.DevLinkGetDeviceList()
   505  }
   506  
   507  func parseDevlinkDevice(msgs [][]byte) (*DevlinkDevice, error) {
   508  	m := msgs[0]
   509  	attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   510  	if err != nil {
   511  		return nil, err
   512  	}
   513  	dev := &DevlinkDevice{}
   514  	if err = dev.parseAttributes(attrs); err != nil {
   515  		return nil, err
   516  	}
   517  	return dev, nil
   518  }
   519  
   520  func (h *Handle) createCmdReq(cmd uint8, bus string, device string) (*GenlFamily, *nl.NetlinkRequest, error) {
   521  	f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
   522  	if err != nil {
   523  		return nil, nil, err
   524  	}
   525  
   526  	msg := &nl.Genlmsg{
   527  		Command: cmd,
   528  		Version: nl.GENL_DEVLINK_VERSION,
   529  	}
   530  	req := h.newNetlinkRequest(int(f.ID),
   531  		unix.NLM_F_REQUEST|unix.NLM_F_ACK)
   532  	req.AddData(msg)
   533  
   534  	b := make([]byte, len(bus)+1)
   535  	copy(b, bus)
   536  	data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
   537  	req.AddData(data)
   538  
   539  	b = make([]byte, len(device)+1)
   540  	copy(b, device)
   541  	data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
   542  	req.AddData(data)
   543  
   544  	return f, req, nil
   545  }
   546  
   547  // DevlinkGetDeviceByName provides a pointer to devlink device and nil error,
   548  // otherwise returns an error code.
   549  func (h *Handle) DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) {
   550  	f, req, err := h.createCmdReq(nl.DEVLINK_CMD_GET, Bus, Device)
   551  	if err != nil {
   552  		return nil, err
   553  	}
   554  
   555  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   556  	if err != nil {
   557  		return nil, err
   558  	}
   559  	dev, err := parseDevlinkDevice(respmsg)
   560  	if err == nil {
   561  		h.getEswitchAttrs(f, dev)
   562  	}
   563  	return dev, err
   564  }
   565  
   566  // DevlinkGetDeviceByName provides a pointer to devlink device and nil error,
   567  // otherwise returns an error code.
   568  func DevLinkGetDeviceByName(Bus string, Device string) (*DevlinkDevice, error) {
   569  	return pkgHandle.DevLinkGetDeviceByName(Bus, Device)
   570  }
   571  
   572  // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or
   573  // returns an error code.
   574  // Equivalent to: `devlink dev eswitch set $dev mode switchdev`
   575  // Equivalent to: `devlink dev eswitch set $dev mode legacy`
   576  func (h *Handle) DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
   577  	mode, err := eswitchStringToMode(NewMode)
   578  	if err != nil {
   579  		return err
   580  	}
   581  
   582  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_ESWITCH_SET, Dev.BusName, Dev.DeviceName)
   583  	if err != nil {
   584  		return err
   585  	}
   586  
   587  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_ESWITCH_MODE, nl.Uint16Attr(mode)))
   588  
   589  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   590  	return err
   591  }
   592  
   593  // DevLinkSetEswitchMode sets eswitch mode if able to set successfully or
   594  // returns an error code.
   595  // Equivalent to: `devlink dev eswitch set $dev mode switchdev`
   596  // Equivalent to: `devlink dev eswitch set $dev mode legacy`
   597  func DevLinkSetEswitchMode(Dev *DevlinkDevice, NewMode string) error {
   598  	return pkgHandle.DevLinkSetEswitchMode(Dev, NewMode)
   599  }
   600  
   601  func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
   602  	for _, a := range attrs {
   603  		switch a.Attr.Type {
   604  		case nl.DEVLINK_ATTR_BUS_NAME:
   605  			port.BusName = string(a.Value[:len(a.Value)-1])
   606  		case nl.DEVLINK_ATTR_DEV_NAME:
   607  			port.DeviceName = string(a.Value[:len(a.Value)-1])
   608  		case nl.DEVLINK_ATTR_PORT_INDEX:
   609  			port.PortIndex = native.Uint32(a.Value)
   610  		case nl.DEVLINK_ATTR_PORT_TYPE:
   611  			port.PortType = native.Uint16(a.Value)
   612  		case nl.DEVLINK_ATTR_PORT_NETDEV_NAME:
   613  			port.NetdeviceName = string(a.Value[:len(a.Value)-1])
   614  		case nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX:
   615  			port.NetdevIfIndex = native.Uint32(a.Value)
   616  		case nl.DEVLINK_ATTR_PORT_IBDEV_NAME:
   617  			port.RdmaDeviceName = string(a.Value[:len(a.Value)-1])
   618  		case nl.DEVLINK_ATTR_PORT_FLAVOUR:
   619  			port.PortFlavour = native.Uint16(a.Value)
   620  		case nl.DEVLINK_ATTR_PORT_FUNCTION:
   621  			port.Fn = &DevlinkPortFn{}
   622  			for nested := range nl.ParseAttributes(a.Value) {
   623  				switch nested.Type {
   624  				case nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR:
   625  					port.Fn.HwAddr = nested.Value[:]
   626  				case nl.DEVLINK_PORT_FN_ATTR_STATE:
   627  					port.Fn.State = uint8(nested.Value[0])
   628  				case nl.DEVLINK_PORT_FN_ATTR_OPSTATE:
   629  					port.Fn.OpState = uint8(nested.Value[0])
   630  				}
   631  			}
   632  		}
   633  	}
   634  	return nil
   635  }
   636  
   637  func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) {
   638  	ports := make([]*DevlinkPort, 0, len(msgs))
   639  	for _, m := range msgs {
   640  		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   641  		if err != nil {
   642  			return nil, err
   643  		}
   644  		port := &DevlinkPort{}
   645  		if err = port.parseAttributes(attrs); err != nil {
   646  			return nil, err
   647  		}
   648  		ports = append(ports, port)
   649  	}
   650  	return ports, nil
   651  }
   652  
   653  // DevLinkGetPortList provides a pointer to devlink ports and nil error,
   654  // otherwise returns an error code.
   655  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   656  // or incomplete.
   657  func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
   658  	f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  	msg := &nl.Genlmsg{
   663  		Command: nl.DEVLINK_CMD_PORT_GET,
   664  		Version: nl.GENL_DEVLINK_VERSION,
   665  	}
   666  	req := h.newNetlinkRequest(int(f.ID),
   667  		unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
   668  	req.AddData(msg)
   669  	msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
   670  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   671  		return nil, executeErr
   672  	}
   673  	ports, err := parseDevLinkAllPortList(msgs)
   674  	if err != nil {
   675  		return nil, err
   676  	}
   677  	return ports, executeErr
   678  }
   679  
   680  // DevLinkGetPortList provides a pointer to devlink ports and nil error,
   681  // otherwise returns an error code.
   682  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   683  // or incomplete.
   684  func DevLinkGetAllPortList() ([]*DevlinkPort, error) {
   685  	return pkgHandle.DevLinkGetAllPortList()
   686  }
   687  
   688  func parseDevlinkPortMsg(msgs [][]byte) (*DevlinkPort, error) {
   689  	m := msgs[0]
   690  	attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  	port := &DevlinkPort{}
   695  	if err = port.parseAttributes(attrs); err != nil {
   696  		return nil, err
   697  	}
   698  	return port, nil
   699  }
   700  
   701  // DevLinkGetPortByIndexprovides a pointer to devlink device and nil error,
   702  // otherwise returns an error code.
   703  func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
   704  
   705  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_GET, Bus, Device)
   706  	if err != nil {
   707  		return nil, err
   708  	}
   709  
   710  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
   711  
   712  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   713  	if err != nil {
   714  		return nil, err
   715  	}
   716  	port, err := parseDevlinkPortMsg(respmsg)
   717  	return port, err
   718  }
   719  
   720  // DevlinkGetDeviceResources returns devlink device resources
   721  func DevlinkGetDeviceResources(bus string, device string) (*DevlinkResources, error) {
   722  	return pkgHandle.DevlinkGetDeviceResources(bus, device)
   723  }
   724  
   725  // DevlinkGetDeviceResources returns devlink device resources
   726  func (h *Handle) DevlinkGetDeviceResources(bus string, device string) (*DevlinkResources, error) {
   727  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_RESOURCE_DUMP, bus, device)
   728  	if err != nil {
   729  		return nil, err
   730  	}
   731  
   732  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   733  	if err != nil {
   734  		return nil, err
   735  	}
   736  
   737  	var resources DevlinkResources
   738  	for _, m := range respmsg {
   739  		attrs, err := nl.ParseRouteAttrAsMap(m[nl.SizeofGenlmsg:])
   740  		if err != nil {
   741  			return nil, err
   742  		}
   743  		resources.parseAttributes(attrs)
   744  	}
   745  
   746  	return &resources, nil
   747  }
   748  
   749  // DevlinkGetDeviceParams returns parameters for devlink device
   750  // Equivalent to: `devlink dev param show <bus>/<device>`
   751  //
   752  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   753  // or incomplete.
   754  func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
   755  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device)
   756  	if err != nil {
   757  		return nil, err
   758  	}
   759  	req.Flags |= unix.NLM_F_DUMP
   760  	respmsg, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
   761  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   762  		return nil, executeErr
   763  	}
   764  	var params []*DevlinkParam
   765  	for _, m := range respmsg {
   766  		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
   767  		if err != nil {
   768  			return nil, err
   769  		}
   770  		p := &DevlinkParam{}
   771  		if err := p.parseAttributes(attrs); err != nil {
   772  			return nil, err
   773  		}
   774  		params = append(params, p)
   775  	}
   776  
   777  	return params, executeErr
   778  }
   779  
   780  // DevlinkGetDeviceParams returns parameters for devlink device
   781  // Equivalent to: `devlink dev param show <bus>/<device>`
   782  //
   783  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   784  // or incomplete.
   785  func DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
   786  	return pkgHandle.DevlinkGetDeviceParams(bus, device)
   787  }
   788  
   789  // DevlinkGetDeviceParamByName returns specific parameter for devlink device
   790  // Equivalent to: `devlink dev param show <bus>/<device> name <param>`
   791  func (h *Handle) DevlinkGetDeviceParamByName(bus string, device string, param string) (*DevlinkParam, error) {
   792  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device)
   793  	if err != nil {
   794  		return nil, err
   795  	}
   796  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_NAME, nl.ZeroTerminated(param)))
   797  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   798  	if err != nil {
   799  		return nil, err
   800  	}
   801  	if len(respmsg) == 0 {
   802  		return nil, fmt.Errorf("unexpected response")
   803  	}
   804  	attrs, err := nl.ParseRouteAttr(respmsg[0][nl.SizeofGenlmsg:])
   805  	if err != nil {
   806  		return nil, err
   807  	}
   808  	p := &DevlinkParam{}
   809  	if err := p.parseAttributes(attrs); err != nil {
   810  		return nil, err
   811  	}
   812  	return p, nil
   813  }
   814  
   815  // DevlinkGetDeviceParamByName returns specific parameter for devlink device
   816  // Equivalent to: `devlink dev param show <bus>/<device> name <param>`
   817  func DevlinkGetDeviceParamByName(bus string, device string, param string) (*DevlinkParam, error) {
   818  	return pkgHandle.DevlinkGetDeviceParamByName(bus, device, param)
   819  }
   820  
   821  // DevlinkSetDeviceParam set specific parameter for devlink device
   822  // Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>`
   823  // cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants
   824  // value argument should have one of the following types: uint8, uint16, uint32, string, bool
   825  func (h *Handle) DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error {
   826  	// retrive the param type
   827  	p, err := h.DevlinkGetDeviceParamByName(bus, device, param)
   828  	if err != nil {
   829  		return fmt.Errorf("failed to get device param: %v", err)
   830  	}
   831  	paramType := p.Type
   832  
   833  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_SET, bus, device)
   834  	if err != nil {
   835  		return err
   836  	}
   837  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_TYPE, nl.Uint8Attr(paramType)))
   838  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_NAME, nl.ZeroTerminated(param)))
   839  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_VALUE_CMODE, nl.Uint8Attr(cmode)))
   840  
   841  	var valueAsBytes []byte
   842  	switch paramType {
   843  	case nl.DEVLINK_PARAM_TYPE_U8:
   844  		v, ok := value.(uint8)
   845  		if !ok {
   846  			return fmt.Errorf("unepected value type required: uint8, actual: %T", value)
   847  		}
   848  		valueAsBytes = nl.Uint8Attr(v)
   849  	case nl.DEVLINK_PARAM_TYPE_U16:
   850  		v, ok := value.(uint16)
   851  		if !ok {
   852  			return fmt.Errorf("unepected value type required: uint16, actual: %T", value)
   853  		}
   854  		valueAsBytes = nl.Uint16Attr(v)
   855  	case nl.DEVLINK_PARAM_TYPE_U32:
   856  		v, ok := value.(uint32)
   857  		if !ok {
   858  			return fmt.Errorf("unepected value type required: uint32, actual: %T", value)
   859  		}
   860  		valueAsBytes = nl.Uint32Attr(v)
   861  	case nl.DEVLINK_PARAM_TYPE_STRING:
   862  		v, ok := value.(string)
   863  		if !ok {
   864  			return fmt.Errorf("unepected value type required: string, actual: %T", value)
   865  		}
   866  		valueAsBytes = nl.ZeroTerminated(v)
   867  	case nl.DEVLINK_PARAM_TYPE_BOOL:
   868  		v, ok := value.(bool)
   869  		if !ok {
   870  			return fmt.Errorf("unepected value type required: bool, actual: %T", value)
   871  		}
   872  		if v {
   873  			valueAsBytes = []byte{}
   874  		}
   875  	default:
   876  		return fmt.Errorf("unsupported parameter type: %d", paramType)
   877  	}
   878  	if valueAsBytes != nil {
   879  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_VALUE_DATA, valueAsBytes))
   880  	}
   881  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   882  	return err
   883  }
   884  
   885  // DevlinkSetDeviceParam set specific parameter for devlink device
   886  // Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>`
   887  // cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants
   888  // value argument should have one of the following types: uint8, uint16, uint32, string, bool
   889  func DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error {
   890  	return pkgHandle.DevlinkSetDeviceParam(bus, device, param, cmode, value)
   891  }
   892  
   893  // DevLinkGetPortByIndex provides a pointer to devlink portand nil error,
   894  // otherwise returns an error code.
   895  func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {
   896  	return pkgHandle.DevLinkGetPortByIndex(Bus, Device, PortIndex)
   897  }
   898  
   899  // DevLinkPortAdd adds a devlink port and returns a port on success
   900  // otherwise returns nil port and an error code.
   901  func (h *Handle) DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
   902  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_NEW, Bus, Device)
   903  	if err != nil {
   904  		return nil, err
   905  	}
   906  
   907  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(Flavour)))
   908  
   909  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(Attrs.PfNumber)))
   910  	if Flavour == nl.DEVLINK_PORT_FLAVOUR_PCI_SF && Attrs.SfNumberValid {
   911  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(Attrs.SfNumber)))
   912  	}
   913  	if Attrs.PortIndexValid {
   914  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(Attrs.PortIndex)))
   915  	}
   916  	if Attrs.ControllerValid {
   917  		req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(Attrs.Controller)))
   918  	}
   919  	respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
   920  	if err != nil {
   921  		return nil, err
   922  	}
   923  	port, err := parseDevlinkPortMsg(respmsg)
   924  	return port, err
   925  }
   926  
   927  // DevLinkPortAdd adds a devlink port and returns a port on success
   928  // otherwise returns nil port and an error code.
   929  func DevLinkPortAdd(Bus string, Device string, Flavour uint16, Attrs DevLinkPortAddAttrs) (*DevlinkPort, error) {
   930  	return pkgHandle.DevLinkPortAdd(Bus, Device, Flavour, Attrs)
   931  }
   932  
   933  // DevLinkPortDel deletes a devlink port and returns success or error code.
   934  func (h *Handle) DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
   935  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_DEL, Bus, Device)
   936  	if err != nil {
   937  		return err
   938  	}
   939  
   940  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
   941  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   942  	return err
   943  }
   944  
   945  // DevLinkPortDel deletes a devlink port and returns success or error code.
   946  func DevLinkPortDel(Bus string, Device string, PortIndex uint32) error {
   947  	return pkgHandle.DevLinkPortDel(Bus, Device, PortIndex)
   948  }
   949  
   950  // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
   951  // It returns 0 on success or error code.
   952  func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
   953  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PORT_SET, Bus, Device)
   954  	if err != nil {
   955  		return err
   956  	}
   957  
   958  	req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(PortIndex)))
   959  
   960  	fnAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION|unix.NLA_F_NESTED, nil)
   961  
   962  	if FnAttrs.HwAddrValid {
   963  		fnAttr.AddRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(FnAttrs.FnAttrs.HwAddr))
   964  	}
   965  
   966  	if FnAttrs.StateValid {
   967  		fnAttr.AddRtAttr(nl.DEVLINK_PORT_FN_ATTR_STATE, nl.Uint8Attr(FnAttrs.FnAttrs.State))
   968  	}
   969  	req.AddData(fnAttr)
   970  
   971  	_, err = req.Execute(unix.NETLINK_GENERIC, 0)
   972  	return err
   973  }
   974  
   975  // DevlinkPortFnSet sets one or more port function attributes specified by the attribute mask.
   976  // It returns 0 on success or error code.
   977  func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
   978  	return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs)
   979  }
   980  
   981  // devlinkInfoGetter is function that is responsible for getting devlink info message
   982  // this is introduced for test purpose
   983  type devlinkInfoGetter func(bus, device string) ([]byte, error)
   984  
   985  // DevlinkGetDeviceInfoByName returns devlink info for selected device,
   986  // otherwise returns an error code.
   987  // Equivalent to: `devlink dev info $dev`
   988  func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) {
   989  	info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg)
   990  	if err != nil {
   991  		return nil, err
   992  	}
   993  
   994  	return parseInfoData(info), nil
   995  }
   996  
   997  // DevlinkGetDeviceInfoByName returns devlink info for selected device,
   998  // otherwise returns an error code.
   999  // Equivalent to: `devlink dev info $dev`
  1000  func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) {
  1001  	return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg)
  1002  }
  1003  
  1004  // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
  1005  // otherwise returns an error code.
  1006  // Equivalent to: `devlink dev info $dev`
  1007  func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) {
  1008  	response, err := getInfoMsg(Bus, Device)
  1009  	if err != nil {
  1010  		return nil, err
  1011  	}
  1012  
  1013  	info, err := parseInfoMsg(response)
  1014  	if err != nil {
  1015  		return nil, err
  1016  	}
  1017  
  1018  	return info, nil
  1019  }
  1020  
  1021  // DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
  1022  // otherwise returns an error code.
  1023  // Equivalent to: `devlink dev info $dev`
  1024  func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) {
  1025  	return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg)
  1026  }
  1027  
  1028  // GetDevlinkInfo returns devlink info for target device,
  1029  // otherwise returns an error code.
  1030  func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) {
  1031  	return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
  1032  }
  1033  
  1034  // GetDevlinkInfoAsMap returns devlink info for target device as a map,
  1035  // otherwise returns an error code.
  1036  func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) {
  1037  	return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
  1038  }
  1039  
  1040  func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) {
  1041  	_, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device)
  1042  	if err != nil {
  1043  		return nil, err
  1044  	}
  1045  
  1046  	response, err := req.Execute(unix.NETLINK_GENERIC, 0)
  1047  	if err != nil {
  1048  		return nil, err
  1049  	}
  1050  
  1051  	if len(response) < 1 {
  1052  		return nil, fmt.Errorf("getDevlinkInfoMsg: message too short")
  1053  	}
  1054  
  1055  	return response[0], nil
  1056  }
  1057  
  1058  func parseInfoMsg(msg []byte) (map[string]string, error) {
  1059  	if len(msg) < nl.SizeofGenlmsg {
  1060  		return nil, fmt.Errorf("parseInfoMsg: message too short")
  1061  	}
  1062  
  1063  	info := make(map[string]string)
  1064  	err := collectInfoData(msg[nl.SizeofGenlmsg:], info)
  1065  
  1066  	if err != nil {
  1067  		return nil, err
  1068  	}
  1069  
  1070  	return info, nil
  1071  }
  1072  
  1073  func collectInfoData(msg []byte, data map[string]string) error {
  1074  	attrs, err := nl.ParseRouteAttr(msg)
  1075  	if err != nil {
  1076  		return err
  1077  	}
  1078  
  1079  	for _, attr := range attrs {
  1080  		switch attr.Attr.Type {
  1081  		case nl.DEVLINK_ATTR_INFO_DRIVER_NAME:
  1082  			data["driver"] = parseInfoValue(attr.Value)
  1083  		case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER:
  1084  			data["serialNumber"] = parseInfoValue(attr.Value)
  1085  		case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED,
  1086  			nl.DEVLINK_ATTR_INFO_VERSION_STORED:
  1087  			key, value, err := getNestedInfoData(attr.Value)
  1088  			if err != nil {
  1089  				return err
  1090  			}
  1091  			data[key] = value
  1092  		}
  1093  	}
  1094  
  1095  	if len(data) == 0 {
  1096  		return fmt.Errorf("collectInfoData: could not read attributes")
  1097  	}
  1098  
  1099  	return nil
  1100  }
  1101  
  1102  func getNestedInfoData(msg []byte) (string, string, error) {
  1103  	nestedAttrs, err := nl.ParseRouteAttr(msg)
  1104  
  1105  	var key, value string
  1106  
  1107  	if err != nil {
  1108  		return "", "", err
  1109  	}
  1110  
  1111  	if len(nestedAttrs) != 2 {
  1112  		return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure")
  1113  	}
  1114  
  1115  	for _, nestedAttr := range nestedAttrs {
  1116  		switch nestedAttr.Attr.Type {
  1117  		case nl.DEVLINK_ATTR_INFO_VERSION_NAME:
  1118  			key = parseInfoValue(nestedAttr.Value)
  1119  		case nl.DEVLINK_ATTR_INFO_VERSION_VALUE:
  1120  			value = parseInfoValue(nestedAttr.Value)
  1121  		}
  1122  	}
  1123  
  1124  	if key == "" {
  1125  		return "", "", fmt.Errorf("getNestedInfoData: key not found")
  1126  	}
  1127  
  1128  	if value == "" {
  1129  		return "", "", fmt.Errorf("getNestedInfoData: value not found")
  1130  	}
  1131  
  1132  	return key, value, nil
  1133  }
  1134  
  1135  func parseInfoData(data map[string]string) *DevlinkDeviceInfo {
  1136  	info := new(DevlinkDeviceInfo)
  1137  	for key, value := range data {
  1138  		switch key {
  1139  		case "driver":
  1140  			info.Driver = value
  1141  		case "serialNumber":
  1142  			info.SerialNumber = value
  1143  		case "board.id":
  1144  			info.BoardID = value
  1145  		case "fw.app":
  1146  			info.FwApp = value
  1147  		case "fw.app.bundle_id":
  1148  			info.FwAppBoundleID = value
  1149  		case "fw.app.name":
  1150  			info.FwAppName = value
  1151  		case "fw.bundle_id":
  1152  			info.FwBoundleID = value
  1153  		case "fw.mgmt":
  1154  			info.FwMgmt = value
  1155  		case "fw.mgmt.api":
  1156  			info.FwMgmtAPI = value
  1157  		case "fw.mgmt.build":
  1158  			info.FwMgmtBuild = value
  1159  		case "fw.netlist":
  1160  			info.FwNetlist = value
  1161  		case "fw.netlist.build":
  1162  			info.FwNetlistBuild = value
  1163  		case "fw.psid.api":
  1164  			info.FwPsidAPI = value
  1165  		case "fw.undi":
  1166  			info.FwUndi = value
  1167  		}
  1168  	}
  1169  	return info
  1170  }
  1171  
  1172  func parseInfoValue(value []byte) string {
  1173  	v := strings.ReplaceAll(string(value), "\x00", "")
  1174  	return strings.TrimSpace(v)
  1175  }