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

     1  package netlink
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  
    10  	"github.com/vishvananda/netlink/nl"
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  // LinkAttrs represents data shared by most link types
    15  type RdmaLinkAttrs struct {
    16  	Index           uint32
    17  	Name            string
    18  	FirmwareVersion string
    19  	NodeGuid        string
    20  	SysImageGuid    string
    21  	NumPorts        uint32
    22  }
    23  
    24  // Link represents a rdma device from netlink.
    25  type RdmaLink struct {
    26  	Attrs RdmaLinkAttrs
    27  }
    28  
    29  func getProtoField(clientType int, op int) int {
    30  	return ((clientType << nl.RDMA_NL_GET_CLIENT_SHIFT) | op)
    31  }
    32  
    33  func uint64ToGuidString(guid uint64) string {
    34  	//Convert to byte array
    35  	sysGuidBytes := new(bytes.Buffer)
    36  	binary.Write(sysGuidBytes, binary.LittleEndian, guid)
    37  
    38  	//Convert to HardwareAddr
    39  	sysGuidNet := net.HardwareAddr(sysGuidBytes.Bytes())
    40  
    41  	//Get the String
    42  	return sysGuidNet.String()
    43  }
    44  
    45  func executeOneGetRdmaLink(data []byte) (*RdmaLink, error) {
    46  
    47  	link := RdmaLink{}
    48  
    49  	reader := bytes.NewReader(data)
    50  	for reader.Len() >= 4 {
    51  		_, attrType, len, value := parseNfAttrTLV(reader)
    52  
    53  		switch attrType {
    54  		case nl.RDMA_NLDEV_ATTR_DEV_INDEX:
    55  			var Index uint32
    56  			r := bytes.NewReader(value)
    57  			binary.Read(r, nl.NativeEndian(), &Index)
    58  			link.Attrs.Index = Index
    59  		case nl.RDMA_NLDEV_ATTR_DEV_NAME:
    60  			link.Attrs.Name = string(value[0 : len-1])
    61  		case nl.RDMA_NLDEV_ATTR_FW_VERSION:
    62  			link.Attrs.FirmwareVersion = string(value[0 : len-1])
    63  		case nl.RDMA_NLDEV_ATTR_NODE_GUID:
    64  			var guid uint64
    65  			r := bytes.NewReader(value)
    66  			binary.Read(r, nl.NativeEndian(), &guid)
    67  			link.Attrs.NodeGuid = uint64ToGuidString(guid)
    68  		case nl.RDMA_NLDEV_ATTR_SYS_IMAGE_GUID:
    69  			var sysGuid uint64
    70  			r := bytes.NewReader(value)
    71  			binary.Read(r, nl.NativeEndian(), &sysGuid)
    72  			link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid)
    73  		case nl.RDMA_NLDEV_ATTR_PORT_INDEX:
    74  			var availablePort uint32
    75  			r := bytes.NewReader(value)
    76  			binary.Read(r, nl.NativeEndian(), &availablePort)
    77  			link.Attrs.NumPorts = availablePort
    78  		}
    79  		if (len % 4) != 0 {
    80  			// Skip pad bytes
    81  			reader.Seek(int64(4-(len%4)), seekCurrent)
    82  		}
    83  	}
    84  	return &link, nil
    85  }
    86  
    87  func execRdmaSetLink(req *nl.NetlinkRequest) error {
    88  
    89  	_, err := req.Execute(unix.NETLINK_RDMA, 0)
    90  	return err
    91  }
    92  
    93  // RdmaLinkList gets a list of RDMA link devices.
    94  // Equivalent to: `rdma dev show`
    95  //
    96  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
    97  // or incomplete.
    98  func RdmaLinkList() ([]*RdmaLink, error) {
    99  	return pkgHandle.RdmaLinkList()
   100  }
   101  
   102  // RdmaLinkList gets a list of RDMA link devices.
   103  // Equivalent to: `rdma dev show`
   104  //
   105  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   106  // or incomplete.
   107  func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) {
   108  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET)
   109  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP)
   110  
   111  	msgs, executeErr := req.Execute(unix.NETLINK_RDMA, 0)
   112  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   113  		return nil, executeErr
   114  	}
   115  
   116  	var res []*RdmaLink
   117  	for _, m := range msgs {
   118  		link, err := executeOneGetRdmaLink(m)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		res = append(res, link)
   123  	}
   124  
   125  	return res, executeErr
   126  }
   127  
   128  // RdmaLinkByName finds a link by name and returns a pointer to the object if
   129  // found and nil error, otherwise returns error code.
   130  //
   131  // If the returned error is [ErrDumpInterrupted], the result may be missing or
   132  // outdated and the caller should retry.
   133  func RdmaLinkByName(name string) (*RdmaLink, error) {
   134  	return pkgHandle.RdmaLinkByName(name)
   135  }
   136  
   137  // RdmaLinkByName finds a link by name and returns a pointer to the object if
   138  // found and nil error, otherwise returns error code.
   139  //
   140  // If the returned error is [ErrDumpInterrupted], the result may be missing or
   141  // outdated and the caller should retry.
   142  func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) {
   143  	links, err := h.RdmaLinkList()
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	for _, link := range links {
   148  		if link.Attrs.Name == name {
   149  			return link, nil
   150  		}
   151  	}
   152  	return nil, fmt.Errorf("Rdma device %v not found", name)
   153  }
   154  
   155  // RdmaLinkSetName sets the name of the rdma link device. Return nil on success
   156  // or error otherwise.
   157  // Equivalent to: `rdma dev set $old_devname name $name`
   158  func RdmaLinkSetName(link *RdmaLink, name string) error {
   159  	return pkgHandle.RdmaLinkSetName(link, name)
   160  }
   161  
   162  // RdmaLinkSetName sets the name of the rdma link device. Return nil on success
   163  // or error otherwise.
   164  // Equivalent to: `rdma dev set $old_devname name $name`
   165  func (h *Handle) RdmaLinkSetName(link *RdmaLink, name string) error {
   166  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET)
   167  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
   168  
   169  	b := make([]byte, 4)
   170  	native.PutUint32(b, uint32(link.Attrs.Index))
   171  	data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b)
   172  	req.AddData(data)
   173  
   174  	b = make([]byte, len(name)+1)
   175  	copy(b, name)
   176  	data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, b)
   177  	req.AddData(data)
   178  
   179  	return execRdmaSetLink(req)
   180  }
   181  
   182  func netnsModeToString(mode uint8) string {
   183  	switch mode {
   184  	case 0:
   185  		return "exclusive"
   186  	case 1:
   187  		return "shared"
   188  	default:
   189  		return "unknown"
   190  	}
   191  }
   192  
   193  func executeOneGetRdmaNetnsMode(data []byte) (string, error) {
   194  	reader := bytes.NewReader(data)
   195  	for reader.Len() >= 4 {
   196  		_, attrType, len, value := parseNfAttrTLV(reader)
   197  
   198  		switch attrType {
   199  		case nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE:
   200  			var mode uint8
   201  			r := bytes.NewReader(value)
   202  			binary.Read(r, nl.NativeEndian(), &mode)
   203  			return netnsModeToString(mode), nil
   204  		}
   205  		if (len % 4) != 0 {
   206  			// Skip pad bytes
   207  			reader.Seek(int64(4-(len%4)), seekCurrent)
   208  		}
   209  	}
   210  	return "", fmt.Errorf("Invalid netns mode")
   211  }
   212  
   213  // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem
   214  // Returns mode string and error status as nil on success or returns error
   215  // otherwise.
   216  // Equivalent to: `rdma system show netns'
   217  func RdmaSystemGetNetnsMode() (string, error) {
   218  	return pkgHandle.RdmaSystemGetNetnsMode()
   219  }
   220  
   221  // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem
   222  // Returns mode string and error status as nil on success or returns error
   223  // otherwise.
   224  // Equivalent to: `rdma system show netns'
   225  func (h *Handle) RdmaSystemGetNetnsMode() (string, error) {
   226  
   227  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_GET)
   228  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
   229  
   230  	msgs, err := req.Execute(unix.NETLINK_RDMA, 0)
   231  	if err != nil {
   232  		return "", err
   233  	}
   234  	if len(msgs) == 0 {
   235  		return "", fmt.Errorf("No valid response from kernel")
   236  	}
   237  	return executeOneGetRdmaNetnsMode(msgs[0])
   238  }
   239  
   240  func netnsModeStringToUint8(mode string) (uint8, error) {
   241  	switch mode {
   242  	case "exclusive":
   243  		return 0, nil
   244  	case "shared":
   245  		return 1, nil
   246  	default:
   247  		return 0, fmt.Errorf("Invalid mode; %q", mode)
   248  	}
   249  }
   250  
   251  // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem
   252  // Returns nil on success or appropriate error code.
   253  // Equivalent to: `rdma system set netns { shared | exclusive }'
   254  func RdmaSystemSetNetnsMode(NewMode string) error {
   255  	return pkgHandle.RdmaSystemSetNetnsMode(NewMode)
   256  }
   257  
   258  // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem
   259  // Returns nil on success or appropriate error code.
   260  // Equivalent to: `rdma system set netns { shared | exclusive }'
   261  func (h *Handle) RdmaSystemSetNetnsMode(NewMode string) error {
   262  	value, err := netnsModeStringToUint8(NewMode)
   263  	if err != nil {
   264  		return err
   265  	}
   266  
   267  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_SET)
   268  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
   269  
   270  	data := nl.NewRtAttr(nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE, []byte{value})
   271  	req.AddData(data)
   272  
   273  	_, err = req.Execute(unix.NETLINK_RDMA, 0)
   274  	return err
   275  }
   276  
   277  // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The
   278  // fd must be an open file descriptor to a network namespace.
   279  // Similar to: `rdma dev set $dev netns $ns`
   280  func RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error {
   281  	return pkgHandle.RdmaLinkSetNsFd(link, fd)
   282  }
   283  
   284  // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The
   285  // fd must be an open file descriptor to a network namespace.
   286  // Similar to: `rdma dev set $dev netns $ns`
   287  func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error {
   288  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET)
   289  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
   290  
   291  	data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX,
   292  		nl.Uint32Attr(link.Attrs.Index))
   293  	req.AddData(data)
   294  
   295  	data = nl.NewRtAttr(nl.RDMA_NLDEV_NET_NS_FD, nl.Uint32Attr(fd))
   296  	req.AddData(data)
   297  
   298  	return execRdmaSetLink(req)
   299  }
   300  
   301  // RdmaLinkDel deletes an rdma link
   302  //
   303  // Similar to: rdma link delete NAME
   304  // REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html
   305  func RdmaLinkDel(name string) error {
   306  	return pkgHandle.RdmaLinkDel(name)
   307  }
   308  
   309  // RdmaLinkDel deletes an rdma link.
   310  //
   311  // If the returned error is [ErrDumpInterrupted], the caller should retry.
   312  func (h *Handle) RdmaLinkDel(name string) error {
   313  	link, err := h.RdmaLinkByName(name)
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_DELLINK)
   319  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
   320  
   321  	b := make([]byte, 4)
   322  	native.PutUint32(b, link.Attrs.Index)
   323  	req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b))
   324  
   325  	_, err = req.Execute(unix.NETLINK_RDMA, 0)
   326  	return err
   327  }
   328  
   329  // RdmaLinkAdd adds an rdma link for the specified type to the network device.
   330  // Similar to: rdma link add NAME type TYPE netdev NETDEV
   331  //
   332  //	NAME - specifies the new name of the rdma link to add
   333  //	TYPE - specifies which rdma type to use.  Link types:
   334  //		rxe - Soft RoCE driver
   335  //		siw - Soft iWARP driver
   336  //	NETDEV - specifies the network device to which the link is bound
   337  //
   338  // REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html
   339  func RdmaLinkAdd(linkName, linkType, netdev string) error {
   340  	return pkgHandle.RdmaLinkAdd(linkName, linkType, netdev)
   341  }
   342  
   343  // RdmaLinkAdd adds an rdma link for the specified type to the network device.
   344  func (h *Handle) RdmaLinkAdd(linkName string, linkType string, netdev string) error {
   345  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_NEWLINK)
   346  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK)
   347  
   348  	req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, nl.ZeroTerminated(linkName)))
   349  	req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_LINK_TYPE, nl.ZeroTerminated(linkType)))
   350  	req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_NDEV_NAME, nl.ZeroTerminated(netdev)))
   351  	_, err := req.Execute(unix.NETLINK_RDMA, 0)
   352  	return err
   353  }
   354  
   355  // RdmaResource represents a rdma device resource tracking summaries
   356  type RdmaResource struct {
   357  	Index                      uint32
   358  	Name                       string
   359  	RdmaResourceSummaryEntries map[string]uint64
   360  }
   361  
   362  // RdmaResourceList list rdma resource tracking information
   363  // Returns all rdma devices resource tracking summary on success or returns error
   364  // otherwise.
   365  // Equivalent to: `rdma resource'
   366  func RdmaResourceList() ([]*RdmaResource, error) {
   367  	return pkgHandle.RdmaResourceList()
   368  }
   369  
   370  // RdmaResourceList list rdma resource tracking information
   371  // Returns all rdma devices resource tracking summary on success or returns error
   372  // otherwise.
   373  // Equivalent to: `rdma resource'
   374  func (h *Handle) RdmaResourceList() ([]*RdmaResource, error) {
   375  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_RES_GET)
   376  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP)
   377  
   378  	msgs, err := req.Execute(unix.NETLINK_RDMA, 0)
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  	if len(msgs) == 0 {
   383  		return nil, fmt.Errorf("No valid response from kernel")
   384  	}
   385  	var rdmaResources []*RdmaResource
   386  	for _, msg := range msgs {
   387  		res, err := executeOneGetRdmaResourceList(msg)
   388  		if err != nil {
   389  			return nil, err
   390  		}
   391  		rdmaResources = append(rdmaResources, res)
   392  	}
   393  	return rdmaResources, nil
   394  }
   395  
   396  func parseRdmaCounters(counterType uint16, data []byte) (map[string]uint64, error) {
   397  	var counterKeyType, counterValueType uint16
   398  	switch counterType {
   399  	case nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY:
   400  		counterKeyType = nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME
   401  		counterValueType = nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR
   402  	case nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY:
   403  		counterKeyType = nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME
   404  		counterValueType = nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE
   405  	default:
   406  		return nil, fmt.Errorf("Invalid counter type: %d", counterType)
   407  	}
   408  	counters := make(map[string]uint64)
   409  	reader := bytes.NewReader(data)
   410  
   411  	for reader.Len() >= 4 {
   412  		_, attrType, _, value := parseNfAttrTLV(reader)
   413  		if attrType != counterType {
   414  			return nil, fmt.Errorf("Invalid resource summary entry type; %d", attrType)
   415  		}
   416  
   417  		summaryReader := bytes.NewReader(value)
   418  		for summaryReader.Len() >= 4 {
   419  			_, attrType, len, value := parseNfAttrTLV(summaryReader)
   420  			if attrType != counterKeyType {
   421  				return nil, fmt.Errorf("Invalid resource summary entry name type; %d", attrType)
   422  			}
   423  			name := string(value[0 : len-1])
   424  			// Skip pad bytes
   425  			if (len % 4) != 0 {
   426  				summaryReader.Seek(int64(4-(len%4)), seekCurrent)
   427  			}
   428  			_, attrType, len, value = parseNfAttrTLV(summaryReader)
   429  			if attrType != counterValueType {
   430  				return nil, fmt.Errorf("Invalid resource summary entry value type; %d", attrType)
   431  			}
   432  			counters[name] = native.Uint64(value)
   433  		}
   434  	}
   435  	return counters, nil
   436  }
   437  
   438  func executeOneGetRdmaResourceList(data []byte) (*RdmaResource, error) {
   439  	var res RdmaResource
   440  	reader := bytes.NewReader(data)
   441  	for reader.Len() >= 4 {
   442  		_, attrType, len, value := parseNfAttrTLV(reader)
   443  
   444  		switch attrType {
   445  		case nl.RDMA_NLDEV_ATTR_DEV_INDEX:
   446  			var Index uint32
   447  			r := bytes.NewReader(value)
   448  			binary.Read(r, nl.NativeEndian(), &Index)
   449  			res.Index = Index
   450  		case nl.RDMA_NLDEV_ATTR_DEV_NAME:
   451  			res.Name = string(value[0 : len-1])
   452  		case nl.RDMA_NLDEV_ATTR_RES_SUMMARY:
   453  			var err error
   454  			res.RdmaResourceSummaryEntries, err = parseRdmaCounters(nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY, value)
   455  			if err != nil {
   456  				return nil, err
   457  			}
   458  		}
   459  		if (len % 4) != 0 {
   460  			// Skip pad bytes
   461  			reader.Seek(int64(4-(len%4)), seekCurrent)
   462  		}
   463  	}
   464  	return &res, nil
   465  }
   466  
   467  // RdmaPortStatistic represents a rdma port statistic counter
   468  type RdmaPortStatistic struct {
   469  	PortIndex  uint32
   470  	Statistics map[string]uint64
   471  }
   472  
   473  // RdmaDeviceStatistic represents a rdma device statistic counter
   474  type RdmaDeviceStatistic struct {
   475  	RdmaPortStatistics []*RdmaPortStatistic
   476  }
   477  
   478  // RdmaStatistic get rdma device statistic counters
   479  // Returns rdma device statistic counters on success or returns error
   480  // otherwise.
   481  // Equivalent to: `rdma statistic show link [DEV]'
   482  func RdmaStatistic(link *RdmaLink) (*RdmaDeviceStatistic, error) {
   483  	return pkgHandle.RdmaStatistic(link)
   484  }
   485  
   486  // RdmaStatistic get rdma device statistic counters
   487  // Returns rdma device statistic counters on success or returns error
   488  // otherwise.
   489  // Equivalent to: `rdma statistic show link [DEV]'
   490  func (h *Handle) RdmaStatistic(link *RdmaLink) (*RdmaDeviceStatistic, error) {
   491  	rdmaLinkStatistic := make([]*RdmaPortStatistic, 0)
   492  	for portIndex := uint32(1); portIndex <= link.Attrs.NumPorts; portIndex++ {
   493  		portStatistic, err := h.RdmaPortStatisticList(link, portIndex)
   494  		if err != nil {
   495  			return nil, err
   496  		}
   497  		rdmaLinkStatistic = append(rdmaLinkStatistic, portStatistic)
   498  	}
   499  	return &RdmaDeviceStatistic{RdmaPortStatistics: rdmaLinkStatistic}, nil
   500  }
   501  
   502  // RdmaPortStatisticList get rdma device port statistic counters
   503  // Returns rdma device port statistic counters on success or returns error
   504  // otherwise.
   505  // Equivalent to: `rdma statistic show link [DEV/PORT]'
   506  func RdmaPortStatisticList(link *RdmaLink, port uint32) (*RdmaPortStatistic, error) {
   507  	return pkgHandle.RdmaPortStatisticList(link, port)
   508  }
   509  
   510  // RdmaPortStatisticList get rdma device port statistic counters
   511  // Returns rdma device port statistic counters on success or returns error
   512  // otherwise.
   513  // Equivalent to: `rdma statistic show link [DEV/PORT]'
   514  func (h *Handle) RdmaPortStatisticList(link *RdmaLink, port uint32) (*RdmaPortStatistic, error) {
   515  	proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_STAT_GET)
   516  	req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_REQUEST)
   517  	b := make([]byte, 4)
   518  	native.PutUint32(b, link.Attrs.Index)
   519  	data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b)
   520  	req.AddData(data)
   521  
   522  	b = make([]byte, 4)
   523  	native.PutUint32(b, port)
   524  	data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_PORT_INDEX, b)
   525  	req.AddData(data)
   526  
   527  	msgs, err := req.Execute(unix.NETLINK_RDMA, 0)
   528  	if err != nil {
   529  		return nil, err
   530  	}
   531  	if len(msgs) != 1 {
   532  		return nil, fmt.Errorf("No valid response from kernel")
   533  	}
   534  	return executeOneGetRdmaPortStatistics(msgs[0])
   535  }
   536  
   537  func executeOneGetRdmaPortStatistics(data []byte) (*RdmaPortStatistic, error) {
   538  	var stat RdmaPortStatistic
   539  	reader := bytes.NewReader(data)
   540  	for reader.Len() >= 4 {
   541  		_, attrType, len, value := parseNfAttrTLV(reader)
   542  
   543  		switch attrType {
   544  		case nl.RDMA_NLDEV_ATTR_PORT_INDEX:
   545  			var Index uint32
   546  			r := bytes.NewReader(value)
   547  			binary.Read(r, nl.NativeEndian(), &Index)
   548  			stat.PortIndex = Index
   549  		case nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTERS:
   550  			var err error
   551  			stat.Statistics, err = parseRdmaCounters(nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY, value)
   552  			if err != nil {
   553  				return nil, err
   554  			}
   555  		}
   556  		if (len % 4) != 0 {
   557  			// Skip pad bytes
   558  			reader.Seek(int64(4-(len%4)), seekCurrent)
   559  		}
   560  	}
   561  	return &stat, nil
   562  }