mirror of
https://github.com/jsimonetti/rtnetlink.git
synced 2026-03-31 01:41:31 +02:00
* Add attr validation This commit removes unix.IFLA_UNSPEC and introduces checks for the interface name, the link type and the queue disc fields. Interface name validation is necessary to prevent an 'invalid argument' error when creating a link with an empty name. Other checks were added to be consistent with the ip tools. Signed-off-by: Birol Bilgin <birolbilgin@gmail.com> * Add network namespace type This commit introduces the NetNS struct and integrates network namespace capabilities into link attributes. This enhancement facilitates the creation of links, such as veth pairs, across different network namespaces without the need to execute directly within those namespaces. Signed-off-by: Birol Bilgin <birolbilgin@gmail.com> * Add LinkDriver interface This commit introduces a Driver interface and changes Data and SlaveData fields within the LinkInfo struct as LinkDriver to accommodate driver-specific data encoding, addressing the limitation where LinkInfo.Data and SlaveData fields were merely byte slices without support for specific data encoding. Drivers are registered globally with the RegisterDriver function. For un-registered drivers, the default LinkData driver is used. Signed-off-by: Birol Bilgin <birolbilgin@gmail.com> * Add Driver Package This commit introduces the 'driver' package, which contains specific implementations of the LinkDriver interface. It also includes the implementation of the Linux bond driver as LinkDriver and the bond slave driver as LinkSlaveDriver. Signed-off-by: Birol Bilgin <birolbilgin@gmail.com> * Add Netkit and Veth drivers This commit adds Netkit and Veth drivers. Signed-off-by: Birol Bilgin <birolbilgin@gmail.com> --------- Signed-off-by: Birol Bilgin <birolbilgin@gmail.com>
779 lines
22 KiB
Go
779 lines
22 KiB
Go
package rtnetlink
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/jsimonetti/rtnetlink/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.Type(), 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 only can return from the kernel,
|
|
// not be send, so we don't encode them.
|
|
// source: https://elixir.bootlin.com/linux/v5.10.15/source/net/core/rtnetlink.c#L2894
|
|
return nil
|
|
}
|