271 lines
7.0 KiB
Go
271 lines
7.0 KiB
Go
|
package netlink
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/mdlayher/netlink/nlenc"
|
||
|
)
|
||
|
|
||
|
// Flags which may apply to netlink attribute types when communicating with
|
||
|
// certain netlink families.
|
||
|
const (
|
||
|
Nested uint16 = 0x8000
|
||
|
NetByteOrder uint16 = 0x4000
|
||
|
|
||
|
// attrTypeMask masks off Type bits used for the above flags.
|
||
|
attrTypeMask uint16 = 0x3fff
|
||
|
)
|
||
|
|
||
|
// 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.
|
||
|
|
||
|
// Request indicates a request to netlink.
|
||
|
Request HeaderFlags = 1
|
||
|
|
||
|
// Multi indicates a multi-part message, terminated by Done on the
|
||
|
// last message.
|
||
|
Multi HeaderFlags = 2
|
||
|
|
||
|
// Acknowledge requests that netlink reply with an acknowledgement
|
||
|
// using Error and, if needed, an error code.
|
||
|
Acknowledge HeaderFlags = 4
|
||
|
|
||
|
// Echo requests that netlink echo this request back to the sender.
|
||
|
Echo HeaderFlags = 8
|
||
|
|
||
|
// DumpInterrupted indicates that a dump was inconsistent due to a
|
||
|
// sequence change.
|
||
|
DumpInterrupted HeaderFlags = 16
|
||
|
|
||
|
// DumpFiltered indicates that a dump was filtered as requested.
|
||
|
DumpFiltered HeaderFlags = 32
|
||
|
|
||
|
// Flags used to retrieve data from netlink.
|
||
|
|
||
|
// Root requests that netlink return a complete table instead of a
|
||
|
// single entry.
|
||
|
Root HeaderFlags = 0x100
|
||
|
|
||
|
// Match requests that netlink return a list of all matching entries.
|
||
|
Match HeaderFlags = 0x200
|
||
|
|
||
|
// Atomic requests that netlink send an atomic snapshot of its entries.
|
||
|
// Requires CAP_NET_ADMIN or an effective UID of 0.
|
||
|
Atomic HeaderFlags = 0x400
|
||
|
|
||
|
// Dump requests that netlink return a complete list of all entries.
|
||
|
Dump HeaderFlags = Root | Match
|
||
|
|
||
|
// Flags used to create objects.
|
||
|
|
||
|
// Replace indicates request replaces an existing matching object.
|
||
|
Replace HeaderFlags = 0x100
|
||
|
|
||
|
// Excl indicates request does not replace the object if it already exists.
|
||
|
Excl HeaderFlags = 0x200
|
||
|
|
||
|
// Create indicates request creates an object if it doesn't already exist.
|
||
|
Create HeaderFlags = 0x400
|
||
|
|
||
|
// Append indicates request adds to the end of the object list.
|
||
|
Append HeaderFlags = 0x800
|
||
|
)
|
||
|
|
||
|
// String returns the string representation of a HeaderFlags.
|
||
|
func (f HeaderFlags) String() string {
|
||
|
names := []string{
|
||
|
"request",
|
||
|
"multi",
|
||
|
"acknowledge",
|
||
|
"echo",
|
||
|
"dumpinterrupted",
|
||
|
"dumpfiltered",
|
||
|
}
|
||
|
|
||
|
var s string
|
||
|
|
||
|
left := uint(f)
|
||
|
|
||
|
for i, name := range names {
|
||
|
if f&(1<<uint(i)) != 0 {
|
||
|
if s != "" {
|
||
|
s += "|"
|
||
|
}
|
||
|
|
||
|
s += name
|
||
|
|
||
|
left ^= (1 << uint(i))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if s == "" && left == 0 {
|
||
|
s = "0"
|
||
|
}
|
||
|
|
||
|
if left > 0 {
|
||
|
if s != "" {
|
||
|
s += "|"
|
||
|
}
|
||
|
s += fmt.Sprintf("%#x", left)
|
||
|
}
|
||
|
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// HeaderType specifies the type of a Header.
|
||
|
type HeaderType uint16
|
||
|
|
||
|
const (
|
||
|
// Noop indicates that no action was taken.
|
||
|
Noop HeaderType = 0x1
|
||
|
|
||
|
// Error indicates an error code is present, which is also used to indicate
|
||
|
// success when the code is 0.
|
||
|
Error HeaderType = 0x2
|
||
|
|
||
|
// Done indicates the end of a multi-part message.
|
||
|
Done HeaderType = 0x3
|
||
|
|
||
|
// Overrun indicates that data was lost from this message.
|
||
|
Overrun HeaderType = 0x4
|
||
|
)
|
||
|
|
||
|
// String returns the string representation of a HeaderType.
|
||
|
func (t HeaderType) String() string {
|
||
|
switch t {
|
||
|
case Noop:
|
||
|
return "noop"
|
||
|
case Error:
|
||
|
return "error"
|
||
|
case Done:
|
||
|
return "done"
|
||
|
case Overrun:
|
||
|
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 {
|
||
|
// NB: All non-nil errors returned from this function *must* be of type
|
||
|
// OpError in order to maintain the appropriate contract with callers of
|
||
|
// this package.
|
||
|
|
||
|
const success = 0
|
||
|
|
||
|
// Per libnl documentation, only messages that indicate type error can
|
||
|
// contain error codes:
|
||
|
// https://www.infradead.org/~tgr/libnl/doc/core.html#core_errmsg.
|
||
|
//
|
||
|
// However, at one point, this package checked both done and error for
|
||
|
// error codes. Because there was no issue associated with the change,
|
||
|
// it is unknown whether this change was correct or not. If you run into
|
||
|
// a problem with your application because of this change, please file
|
||
|
// an issue.
|
||
|
if m.Header.Type != Error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if len(m.Data) < 4 {
|
||
|
return newOpError("receive", errShortErrorMessage)
|
||
|
}
|
||
|
|
||
|
if c := nlenc.Int32(m.Data[0:4]); c != success {
|
||
|
// Error code is a negative integer, convert it into an OS-specific raw
|
||
|
// system call error, but do not wrap with os.NewSyscallError to signify
|
||
|
// that this error was produced by a netlink message; not a system call.
|
||
|
return newOpError("receive", newError(-1*int(c)))
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|