mirror of
https://github.com/jsimonetti/rtnetlink.git
synced 2026-04-15 09:11:08 +02:00
* L3 Interfaces do not have hardware addresses * tests: L3 Interfaces do not have hardware addresses
534 lines
12 KiB
Go
534 lines
12 KiB
Go
package rtnetlink
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/mdlayher/netlink"
|
|
"github.com/mdlayher/netlink/nlenc"
|
|
)
|
|
|
|
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, reserved for future use and should
|
|
// always be 0xffffffff
|
|
Change uint32
|
|
|
|
// Attributes List
|
|
Attributes LinkAttributes
|
|
}
|
|
|
|
const linkMessageLength = 16
|
|
|
|
// MarshalBinary marshals a LinkMessage into a byte slice.
|
|
func (m *LinkMessage) MarshalBinary() ([]byte, error) {
|
|
b := make([]byte, linkMessageLength)
|
|
|
|
b[0] = 0 //Family
|
|
b[1] = 0 //reserved
|
|
nlenc.PutUint16(b[2:4], m.Type)
|
|
nlenc.PutUint32(b[4:8], m.Index)
|
|
nlenc.PutUint32(b[8:12], m.Flags)
|
|
nlenc.PutUint32(b[12:16], 0) //Change, reserved
|
|
|
|
a, err := m.Attributes.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return append(b, a...), nil
|
|
}
|
|
|
|
// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
|
|
func (m *LinkMessage) UnmarshalBinary(b []byte) error {
|
|
l := len(b)
|
|
if l < linkMessageLength {
|
|
return errInvalidLinkMessage
|
|
}
|
|
|
|
m.Family = nlenc.Uint16(b[0:2])
|
|
m.Type = nlenc.Uint16(b[2:4])
|
|
m.Index = nlenc.Uint32(b[4:8])
|
|
m.Flags = nlenc.Uint32(b[8:12])
|
|
m.Change = nlenc.Uint32(b[12:16])
|
|
|
|
if l > linkMessageLength {
|
|
m.Attributes = LinkAttributes{}
|
|
err := m.Attributes.UnmarshalBinary(b[16:])
|
|
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
|
|
}
|
|
|
|
// Constants used to request information from rtnetlink links.
|
|
const (
|
|
RTNLGRP_LINK = 0x1
|
|
|
|
RTM_NEWLINK = 0x10
|
|
RTM_DELLINK = 0x11
|
|
RTM_GETLINK = 0x12
|
|
RTM_SETLINK = 0x13
|
|
)
|
|
|
|
// 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.c.Execute(req, RTM_NEWLINK, flags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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, RTM_DELLINK, flags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Get retrieves interface information by index.
|
|
func (l *LinkService) Get(index uint32) (LinkMessage, error) {
|
|
req := &LinkMessage{
|
|
Index: index,
|
|
}
|
|
|
|
flags := netlink.Request | netlink.DumpFiltered
|
|
msg, err := l.c.Execute(req, RTM_GETLINK, flags)
|
|
if err != nil {
|
|
return LinkMessage{}, err
|
|
}
|
|
|
|
if len(msg) != 1 {
|
|
return LinkMessage{}, fmt.Errorf("too many/little matches, expected 1")
|
|
}
|
|
|
|
link := (msg[0]).(*LinkMessage)
|
|
return *link, nil
|
|
}
|
|
|
|
// Set sets interface attributes according to the LinkMessage information.
|
|
func (l *LinkService) Set(req *LinkMessage) error {
|
|
flags := netlink.Request | netlink.Acknowledge
|
|
_, err := l.c.Execute(req, RTM_SETLINK, flags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// List retrieves all interfaces.
|
|
func (l *LinkService) List() ([]LinkMessage, error) {
|
|
req := &LinkMessage{}
|
|
|
|
flags := netlink.Request | netlink.Dump
|
|
msgs, err := l.c.Execute(req, RTM_GETLINK, flags)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
links := make([]LinkMessage, 0, len(msgs))
|
|
for _, m := range msgs {
|
|
link := (m).(*LinkMessage)
|
|
links = append(links, *link)
|
|
}
|
|
|
|
return links, nil
|
|
}
|
|
|
|
// LinkAttributes contains all attributes for an interface.
|
|
type LinkAttributes struct {
|
|
Address net.HardwareAddr // Interface L2 address
|
|
Broadcast net.HardwareAddr // L2 broadcast address
|
|
Name string // Device name
|
|
MTU uint32 // MTU of the device
|
|
Type uint32 // Link type
|
|
QueueDisc string // Queueing discipline
|
|
OperationalState OperationalState // Interface operation state
|
|
Stats *LinkStats // Interface Statistics
|
|
Info *LinkInfo // Detailed Interface Information
|
|
}
|
|
|
|
// 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
|
|
)
|
|
|
|
// Attribute IDs mapped to specific LinkAttribute fields.
|
|
const (
|
|
iflaUnspec uint16 = iota
|
|
iflaAddress
|
|
iflaBroadcast
|
|
iflaIfname
|
|
iflaMTU
|
|
iflaLink
|
|
iflaQdisc
|
|
iflaStats
|
|
iflaCost
|
|
iflaPriority
|
|
iflaMaster
|
|
iflaWireless
|
|
iflaProtInfo
|
|
iflaTXQLen
|
|
iflaMap
|
|
iflaWeight
|
|
iflaOperState
|
|
iflaLinkMode
|
|
iflaLinkInfo
|
|
iflaNetNSPid
|
|
iflaIFAlias
|
|
iflaNumVF
|
|
iflaVFInfoList
|
|
iflaStats64
|
|
iflaVFPorts
|
|
iflaPortSelf
|
|
iflaAFSpec
|
|
iflaGroup
|
|
iflaNetNSFD
|
|
iflaExtMask
|
|
iflaPromiscuity
|
|
iflaNumTXQueues
|
|
iflaNumRXQueues
|
|
iflaCarrier
|
|
iflaPhysPortID
|
|
iflaCarrierChanges
|
|
iflaPhysSwitchID
|
|
iflaProtoDown
|
|
iflaGSOMaxSegs
|
|
iflaGSOMaxSize
|
|
iflaPad
|
|
iflaXDP
|
|
iflaEvent
|
|
iflaNewNetNSID
|
|
iflaIfNetNSID
|
|
iflaCarrierUpCount
|
|
iflaCarrierDownCount
|
|
iflaNewIFIndex
|
|
)
|
|
|
|
// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
|
|
func (a *LinkAttributes) UnmarshalBinary(b []byte) error {
|
|
attrs, err := netlink.UnmarshalAttributes(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, attr := range attrs {
|
|
switch attr.Type {
|
|
case iflaUnspec:
|
|
//unused attribute
|
|
case iflaAddress:
|
|
l := len(attr.Data)
|
|
if l < 4 || l > 32 {
|
|
return errInvalidLinkMessageAttr
|
|
}
|
|
a.Address = attr.Data
|
|
case iflaBroadcast:
|
|
l := len(attr.Data)
|
|
if l < 4 || l > 32 {
|
|
return errInvalidLinkMessageAttr
|
|
}
|
|
a.Broadcast = attr.Data
|
|
case iflaIfname:
|
|
a.Name = nlenc.String(attr.Data)
|
|
case iflaMTU:
|
|
if len(attr.Data) != 4 {
|
|
return errInvalidLinkMessageAttr
|
|
}
|
|
a.MTU = nlenc.Uint32(attr.Data)
|
|
case iflaLink:
|
|
if len(attr.Data) != 4 {
|
|
return errInvalidLinkMessageAttr
|
|
}
|
|
a.Type = nlenc.Uint32(attr.Data)
|
|
case iflaQdisc:
|
|
a.QueueDisc = nlenc.String(attr.Data)
|
|
case iflaOperState:
|
|
a.OperationalState = OperationalState(nlenc.Uint8(attr.Data))
|
|
case iflaStats:
|
|
a.Stats = &LinkStats{}
|
|
err := a.Stats.UnmarshalBinary(attr.Data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case iflaLinkInfo:
|
|
a.Info = &LinkInfo{}
|
|
err := a.Info.UnmarshalBinary(attr.Data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalBinary marshals a LinkAttributes into a byte slice.
|
|
func (a *LinkAttributes) MarshalBinary() ([]byte, error) {
|
|
attrs := []netlink.Attribute{
|
|
{
|
|
Type: iflaUnspec,
|
|
Data: nlenc.Uint16Bytes(0),
|
|
},
|
|
{
|
|
Type: iflaIfname,
|
|
Data: nlenc.Bytes(a.Name),
|
|
},
|
|
{
|
|
Type: iflaMTU,
|
|
Data: nlenc.Uint32Bytes(a.MTU),
|
|
},
|
|
{
|
|
Type: iflaLink,
|
|
Data: nlenc.Uint32Bytes(a.Type),
|
|
},
|
|
{
|
|
Type: iflaQdisc,
|
|
Data: nlenc.Bytes(a.QueueDisc),
|
|
},
|
|
/*
|
|
{
|
|
Type: iflaStats,
|
|
Data: nlenc.Bytes(name),
|
|
},
|
|
*/
|
|
}
|
|
|
|
if len(a.Address) != 0 {
|
|
attrs = append(attrs, netlink.Attribute{
|
|
Type: iflaAddress,
|
|
Data: a.Address,
|
|
})
|
|
}
|
|
|
|
if len(a.Broadcast) != 0 {
|
|
attrs = append(attrs, netlink.Attribute{
|
|
Type: iflaBroadcast,
|
|
Data: a.Broadcast,
|
|
})
|
|
}
|
|
|
|
if a.OperationalState != OperStateUnknown {
|
|
attrs = append(attrs, netlink.Attribute{
|
|
Type: iflaOperState,
|
|
Data: nlenc.Uint8Bytes(uint8(a.OperationalState)),
|
|
})
|
|
}
|
|
|
|
if a.Info != nil {
|
|
info, err := a.Info.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
attrs = append(attrs, netlink.Attribute{
|
|
Type: iflaLinkInfo,
|
|
Data: info,
|
|
})
|
|
}
|
|
|
|
return netlink.MarshalAttributes(attrs)
|
|
}
|
|
|
|
//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 {
|
|
if len(b) != 96 && len(b) != 104 {
|
|
return fmt.Errorf("incorrect size, want: 96 or 104, got: %d", len(b))
|
|
}
|
|
|
|
a.RXPackets = nlenc.Uint32(b[0:4])
|
|
a.TXPackets = nlenc.Uint32(b[4:8])
|
|
a.RXBytes = nlenc.Uint32(b[8:12])
|
|
a.TXBytes = nlenc.Uint32(b[12:16])
|
|
a.RXErrors = nlenc.Uint32(b[16:20])
|
|
a.TXErrors = nlenc.Uint32(b[20:24])
|
|
a.RXDropped = nlenc.Uint32(b[24:28])
|
|
a.TXDropped = nlenc.Uint32(b[28:32])
|
|
a.Multicast = nlenc.Uint32(b[32:36])
|
|
a.Collisions = nlenc.Uint32(b[36:40])
|
|
|
|
a.RXLengthErrors = nlenc.Uint32(b[40:44])
|
|
a.RXOverErrors = nlenc.Uint32(b[44:48])
|
|
a.RXCRCErrors = nlenc.Uint32(b[48:52])
|
|
a.RXFrameErrors = nlenc.Uint32(b[52:56])
|
|
a.RXFIFOErrors = nlenc.Uint32(b[56:60])
|
|
a.RXMissedErrors = nlenc.Uint32(b[60:64])
|
|
|
|
a.TXAbortedErrors = nlenc.Uint32(b[68:72])
|
|
a.TXCarrierErrors = nlenc.Uint32(b[76:80])
|
|
a.TXFIFOErrors = nlenc.Uint32(b[80:84])
|
|
a.TXHeartbeatErrors = nlenc.Uint32(b[84:88])
|
|
a.TXWindowErrors = nlenc.Uint32(b[88:92])
|
|
|
|
if len(b) == 96 {
|
|
a.RXNoHandler = nlenc.Uint32(b[92:96])
|
|
}
|
|
|
|
if len(b) == 104 {
|
|
a.RXCompressed = nlenc.Uint32(b[92:96])
|
|
a.TXCompressed = nlenc.Uint32(b[96:100])
|
|
a.RXNoHandler = nlenc.Uint32(b[100:104])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
iflaInfoUnspec uint16 = iota
|
|
iflaInfoKind
|
|
iflaInfoData
|
|
iflaInfoSlaveKind
|
|
iflaInfoSlaveData
|
|
)
|
|
|
|
// LinkInfo contains data for specific network types
|
|
type LinkInfo struct {
|
|
Kind string // Driver name
|
|
Data []byte // Driver specific configuration stored as nested Netlink messages
|
|
SlaveKind string // Slave driver name
|
|
SlaveData []byte // Slave driver specific configuration
|
|
}
|
|
|
|
// UnmarshalBinary unmarshals the contents of a byte slice into a LinkInfo.
|
|
func (i *LinkInfo) UnmarshalBinary(b []byte) error {
|
|
attrs, err := netlink.UnmarshalAttributes(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, attr := range attrs {
|
|
switch attr.Type {
|
|
case iflaInfoKind:
|
|
i.Kind = nlenc.String(attr.Data)
|
|
case iflaInfoSlaveKind:
|
|
i.SlaveKind = nlenc.String(attr.Data)
|
|
case iflaInfoData:
|
|
i.Data = attr.Data
|
|
case iflaInfoSlaveData:
|
|
i.SlaveData = attr.Data
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalBinary marshals a LinkInfo into a byte slice.
|
|
func (i *LinkInfo) MarshalBinary() ([]byte, error) {
|
|
attrs := []netlink.Attribute{
|
|
{
|
|
Type: iflaInfoKind,
|
|
Data: nlenc.Bytes(i.Kind),
|
|
},
|
|
{
|
|
Type: iflaInfoData,
|
|
Data: i.Data,
|
|
},
|
|
}
|
|
|
|
if len(i.SlaveData) > 0 {
|
|
attrs = append(attrs,
|
|
netlink.Attribute{
|
|
Type: iflaInfoSlaveKind,
|
|
Data: nlenc.Bytes(i.SlaveKind),
|
|
},
|
|
netlink.Attribute{
|
|
Type: iflaInfoSlaveData,
|
|
Data: i.SlaveData,
|
|
},
|
|
)
|
|
}
|
|
|
|
return netlink.MarshalAttributes(attrs)
|
|
}
|