| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- 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 often populated with netlink attributes. For easy encoding and
- // decoding of attributes, see the AttributeDecoder and AttributeEncoder types.
- 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
- }
|