232 lines
5.8 KiB
Go
232 lines
5.8 KiB
Go
package netlink
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/mdlayher/netlink/nlenc"
|
|
)
|
|
|
|
// Various errors which may occur when attempting to marshal or unmarshal
|
|
// a Message to and from its binary form.
|
|
var (
|
|
errIncorrectMessageLength = errors.New("netlink message header length incorrect")
|
|
errShortMessage = errors.New("not enough data to create a netlink message")
|
|
errUnalignedMessage = errors.New("input data is not properly aligned for netlink message")
|
|
)
|
|
|
|
// HeaderFlags specify flags which may be present in a Header.
|
|
type HeaderFlags uint16
|
|
|
|
const (
|
|
// General netlink communication flags.
|
|
|
|
// HeaderFlagsRequest indicates a request to netlink.
|
|
HeaderFlagsRequest HeaderFlags = 1
|
|
|
|
// HeaderFlagsMulti indicates a multi-part message, terminated
|
|
// by HeaderTypeDone on the last message.
|
|
HeaderFlagsMulti HeaderFlags = 2
|
|
|
|
// HeaderFlagsAcknowledge requests that netlink reply with
|
|
// an acknowledgement using HeaderTypeError and, if needed,
|
|
// an error code.
|
|
HeaderFlagsAcknowledge HeaderFlags = 4
|
|
|
|
// HeaderFlagsEcho requests that netlink echo this request
|
|
// back to the sender.
|
|
HeaderFlagsEcho HeaderFlags = 8
|
|
|
|
// HeaderFlagsDumpInterrupted indicates that a dump was
|
|
// inconsistent due to a sequence change.
|
|
HeaderFlagsDumpInterrupted HeaderFlags = 16
|
|
|
|
// HeaderFlagsDumpFiltered indicates that a dump was filtered
|
|
// as requested.
|
|
HeaderFlagsDumpFiltered HeaderFlags = 32
|
|
|
|
// Flags used to retrieve data from netlink.
|
|
|
|
// HeaderFlagsRoot requests that netlink return a complete table instead
|
|
// of a single entry.
|
|
HeaderFlagsRoot HeaderFlags = 0x100
|
|
|
|
// HeaderFlagsMatch requests that netlink return a list of all matching
|
|
// entries.
|
|
HeaderFlagsMatch HeaderFlags = 0x200
|
|
|
|
// HeaderFlagsAtomic requests that netlink send an atomic snapshot of
|
|
// its entries. Requires CAP_NET_ADMIN or an effective UID of 0.
|
|
HeaderFlagsAtomic HeaderFlags = 0x400
|
|
|
|
// HeaderFlagsDump requests that netlink return a complete list of
|
|
// all entries.
|
|
HeaderFlagsDump HeaderFlags = HeaderFlagsRoot | HeaderFlagsMatch
|
|
)
|
|
|
|
// String returns the string representation of a HeaderFlags.
|
|
func (f HeaderFlags) String() string {
|
|
names := []string{
|
|
"request",
|
|
"multi",
|
|
"acknowledge",
|
|
"echo",
|
|
"dumpinterrupted",
|
|
"dumpfiltered",
|
|
"1<<6",
|
|
"1<<7",
|
|
"root",
|
|
"match",
|
|
"atomic",
|
|
}
|
|
|
|
var s string
|
|
for i, name := range names {
|
|
if f&(1<<uint(i)) != 0 {
|
|
if s != "" {
|
|
s += "|"
|
|
}
|
|
|
|
s += name
|
|
}
|
|
}
|
|
|
|
if s == "" {
|
|
s = "0"
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// HeaderType specifies the type of a Header.
|
|
type HeaderType uint16
|
|
|
|
const (
|
|
// HeaderTypeNoop indicates that no action was taken.
|
|
HeaderTypeNoop HeaderType = 0x1
|
|
|
|
// HeaderTypeError indicates an error code is present, which is also
|
|
// used to indicate success when the code is 0.
|
|
HeaderTypeError HeaderType = 0x2
|
|
|
|
// HeaderTypeDone indicates the end of a multi-part message.
|
|
HeaderTypeDone HeaderType = 0x3
|
|
|
|
// HeaderTypeOverrun indicates that data was lost from this message.
|
|
HeaderTypeOverrun HeaderType = 0x4
|
|
)
|
|
|
|
// String returns the string representation of a HeaderType.
|
|
func (t HeaderType) String() string {
|
|
switch t {
|
|
case HeaderTypeNoop:
|
|
return "noop"
|
|
case HeaderTypeError:
|
|
return "error"
|
|
case HeaderTypeDone:
|
|
return "done"
|
|
case HeaderTypeOverrun:
|
|
return "overrun"
|
|
default:
|
|
return fmt.Sprintf("unknown(%d)", t)
|
|
}
|
|
}
|
|
|
|
// NB: the memory layout of Header and Linux's syscall.NlMsgHdr must be
|
|
// exactly the same. Cannot reorder, change data type, add, or remove fields.
|
|
// Named types of the same size (e.g. HeaderFlags is a uint16) are okay.
|
|
|
|
// A Header is a netlink header. A Header is sent and received with each
|
|
// Message to indicate metadata regarding a Message.
|
|
type Header struct {
|
|
// Length of a Message, including this Header.
|
|
Length uint32
|
|
|
|
// Contents of a Message.
|
|
Type HeaderType
|
|
|
|
// Flags which may be used to modify a request or response.
|
|
Flags HeaderFlags
|
|
|
|
// The sequence number of a Message.
|
|
Sequence uint32
|
|
|
|
// The process ID of the sending process.
|
|
PID uint32
|
|
}
|
|
|
|
// A Message is a netlink message. It contains a Header and an arbitrary
|
|
// byte payload, which may be decoded using information from the Header.
|
|
//
|
|
// Data is encoded in the native endianness of the host system. For easier
|
|
// of encoding and decoding of integers, use package nlenc.
|
|
type Message struct {
|
|
Header Header
|
|
Data []byte
|
|
}
|
|
|
|
// MarshalBinary marshals a Message into a byte slice.
|
|
func (m Message) MarshalBinary() ([]byte, error) {
|
|
ml := nlmsgAlign(int(m.Header.Length))
|
|
if ml < nlmsgHeaderLen || ml != int(m.Header.Length) {
|
|
return nil, errIncorrectMessageLength
|
|
}
|
|
|
|
b := make([]byte, ml)
|
|
|
|
nlenc.PutUint32(b[0:4], m.Header.Length)
|
|
nlenc.PutUint16(b[4:6], uint16(m.Header.Type))
|
|
nlenc.PutUint16(b[6:8], uint16(m.Header.Flags))
|
|
nlenc.PutUint32(b[8:12], m.Header.Sequence)
|
|
nlenc.PutUint32(b[12:16], m.Header.PID)
|
|
copy(b[16:], m.Data)
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// UnmarshalBinary unmarshals the contents of a byte slice into a Message.
|
|
func (m *Message) UnmarshalBinary(b []byte) error {
|
|
if len(b) < nlmsgHeaderLen {
|
|
return errShortMessage
|
|
}
|
|
if len(b) != nlmsgAlign(len(b)) {
|
|
return errUnalignedMessage
|
|
}
|
|
|
|
// Don't allow misleading length
|
|
m.Header.Length = nlenc.Uint32(b[0:4])
|
|
if int(m.Header.Length) != len(b) {
|
|
return errShortMessage
|
|
}
|
|
|
|
m.Header.Type = HeaderType(nlenc.Uint16(b[4:6]))
|
|
m.Header.Flags = HeaderFlags(nlenc.Uint16(b[6:8]))
|
|
m.Header.Sequence = nlenc.Uint32(b[8:12])
|
|
m.Header.PID = nlenc.Uint32(b[12:16])
|
|
m.Data = b[16:]
|
|
|
|
return nil
|
|
}
|
|
|
|
// checkMessage checks a single Message for netlink errors.
|
|
func checkMessage(m Message) error {
|
|
const success = 0
|
|
|
|
// HeaderTypeError may indicate an error code, or success
|
|
if m.Header.Type != HeaderTypeError {
|
|
return nil
|
|
}
|
|
|
|
if len(m.Data) < 4 {
|
|
return errShortErrorMessage
|
|
}
|
|
|
|
if c := nlenc.Int32(m.Data[0:4]); c != success {
|
|
// Error code is a negative integer, convert it into
|
|
// an OS-specific system call error
|
|
return newError(-1 * int(c))
|
|
}
|
|
|
|
return nil
|
|
}
|