mirror of
https://github.com/jsimonetti/rtnetlink.git
synced 2026-03-31 09:51:08 +02:00
* netns: remove iproute2 dependency This commit introduces a breaking change to rtnetlink.NetNS. The existing netns implementation had a few problems. It assumed that network namespaces have names, that they would always be pinned to /var/run/netns, and that numeric/integer references are pid references. This made the NetNS type unusable for referring to existing netns by fd, such as ones created by other libraries, or by opening procfs entries directly as demonstrated in the new testutils.NetNS() function. The forced dependency on the `ip` CLI tool also wasn't reasonable for a pure-Go library. Using the old implementation in a scratch/distroless container would quickly run into roadblocks. This commit also removes the functionality of creating and pinning new netns. There are plenty of options out in the Go ecosystem for that, and providing your own is only a few lines of code. Signed-off-by: Timo Beckers <timo@incline.eu> * test: remove calls to unix.Setrlimit() in favor of rlimit.RemoveMemlock() ebpf-go provides this out of the box and skips setting the rlimit on kernels that support bpf memory cgroup accounting. Signed-off-by: Timo Beckers <timo@incline.eu> * neigh: fix flaky tests, add State field to Neigh entry The flaky tests that were documented in the code are expected. Use the State field to discard entries that can't reasonably be considered in tests. Signed-off-by: Timo Beckers <timo@incline.eu> * neigh: fix race in Conn.Neighbours When running tests locally, I would frequently hit "too many/little matches, expected 1, actual 0" due to other tests creating and deleting interfaces in the common host netns used by all tests. Neigh entries that fail the interface lookup can't have their Interface fields populated and should be dropped from the result since the interface is no longer there to begin with. Signed-off-by: Timo Beckers <timo@incline.eu> * xdp: refactor test suite to use test helpers and netns-driven tests While running the test suite for testing netns-related changes, I noticed some of the xdp tests started failing because they wanted to create a dummy interface in the host network namespace. Avoid the complexity of managing dummy interfaces altogether by running all tests within their own netns and use the existing lo device that's present by default. Signed-off-by: Timo Beckers <timo@incline.eu> * xdp,netkit: remove duplicate kernelMinReq in favor of testutils.SkipOnOldKernel There were two implementations of this, so move them to common testutils. Signed-off-by: Timo Beckers <timo@incline.eu> --------- Signed-off-by: Timo Beckers <timo@incline.eu>
779 lines
22 KiB
Go
779 lines
22 KiB
Go
package rtnetlink
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/jsimonetti/rtnetlink/v2/internal/unix"
|
|
|
|
"github.com/mdlayher/netlink"
|
|
)
|
|
|
|
var (
|
|
// errInvalidLinkMessage is returned when a LinkMessage is malformed.
|
|
errInvalidLinkMessage = errors.New("rtnetlink LinkMessage is invalid or too short")
|
|
|
|
// errInvalidLinkMessageAttr is returned when link attributes are malformed.
|
|
errInvalidLinkMessageAttr = errors.New("rtnetlink LinkMessage has a wrong attribute data length")
|
|
)
|
|
|
|
var _ Message = &LinkMessage{}
|
|
|
|
// A LinkMessage is a route netlink link message.
|
|
type LinkMessage struct {
|
|
// Always set to AF_UNSPEC (0)
|
|
Family uint16
|
|
|
|
// Device Type
|
|
Type uint16
|
|
|
|
// Unique interface index, using a nonzero value with
|
|
// NewLink will instruct the kernel to create a
|
|
// device with the given index (kernel 3.7+ required)
|
|
Index uint32
|
|
|
|
// Contains device flags, see netdevice(7)
|
|
Flags uint32
|
|
|
|
// Change Flags, specifies which flags will be affected by the Flags field
|
|
Change uint32
|
|
|
|
// Attributes List
|
|
Attributes *LinkAttributes
|
|
}
|
|
|
|
// MarshalBinary marshals a LinkMessage into a byte slice.
|
|
func (m *LinkMessage) MarshalBinary() ([]byte, error) {
|
|
b := make([]byte, unix.SizeofIfInfomsg)
|
|
|
|
b[0] = 0 // Family
|
|
b[1] = 0 // reserved
|
|
nativeEndian.PutUint16(b[2:4], m.Type)
|
|
nativeEndian.PutUint32(b[4:8], m.Index)
|
|
nativeEndian.PutUint32(b[8:12], m.Flags)
|
|
nativeEndian.PutUint32(b[12:16], m.Change)
|
|
|
|
if m.Attributes != nil {
|
|
if m.Attributes.Info != nil && m.Attributes.Info.Data != nil {
|
|
if verifier, ok := m.Attributes.Info.Data.(LinkDriverVerifier); ok {
|
|
if err := verifier.Verify(m); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
ae := netlink.NewAttributeEncoder()
|
|
ae.ByteOrder = nativeEndian
|
|
err := m.Attributes.encode(ae)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
a, err := ae.Encode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return append(b, a...), nil
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
|
|
func (m *LinkMessage) UnmarshalBinary(b []byte) error {
|
|
l := len(b)
|
|
if l < unix.SizeofIfInfomsg {
|
|
return errInvalidLinkMessage
|
|
}
|
|
|
|
m.Family = nativeEndian.Uint16(b[0:2])
|
|
m.Type = nativeEndian.Uint16(b[2:4])
|
|
m.Index = nativeEndian.Uint32(b[4:8])
|
|
m.Flags = nativeEndian.Uint32(b[8:12])
|
|
m.Change = nativeEndian.Uint32(b[12:16])
|
|
|
|
if l > unix.SizeofIfInfomsg {
|
|
m.Attributes = &LinkAttributes{}
|
|
ad, err := netlink.NewAttributeDecoder(b[16:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ad.ByteOrder = nativeEndian
|
|
err = m.Attributes.decode(ad)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// rtMessage is an empty method to sattisfy the Message interface.
|
|
func (*LinkMessage) rtMessage() {}
|
|
|
|
// LinkService is used to retrieve rtnetlink family information.
|
|
type LinkService struct {
|
|
c *Conn
|
|
}
|
|
|
|
// execute executes the request and returns the messages as a LinkMessage slice
|
|
func (l *LinkService) execute(m Message, family uint16, flags netlink.HeaderFlags) ([]LinkMessage, error) {
|
|
msgs, err := l.c.Execute(m, family, flags)
|
|
|
|
links := make([]LinkMessage, len(msgs))
|
|
for i := range msgs {
|
|
links[i] = *msgs[i].(*LinkMessage)
|
|
}
|
|
|
|
return links, err
|
|
}
|
|
|
|
// New creates a new interface using the LinkMessage information.
|
|
func (l *LinkService) New(req *LinkMessage) error {
|
|
flags := netlink.Request | netlink.Create | netlink.Acknowledge | netlink.Excl
|
|
_, err := l.execute(req, unix.RTM_NEWLINK, flags)
|
|
|
|
return err
|
|
}
|
|
|
|
// Delete removes an interface by index.
|
|
func (l *LinkService) Delete(index uint32) error {
|
|
req := &LinkMessage{
|
|
Index: index,
|
|
}
|
|
|
|
flags := netlink.Request | netlink.Acknowledge
|
|
_, err := l.c.Execute(req, unix.RTM_DELLINK, flags)
|
|
|
|
return err
|
|
}
|
|
|
|
// Get retrieves interface information by index.
|
|
func (l *LinkService) Get(index uint32) (LinkMessage, error) {
|
|
req := &LinkMessage{
|
|
Index: index,
|
|
}
|
|
|
|
flags := netlink.Request | netlink.DumpFiltered
|
|
links, err := l.execute(req, unix.RTM_GETLINK, flags)
|
|
|
|
if len(links) != 1 {
|
|
return LinkMessage{}, fmt.Errorf("too many/little matches, expected 1, actual %d", len(links))
|
|
}
|
|
|
|
return links[0], err
|
|
}
|
|
|
|
// Set sets interface attributes according to the LinkMessage information.
|
|
//
|
|
// ref: https://lwn.net/Articles/236919/
|
|
// We explicitly use RTM_NEWLINK to set link attributes instead of
|
|
// RTM_SETLINK because:
|
|
// - using RTM_SETLINK is actually an old rtnetlink API, not supporting most
|
|
// attributes common today
|
|
// - using RTM_NEWLINK is the preferred way to create AND update links
|
|
// - RTM_NEWLINK is backward compatible to RTM_SETLINK
|
|
func (l *LinkService) Set(req *LinkMessage) error {
|
|
flags := netlink.Request | netlink.Acknowledge
|
|
_, err := l.c.Execute(req, unix.RTM_NEWLINK, flags)
|
|
|
|
return err
|
|
}
|
|
|
|
func (l *LinkService) list(kind string) ([]LinkMessage, error) {
|
|
req := &LinkMessage{}
|
|
if kind != "" {
|
|
req.Attributes = &LinkAttributes{
|
|
Info: &LinkInfo{Kind: kind},
|
|
}
|
|
}
|
|
|
|
flags := netlink.Request | netlink.Dump
|
|
return l.execute(req, unix.RTM_GETLINK, flags)
|
|
}
|
|
|
|
// ListByKind retrieves all interfaces of a specific kind.
|
|
func (l *LinkService) ListByKind(kind string) ([]LinkMessage, error) {
|
|
return l.list(kind)
|
|
}
|
|
|
|
// List retrieves all interfaces.
|
|
func (l *LinkService) List() ([]LinkMessage, error) {
|
|
return l.list("")
|
|
}
|
|
|
|
// LinkAttributes contains all attributes for an interface.
|
|
type LinkAttributes struct {
|
|
Address net.HardwareAddr // Interface L2 address
|
|
Alias *string // Interface alias name
|
|
Broadcast net.HardwareAddr // L2 broadcast address
|
|
Carrier *uint8 // Current physical link state of the interface.
|
|
CarrierChanges *uint32 // Number of times the link has seen a change from UP to DOWN and vice versa
|
|
CarrierUpCount *uint32 // Number of times the link has been up
|
|
CarrierDownCount *uint32 // Number of times the link has been down
|
|
Index *uint32 // System-wide interface unique index identifier
|
|
Info *LinkInfo // Detailed Interface Information
|
|
LinkMode *uint8 // Interface link mode
|
|
MTU uint32 // MTU of the device
|
|
Name string // Device name
|
|
NetDevGroup *uint32 // Interface network device group
|
|
OperationalState OperationalState // Interface operation state
|
|
PhysPortID *string // Interface unique physical port identifier within the NIC
|
|
PhysPortName *string // Interface physical port name within the NIC
|
|
PhysSwitchID *string // Unique physical switch identifier of a switch this port belongs to
|
|
QueueDisc string // Queueing discipline
|
|
Master *uint32 // Master device index (0 value un-enslaves)
|
|
Stats *LinkStats // Interface Statistics
|
|
Stats64 *LinkStats64 // Interface Statistics (64 bits version)
|
|
TxQueueLen *uint32 // Interface transmit queue len in number of packets
|
|
Type uint32 // Link type
|
|
XDP *LinkXDP // Express Data Patch Information
|
|
NetNS *NetNS // Interface network namespace
|
|
}
|
|
|
|
// OperationalState represents an interface's operational state.
|
|
type OperationalState uint8
|
|
|
|
// Constants that represent operational state of an interface
|
|
//
|
|
// Adapted from https://elixir.bootlin.com/linux/v4.19.2/source/include/uapi/linux/if.h#L166
|
|
const (
|
|
OperStateUnknown OperationalState = iota // status could not be determined
|
|
OperStateNotPresent // down, due to some missing component (typically hardware)
|
|
OperStateDown // down, either administratively or due to a fault
|
|
OperStateLowerLayerDown // down, due to lower-layer interfaces
|
|
OperStateTesting // operationally down, in some test mode
|
|
OperStateDormant // down, waiting for some external event
|
|
OperStateUp // interface is in a state to send and receive packets
|
|
)
|
|
|
|
// unmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
|
|
func (a *LinkAttributes) decode(ad *netlink.AttributeDecoder) error {
|
|
for ad.Next() {
|
|
switch ad.Type() {
|
|
case unix.IFLA_UNSPEC:
|
|
// unused attribute
|
|
case unix.IFLA_ADDRESS:
|
|
l := len(ad.Bytes())
|
|
if l < 4 || l > 32 {
|
|
return errInvalidLinkMessageAttr
|
|
}
|
|
a.Address = ad.Bytes()
|
|
case unix.IFLA_IFALIAS:
|
|
v := ad.String()
|
|
a.Alias = &v
|
|
case unix.IFLA_BROADCAST:
|
|
l := len(ad.Bytes())
|
|
if l < 4 || l > 32 {
|
|
return errInvalidLinkMessageAttr
|
|
}
|
|
a.Broadcast = ad.Bytes()
|
|
case unix.IFLA_CARRIER:
|
|
v := ad.Uint8()
|
|
a.Carrier = &v
|
|
case unix.IFLA_CARRIER_CHANGES:
|
|
v := ad.Uint32()
|
|
a.CarrierChanges = &v
|
|
case unix.IFLA_CARRIER_UP_COUNT:
|
|
v := ad.Uint32()
|
|
a.CarrierUpCount = &v
|
|
case unix.IFLA_CARRIER_DOWN_COUNT:
|
|
v := ad.Uint32()
|
|
a.CarrierDownCount = &v
|
|
case unix.IFLA_GROUP:
|
|
v := ad.Uint32()
|
|
a.NetDevGroup = &v
|
|
case unix.IFLA_MTU:
|
|
a.MTU = ad.Uint32()
|
|
case unix.IFLA_IFNAME:
|
|
a.Name = ad.String()
|
|
case unix.IFLA_LINK:
|
|
a.Type = ad.Uint32()
|
|
case unix.IFLA_LINKINFO:
|
|
a.Info = &LinkInfo{}
|
|
ad.Nested(a.Info.decode)
|
|
case unix.IFLA_LINKMODE:
|
|
v := ad.Uint8()
|
|
a.LinkMode = &v
|
|
case unix.IFLA_MASTER:
|
|
v := ad.Uint32()
|
|
a.Master = &v
|
|
case unix.IFLA_OPERSTATE:
|
|
a.OperationalState = OperationalState(ad.Uint8())
|
|
case unix.IFLA_PHYS_PORT_ID:
|
|
v := ad.String()
|
|
a.PhysPortID = &v
|
|
case unix.IFLA_PHYS_SWITCH_ID:
|
|
v := ad.String()
|
|
a.PhysSwitchID = &v
|
|
case unix.IFLA_PHYS_PORT_NAME:
|
|
v := ad.String()
|
|
a.PhysPortName = &v
|
|
case unix.IFLA_QDISC:
|
|
a.QueueDisc = ad.String()
|
|
case unix.IFLA_STATS:
|
|
a.Stats = &LinkStats{}
|
|
err := a.Stats.unmarshalBinary(ad.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case unix.IFLA_STATS64:
|
|
a.Stats64 = &LinkStats64{}
|
|
err := a.Stats64.unmarshalBinary(ad.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case unix.IFLA_TXQLEN:
|
|
v := ad.Uint32()
|
|
a.TxQueueLen = &v
|
|
case unix.IFLA_XDP:
|
|
a.XDP = &LinkXDP{}
|
|
ad.Nested(a.XDP.decode)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalBinary marshals a LinkAttributes into a byte slice.
|
|
func (a *LinkAttributes) encode(ae *netlink.AttributeEncoder) error {
|
|
if a.Name != "" {
|
|
ae.String(unix.IFLA_IFNAME, a.Name)
|
|
}
|
|
|
|
if a.Type != 0 {
|
|
ae.Uint32(unix.IFLA_LINK, a.Type)
|
|
}
|
|
|
|
if a.QueueDisc != "" {
|
|
ae.String(unix.IFLA_QDISC, a.QueueDisc)
|
|
}
|
|
|
|
if a.MTU != 0 {
|
|
ae.Uint32(unix.IFLA_MTU, a.MTU)
|
|
}
|
|
|
|
if len(a.Address) != 0 {
|
|
ae.Bytes(unix.IFLA_ADDRESS, a.Address)
|
|
}
|
|
|
|
if len(a.Broadcast) != 0 {
|
|
ae.Bytes(unix.IFLA_BROADCAST, a.Broadcast)
|
|
}
|
|
|
|
if a.OperationalState != OperStateUnknown {
|
|
ae.Uint8(unix.IFLA_OPERSTATE, uint8(a.OperationalState))
|
|
}
|
|
|
|
if a.Info != nil {
|
|
nae := netlink.NewAttributeEncoder()
|
|
nae.ByteOrder = ae.ByteOrder
|
|
|
|
err := a.Info.encode(nae)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b, err := nae.Encode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ae.Bytes(unix.IFLA_LINKINFO, b)
|
|
}
|
|
|
|
if a.XDP != nil {
|
|
nae := netlink.NewAttributeEncoder()
|
|
nae.ByteOrder = ae.ByteOrder
|
|
|
|
err := a.XDP.encode(nae)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b, err := nae.Encode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ae.Bytes(unix.IFLA_XDP, b)
|
|
}
|
|
|
|
if a.Master != nil {
|
|
ae.Uint32(unix.IFLA_MASTER, *a.Master)
|
|
}
|
|
|
|
if a.NetNS != nil {
|
|
ae.Uint32(a.NetNS.value())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LinkStats contains packet statistics
|
|
type LinkStats struct {
|
|
RXPackets uint32 // total packets received
|
|
TXPackets uint32 // total packets transmitted
|
|
RXBytes uint32 // total bytes received
|
|
TXBytes uint32 // total bytes transmitted
|
|
RXErrors uint32 // bad packets received
|
|
TXErrors uint32 // packet transmit problems
|
|
RXDropped uint32 // no space in linux buffers
|
|
TXDropped uint32 // no space available in linux
|
|
Multicast uint32 // multicast packets received
|
|
Collisions uint32
|
|
|
|
// detailed rx_errors:
|
|
RXLengthErrors uint32
|
|
RXOverErrors uint32 // receiver ring buff overflow
|
|
RXCRCErrors uint32 // recved pkt with crc error
|
|
RXFrameErrors uint32 // recv'd frame alignment error
|
|
RXFIFOErrors uint32 // recv'r fifo overrun
|
|
RXMissedErrors uint32 // receiver missed packet
|
|
|
|
// detailed tx_errors
|
|
TXAbortedErrors uint32
|
|
TXCarrierErrors uint32
|
|
TXFIFOErrors uint32
|
|
TXHeartbeatErrors uint32
|
|
TXWindowErrors uint32
|
|
|
|
// for cslip etc
|
|
RXCompressed uint32
|
|
TXCompressed uint32
|
|
|
|
RXNoHandler uint32 // dropped, no handler found
|
|
}
|
|
|
|
// unmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
|
|
func (a *LinkStats) unmarshalBinary(b []byte) error {
|
|
l := len(b)
|
|
if l != 92 && l != 96 {
|
|
return fmt.Errorf("incorrect LinkMessage size, want: 92 or 96, got: %d", len(b))
|
|
}
|
|
|
|
a.RXPackets = nativeEndian.Uint32(b[0:4])
|
|
a.TXPackets = nativeEndian.Uint32(b[4:8])
|
|
a.RXBytes = nativeEndian.Uint32(b[8:12])
|
|
a.TXBytes = nativeEndian.Uint32(b[12:16])
|
|
a.RXErrors = nativeEndian.Uint32(b[16:20])
|
|
a.TXErrors = nativeEndian.Uint32(b[20:24])
|
|
a.RXDropped = nativeEndian.Uint32(b[24:28])
|
|
a.TXDropped = nativeEndian.Uint32(b[28:32])
|
|
a.Multicast = nativeEndian.Uint32(b[32:36])
|
|
a.Collisions = nativeEndian.Uint32(b[36:40])
|
|
|
|
a.RXLengthErrors = nativeEndian.Uint32(b[40:44])
|
|
a.RXOverErrors = nativeEndian.Uint32(b[44:48])
|
|
a.RXCRCErrors = nativeEndian.Uint32(b[48:52])
|
|
a.RXFrameErrors = nativeEndian.Uint32(b[52:56])
|
|
a.RXFIFOErrors = nativeEndian.Uint32(b[56:60])
|
|
a.RXMissedErrors = nativeEndian.Uint32(b[60:64])
|
|
|
|
a.TXAbortedErrors = nativeEndian.Uint32(b[64:68])
|
|
a.TXCarrierErrors = nativeEndian.Uint32(b[68:72])
|
|
a.TXFIFOErrors = nativeEndian.Uint32(b[72:76])
|
|
a.TXHeartbeatErrors = nativeEndian.Uint32(b[76:80])
|
|
a.TXWindowErrors = nativeEndian.Uint32(b[80:84])
|
|
|
|
a.RXCompressed = nativeEndian.Uint32(b[84:88])
|
|
a.TXCompressed = nativeEndian.Uint32(b[88:92])
|
|
|
|
if l == 96 { // kernel 4.6+
|
|
a.RXNoHandler = nativeEndian.Uint32(b[92:96])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LinkStats64 contains packet statistics
|
|
type LinkStats64 struct {
|
|
RXPackets uint64 // total packets received
|
|
TXPackets uint64 // total packets transmitted
|
|
RXBytes uint64 // total bytes received
|
|
TXBytes uint64 // total bytes transmitted
|
|
RXErrors uint64 // bad packets received
|
|
TXErrors uint64 // packet transmit problems
|
|
RXDropped uint64 // no space in linux buffers
|
|
TXDropped uint64 // no space available in linux
|
|
Multicast uint64 // multicast packets received
|
|
Collisions uint64
|
|
|
|
// detailed rx_errors:
|
|
RXLengthErrors uint64
|
|
RXOverErrors uint64 // receiver ring buff overflow
|
|
RXCRCErrors uint64 // recved pkt with crc error
|
|
RXFrameErrors uint64 // recv'd frame alignment error
|
|
RXFIFOErrors uint64 // recv'r fifo overrun
|
|
RXMissedErrors uint64 // receiver missed packet
|
|
|
|
// detailed tx_errors
|
|
TXAbortedErrors uint64
|
|
TXCarrierErrors uint64
|
|
TXFIFOErrors uint64
|
|
TXHeartbeatErrors uint64
|
|
TXWindowErrors uint64
|
|
|
|
// for cslip etc
|
|
RXCompressed uint64
|
|
TXCompressed uint64
|
|
|
|
RXNoHandler uint64 // dropped, no handler found
|
|
|
|
RXOtherhostDropped uint64 // Number of packets dropped due to mismatch in destination MAC address.
|
|
}
|
|
|
|
// unmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
|
|
func (a *LinkStats64) unmarshalBinary(b []byte) error {
|
|
l := len(b)
|
|
if l != 184 && l != 192 && l != 200 {
|
|
return fmt.Errorf("incorrect size, want: 184 or 192 or 200")
|
|
}
|
|
|
|
a.RXPackets = nativeEndian.Uint64(b[0:8])
|
|
a.TXPackets = nativeEndian.Uint64(b[8:16])
|
|
a.RXBytes = nativeEndian.Uint64(b[16:24])
|
|
a.TXBytes = nativeEndian.Uint64(b[24:32])
|
|
a.RXErrors = nativeEndian.Uint64(b[32:40])
|
|
a.TXErrors = nativeEndian.Uint64(b[40:48])
|
|
a.RXDropped = nativeEndian.Uint64(b[48:56])
|
|
a.TXDropped = nativeEndian.Uint64(b[56:64])
|
|
a.Multicast = nativeEndian.Uint64(b[64:72])
|
|
a.Collisions = nativeEndian.Uint64(b[72:80])
|
|
|
|
a.RXLengthErrors = nativeEndian.Uint64(b[80:88])
|
|
a.RXOverErrors = nativeEndian.Uint64(b[88:96])
|
|
a.RXCRCErrors = nativeEndian.Uint64(b[96:104])
|
|
a.RXFrameErrors = nativeEndian.Uint64(b[104:112])
|
|
a.RXFIFOErrors = nativeEndian.Uint64(b[112:120])
|
|
a.RXMissedErrors = nativeEndian.Uint64(b[120:128])
|
|
|
|
a.TXAbortedErrors = nativeEndian.Uint64(b[128:136])
|
|
a.TXCarrierErrors = nativeEndian.Uint64(b[136:144])
|
|
a.TXFIFOErrors = nativeEndian.Uint64(b[144:152])
|
|
a.TXHeartbeatErrors = nativeEndian.Uint64(b[152:160])
|
|
a.TXWindowErrors = nativeEndian.Uint64(b[160:168])
|
|
|
|
a.RXCompressed = nativeEndian.Uint64(b[168:176])
|
|
a.TXCompressed = nativeEndian.Uint64(b[176:184])
|
|
|
|
if l > 191 { // kernel 4.6+
|
|
a.RXNoHandler = nativeEndian.Uint64(b[184:192])
|
|
}
|
|
|
|
if l > 199 { // kernel 5.19+
|
|
a.RXOtherhostDropped = nativeEndian.Uint64(b[192:200])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
// registeredDrivers is the global map of registered drivers
|
|
registeredDrivers = make(map[string]LinkDriver)
|
|
|
|
// registeredSlaveDrivers is the global map of registered slave drivers
|
|
registeredSlaveDrivers = make(map[string]LinkDriver)
|
|
)
|
|
|
|
// RegisterDriver registers a driver with the link service
|
|
// This allows the driver to be used to encode/decode the link data
|
|
//
|
|
// This function is not threadsafe. This should not be used after Dial
|
|
func RegisterDriver(d LinkDriver) error {
|
|
if _, ok := d.(LinkSlaveDriver); ok {
|
|
if _, ok := registeredSlaveDrivers[d.Kind()]; ok {
|
|
return fmt.Errorf("driver %s already registered", d.Kind())
|
|
}
|
|
registeredSlaveDrivers[d.Kind()] = d
|
|
return nil
|
|
}
|
|
if _, ok := registeredDrivers[d.Kind()]; ok {
|
|
return fmt.Errorf("driver %s already registered", d.Kind())
|
|
}
|
|
registeredDrivers[d.Kind()] = d
|
|
return nil
|
|
}
|
|
|
|
// getDriver returns the driver instance for the given kind, and true if the driver is registered
|
|
// it returns the default (LinkData) driver, and false if the driver is not registered
|
|
func getDriver(kind string, slave bool) (LinkDriver, bool) {
|
|
if slave {
|
|
if t, ok := registeredSlaveDrivers[kind]; ok {
|
|
return t.New(), true
|
|
}
|
|
|
|
return &LinkData{Name: kind, Slave: true}, false
|
|
}
|
|
if t, ok := registeredDrivers[kind]; ok {
|
|
return t.New(), true
|
|
}
|
|
|
|
return &LinkData{Name: kind}, false
|
|
}
|
|
|
|
// LinkDriver is the interface that wraps link-specific Encode, Decode, and Kind methods
|
|
type LinkDriver interface {
|
|
// New returns a new instance of the LinkDriver
|
|
New() LinkDriver
|
|
|
|
// Encode the driver data into the netlink message attribute
|
|
Encode(*netlink.AttributeEncoder) error
|
|
|
|
// Decode the driver data from the netlink message attribute
|
|
Decode(*netlink.AttributeDecoder) error
|
|
|
|
// Return the driver kind as string, this will be matched with the LinkInfo.Kind to find a driver to decode the data
|
|
Kind() string
|
|
}
|
|
|
|
// LinkSlaveDriver defines a LinkDriver with Slave method
|
|
type LinkSlaveDriver interface {
|
|
LinkDriver
|
|
|
|
// Slave method specifies driver is a slave link info
|
|
Slave()
|
|
}
|
|
|
|
// LinkDriverVerifier defines a LinkDriver with Verify method
|
|
type LinkDriverVerifier interface {
|
|
LinkDriver
|
|
|
|
// Verify function run before Encode function to check for correctness and
|
|
// pass related values that otherwise unavailable to the driver
|
|
Verify(*LinkMessage) error
|
|
}
|
|
|
|
// LinkData implements the default LinkDriver interface for not registered drivers
|
|
type LinkData struct {
|
|
Name string
|
|
Data []byte
|
|
Slave bool
|
|
}
|
|
|
|
var _ LinkDriver = &LinkData{}
|
|
|
|
func (d *LinkData) New() LinkDriver {
|
|
return &LinkData{}
|
|
}
|
|
|
|
func (d *LinkData) Decode(ad *netlink.AttributeDecoder) error {
|
|
d.Data = ad.Bytes()
|
|
return nil
|
|
}
|
|
|
|
func (d *LinkData) Encode(ae *netlink.AttributeEncoder) error {
|
|
if len(d.Data) == 0 {
|
|
return nil
|
|
}
|
|
if d.Slave {
|
|
ae.Bytes(unix.IFLA_INFO_SLAVE_DATA, d.Data)
|
|
} else {
|
|
ae.Bytes(unix.IFLA_INFO_DATA, d.Data)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *LinkData) Kind() string {
|
|
return d.Name
|
|
}
|
|
|
|
// LinkInfo contains data for specific network types
|
|
type LinkInfo struct {
|
|
Kind string // Driver name
|
|
Data LinkDriver // Driver specific configuration stored as nested Netlink messages
|
|
SlaveKind string // Slave driver name
|
|
SlaveData LinkDriver // Slave driver specific configuration
|
|
}
|
|
|
|
func (i *LinkInfo) decode(ad *netlink.AttributeDecoder) error {
|
|
for ad.Next() {
|
|
switch ad.Type() {
|
|
case unix.IFLA_INFO_KIND:
|
|
i.Kind = ad.String()
|
|
case unix.IFLA_INFO_SLAVE_KIND:
|
|
i.SlaveKind = ad.String()
|
|
case unix.IFLA_INFO_DATA:
|
|
driver, found := getDriver(i.Kind, false)
|
|
i.Data = driver
|
|
if found {
|
|
ad.Nested(i.Data.Decode)
|
|
continue
|
|
}
|
|
_ = i.Data.Decode(ad)
|
|
case unix.IFLA_INFO_SLAVE_DATA:
|
|
driver, found := getDriver(i.SlaveKind, true)
|
|
i.SlaveData = driver
|
|
if found {
|
|
ad.Nested(i.SlaveData.Decode)
|
|
continue
|
|
}
|
|
_ = i.SlaveData.Decode(ad)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *LinkInfo) encode(ae *netlink.AttributeEncoder) error {
|
|
ae.String(unix.IFLA_INFO_KIND, i.Kind)
|
|
if i.Data != nil {
|
|
if i.Kind != i.Data.Kind() {
|
|
return fmt.Errorf("driver kind %s is not equal to info kind %s", i.Data.Kind(), i.Kind)
|
|
}
|
|
if _, ok := i.Data.(*LinkData); ok {
|
|
_ = i.Data.Encode(ae)
|
|
} else {
|
|
ae.Nested(unix.IFLA_INFO_DATA, i.Data.Encode)
|
|
}
|
|
}
|
|
if i.SlaveData != nil {
|
|
if i.SlaveKind != i.SlaveData.Kind() {
|
|
return fmt.Errorf("slave driver kind %s is not equal to slave info kind %s", i.SlaveData.Kind(), i.SlaveKind)
|
|
}
|
|
ae.String(unix.IFLA_INFO_SLAVE_KIND, i.SlaveKind)
|
|
if _, ok := i.SlaveData.(*LinkData); ok {
|
|
_ = i.SlaveData.Encode(ae)
|
|
} else {
|
|
ae.Nested(unix.IFLA_INFO_SLAVE_DATA, i.SlaveData.Encode)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LinkXDP holds Express Data Path specific information
|
|
type LinkXDP struct {
|
|
FD int32
|
|
ExpectedFD int32
|
|
Attached uint8
|
|
Flags uint32
|
|
ProgID uint32
|
|
}
|
|
|
|
func (xdp *LinkXDP) decode(ad *netlink.AttributeDecoder) error {
|
|
for ad.Next() {
|
|
switch ad.Type() {
|
|
case unix.IFLA_XDP_FD:
|
|
xdp.FD = ad.Int32()
|
|
case unix.IFLA_XDP_EXPECTED_FD:
|
|
xdp.ExpectedFD = ad.Int32()
|
|
case unix.IFLA_XDP_ATTACHED:
|
|
xdp.Attached = ad.Uint8()
|
|
case unix.IFLA_XDP_FLAGS:
|
|
xdp.Flags = ad.Uint32()
|
|
case unix.IFLA_XDP_PROG_ID:
|
|
xdp.ProgID = ad.Uint32()
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (xdp *LinkXDP) encode(ae *netlink.AttributeEncoder) error {
|
|
ae.Int32(unix.IFLA_XDP_FD, xdp.FD)
|
|
ae.Int32(unix.IFLA_XDP_EXPECTED_FD, xdp.ExpectedFD)
|
|
ae.Uint32(unix.IFLA_XDP_FLAGS, xdp.Flags)
|
|
// XDP_ATTACHED and XDP_PROG_ID are things that can only be returned by the
|
|
// kernel, so we don't encode them. source:
|
|
// https://elixir.bootlin.com/linux/v5.10.15/source/net/core/rtnetlink.c#L2894
|
|
return nil
|
|
}
|