525 lines
13 KiB
Go
525 lines
13 KiB
Go
|
package netlink
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/mdlayher/netlink/nlenc"
|
||
|
)
|
||
|
|
||
|
// errInvalidAttribute specifies if an Attribute's length is incorrect.
|
||
|
var errInvalidAttribute = errors.New("invalid attribute; length too short or too large")
|
||
|
|
||
|
// An Attribute is a netlink attribute. Attributes are packed and unpacked
|
||
|
// to and from the Data field of Message for some netlink families.
|
||
|
type Attribute struct {
|
||
|
// Length of an Attribute, including this field and Type.
|
||
|
Length uint16
|
||
|
|
||
|
// The type of this Attribute, typically matched to a constant. Note that
|
||
|
// flags such as Nested and NetByteOrder must be handled manually when
|
||
|
// working with Attribute structures directly.
|
||
|
Type uint16
|
||
|
|
||
|
// An arbitrary payload which is specified by Type.
|
||
|
Data []byte
|
||
|
}
|
||
|
|
||
|
// marshal marshals the contents of a into b and returns the number of bytes
|
||
|
// written to b, including attribute alignment padding.
|
||
|
func (a *Attribute) marshal(b []byte) (int, error) {
|
||
|
if int(a.Length) < nlaHeaderLen {
|
||
|
return 0, errInvalidAttribute
|
||
|
}
|
||
|
|
||
|
nlenc.PutUint16(b[0:2], a.Length)
|
||
|
nlenc.PutUint16(b[2:4], a.Type)
|
||
|
n := copy(b[nlaHeaderLen:], a.Data)
|
||
|
|
||
|
return nlaHeaderLen + nlaAlign(n), nil
|
||
|
}
|
||
|
|
||
|
// unmarshal unmarshals the contents of a byte slice into an Attribute.
|
||
|
func (a *Attribute) unmarshal(b []byte) error {
|
||
|
if len(b) < nlaHeaderLen {
|
||
|
return errInvalidAttribute
|
||
|
}
|
||
|
|
||
|
a.Length = nlenc.Uint16(b[0:2])
|
||
|
a.Type = nlenc.Uint16(b[2:4])
|
||
|
|
||
|
if int(a.Length) > len(b) {
|
||
|
return errInvalidAttribute
|
||
|
}
|
||
|
|
||
|
switch {
|
||
|
// No length, no data
|
||
|
case a.Length == 0:
|
||
|
a.Data = make([]byte, 0)
|
||
|
// Not enough length for any data
|
||
|
case int(a.Length) < nlaHeaderLen:
|
||
|
return errInvalidAttribute
|
||
|
// Data present
|
||
|
case int(a.Length) >= nlaHeaderLen:
|
||
|
a.Data = make([]byte, len(b[nlaHeaderLen:a.Length]))
|
||
|
copy(a.Data, b[nlaHeaderLen:a.Length])
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// MarshalAttributes packs a slice of Attributes into a single byte slice.
|
||
|
// In most cases, the Length field of each Attribute should be set to 0, so it
|
||
|
// can be calculated and populated automatically for each Attribute.
|
||
|
//
|
||
|
// It is recommend to use the AttributeEncoder type where possible instead of
|
||
|
// calling MarshalAttributes and using package nlenc functions directly.
|
||
|
func MarshalAttributes(attrs []Attribute) ([]byte, error) {
|
||
|
// Count how many bytes we should allocate to store each attribute's contents.
|
||
|
var c int
|
||
|
for _, a := range attrs {
|
||
|
c += nlaHeaderLen + nlaAlign(len(a.Data))
|
||
|
}
|
||
|
|
||
|
// Advance through b with idx to place attribute data at the correct offset.
|
||
|
var idx int
|
||
|
b := make([]byte, c)
|
||
|
for _, a := range attrs {
|
||
|
// Infer the length of attribute if zero.
|
||
|
if a.Length == 0 {
|
||
|
a.Length = uint16(nlaHeaderLen + len(a.Data))
|
||
|
}
|
||
|
|
||
|
// Marshal a into b and advance idx to show many bytes are occupied.
|
||
|
n, err := a.marshal(b[idx:])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
idx += n
|
||
|
}
|
||
|
|
||
|
return b, nil
|
||
|
}
|
||
|
|
||
|
// UnmarshalAttributes unpacks a slice of Attributes from a single byte slice.
|
||
|
//
|
||
|
// It is recommend to use the AttributeDecoder type where possible instead of calling
|
||
|
// UnmarshalAttributes and using package nlenc functions directly.
|
||
|
func UnmarshalAttributes(b []byte) ([]Attribute, error) {
|
||
|
var attrs []Attribute
|
||
|
var i int
|
||
|
for {
|
||
|
if i > len(b) || len(b[i:]) == 0 {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
var a Attribute
|
||
|
if err := (&a).unmarshal(b[i:]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if a.Length == 0 {
|
||
|
i += nlaHeaderLen
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
i += nlaAlign(int(a.Length))
|
||
|
|
||
|
attrs = append(attrs, a)
|
||
|
}
|
||
|
|
||
|
return attrs, nil
|
||
|
}
|
||
|
|
||
|
// An AttributeDecoder provides a safe, iterator-like, API around attribute
|
||
|
// decoding.
|
||
|
//
|
||
|
// It is recommend to use an AttributeDecoder where possible instead of calling
|
||
|
// UnmarshalAttributes and using package nlenc functions directly.
|
||
|
//
|
||
|
// The Err method must be called after the Next method returns false to determine
|
||
|
// if any errors occurred during iteration.
|
||
|
type AttributeDecoder struct {
|
||
|
// ByteOrder defines a specific byte order to use when processing integer
|
||
|
// attributes. ByteOrder should be set immediately after creating the
|
||
|
// AttributeDecoder: before any attributes are parsed.
|
||
|
//
|
||
|
// If not set, the native byte order will be used.
|
||
|
ByteOrder binary.ByteOrder
|
||
|
|
||
|
// The attributes being worked on, and the iterator index into the slice of
|
||
|
// attributes.
|
||
|
attrs []Attribute
|
||
|
i int
|
||
|
|
||
|
// Any error encountered while decoding attributes.
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
// NewAttributeDecoder creates an AttributeDecoder that unpacks Attributes
|
||
|
// from b and prepares the decoder for iteration.
|
||
|
func NewAttributeDecoder(b []byte) (*AttributeDecoder, error) {
|
||
|
attrs, err := UnmarshalAttributes(b)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &AttributeDecoder{
|
||
|
// By default, use native byte order.
|
||
|
ByteOrder: nlenc.NativeEndian(),
|
||
|
|
||
|
attrs: attrs,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Next advances the decoder to the next netlink attribute. It returns false
|
||
|
// when no more attributes are present, or an error was encountered.
|
||
|
func (ad *AttributeDecoder) Next() bool {
|
||
|
if ad.err != nil {
|
||
|
// Hit an error, stop iteration.
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
ad.i++
|
||
|
|
||
|
// More attributes?
|
||
|
return len(ad.attrs) >= ad.i
|
||
|
}
|
||
|
|
||
|
// Type returns the Attribute.Type field of the current netlink attribute
|
||
|
// pointed to by the decoder.
|
||
|
//
|
||
|
// Type masks off the high bits of the netlink attribute type which may contain
|
||
|
// the Nested and NetByteOrder flags. If you need direct access to these flags,
|
||
|
// consider using UnmarshalAttributes instead.
|
||
|
func (ad *AttributeDecoder) Type() uint16 {
|
||
|
// Mask off any flags stored in the high bits.
|
||
|
return ad.attr().Type & attrTypeMask
|
||
|
}
|
||
|
|
||
|
// Len returns the number of netlink attributes pointed to by the decoder.
|
||
|
func (ad *AttributeDecoder) Len() int { return len(ad.attrs) }
|
||
|
|
||
|
// attr returns the current Attribute pointed to by the decoder.
|
||
|
func (ad *AttributeDecoder) attr() Attribute {
|
||
|
return ad.attrs[ad.i-1]
|
||
|
}
|
||
|
|
||
|
// data returns the Data field of the current Attribute pointed to by the decoder.
|
||
|
func (ad *AttributeDecoder) data() []byte {
|
||
|
return ad.attr().Data
|
||
|
}
|
||
|
|
||
|
// Err returns the first error encountered by the decoder.
|
||
|
func (ad *AttributeDecoder) Err() error {
|
||
|
return ad.err
|
||
|
}
|
||
|
|
||
|
// Bytes returns the raw bytes of the current Attribute's data.
|
||
|
func (ad *AttributeDecoder) Bytes() []byte {
|
||
|
src := ad.data()
|
||
|
dest := make([]byte, len(src))
|
||
|
copy(dest, src)
|
||
|
return dest
|
||
|
}
|
||
|
|
||
|
// String returns the string representation of the current Attribute's data.
|
||
|
func (ad *AttributeDecoder) String() string {
|
||
|
if ad.err != nil {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
return nlenc.String(ad.data())
|
||
|
}
|
||
|
|
||
|
// Uint8 returns the uint8 representation of the current Attribute's data.
|
||
|
func (ad *AttributeDecoder) Uint8() uint8 {
|
||
|
if ad.err != nil {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
b := ad.data()
|
||
|
if len(b) != 1 {
|
||
|
ad.err = fmt.Errorf("netlink: attribute %d is not a uint8; length: %d", ad.Type(), len(b))
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
return uint8(b[0])
|
||
|
}
|
||
|
|
||
|
// Uint16 returns the uint16 representation of the current Attribute's data.
|
||
|
func (ad *AttributeDecoder) Uint16() uint16 {
|
||
|
if ad.err != nil {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
b := ad.data()
|
||
|
if len(b) != 2 {
|
||
|
ad.err = fmt.Errorf("netlink: attribute %d is not a uint16; length: %d", ad.Type(), len(b))
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
return ad.ByteOrder.Uint16(b)
|
||
|
}
|
||
|
|
||
|
// Uint32 returns the uint32 representation of the current Attribute's data.
|
||
|
func (ad *AttributeDecoder) Uint32() uint32 {
|
||
|
if ad.err != nil {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
b := ad.data()
|
||
|
if len(b) != 4 {
|
||
|
ad.err = fmt.Errorf("netlink: attribute %d is not a uint32; length: %d", ad.Type(), len(b))
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
return ad.ByteOrder.Uint32(b)
|
||
|
}
|
||
|
|
||
|
// Uint64 returns the uint64 representation of the current Attribute's data.
|
||
|
func (ad *AttributeDecoder) Uint64() uint64 {
|
||
|
if ad.err != nil {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
b := ad.data()
|
||
|
if len(b) != 8 {
|
||
|
ad.err = fmt.Errorf("netlink: attribute %d is not a uint64; length: %d", ad.Type(), len(b))
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
return ad.ByteOrder.Uint64(b)
|
||
|
}
|
||
|
|
||
|
// Flag returns a boolean representing the Attribute.
|
||
|
func (ad *AttributeDecoder) Flag() bool {
|
||
|
if ad.err != nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
b := ad.data()
|
||
|
if len(b) != 0 {
|
||
|
ad.err = fmt.Errorf("netlink: attribute %d is not a flag; length: %d", ad.Type(), len(b))
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Do is a general purpose function which allows access to the current data
|
||
|
// pointed to by the AttributeDecoder.
|
||
|
//
|
||
|
// Do can be used to allow parsing arbitrary data within the context of the
|
||
|
// decoder. Do is most useful when dealing with nested attributes, attribute
|
||
|
// arrays, or decoding arbitrary types (such as C structures) which don't fit
|
||
|
// cleanly into a typical unsigned integer value.
|
||
|
//
|
||
|
// The function fn should not retain any reference to the data b outside of the
|
||
|
// scope of the function.
|
||
|
func (ad *AttributeDecoder) Do(fn func(b []byte) error) {
|
||
|
if ad.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b := ad.data()
|
||
|
if err := fn(b); err != nil {
|
||
|
ad.err = err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Nested decodes data into a nested AttributeDecoder to handle nested netlink
|
||
|
// attributes. When calling Nested, the Err method does not need to be called on
|
||
|
// the nested AttributeDecoder.
|
||
|
//
|
||
|
// The nested AttributeDecoder nad inherits the same ByteOrder setting as the
|
||
|
// top-level AttributeDecoder ad.
|
||
|
func (ad *AttributeDecoder) Nested(fn func(nad *AttributeDecoder) error) {
|
||
|
// Because we are wrapping Do, there is no need to check ad.err immediately.
|
||
|
ad.Do(func(b []byte) error {
|
||
|
nad, err := NewAttributeDecoder(b)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
nad.ByteOrder = ad.ByteOrder
|
||
|
|
||
|
if err := fn(nad); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nad.Err()
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// An AttributeEncoder provides a safe way to encode attributes.
|
||
|
//
|
||
|
// It is recommended to use an AttributeEncoder where possible instead of
|
||
|
// calling MarshalAttributes or using package nlenc directly.
|
||
|
//
|
||
|
// Errors from intermediate encoding steps are returned in the call to
|
||
|
// Encode.
|
||
|
type AttributeEncoder struct {
|
||
|
// ByteOrder defines a specific byte order to use when processing integer
|
||
|
// attributes. ByteOrder should be set immediately after creating the
|
||
|
// AttributeEncoder: before any attributes are encoded.
|
||
|
//
|
||
|
// If not set, the native byte order will be used.
|
||
|
ByteOrder binary.ByteOrder
|
||
|
|
||
|
attrs []Attribute
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
// NewAttributeEncoder creates an AttributeEncoder that encodes Attributes.
|
||
|
func NewAttributeEncoder() *AttributeEncoder {
|
||
|
return &AttributeEncoder{
|
||
|
ByteOrder: nlenc.NativeEndian(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Uint8 encodes uint8 data into an Attribute specified by typ.
|
||
|
func (ae *AttributeEncoder) Uint8(typ uint16, v uint8) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: []byte{v},
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Uint16 encodes uint16 data into an Attribute specified by typ.
|
||
|
func (ae *AttributeEncoder) Uint16(typ uint16, v uint16) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b := make([]byte, 2)
|
||
|
ae.ByteOrder.PutUint16(b, v)
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: b,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Uint32 encodes uint32 data into an Attribute specified by typ.
|
||
|
func (ae *AttributeEncoder) Uint32(typ uint16, v uint32) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b := make([]byte, 4)
|
||
|
ae.ByteOrder.PutUint32(b, v)
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: b,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Uint64 encodes uint64 data into an Attribute specified by typ.
|
||
|
func (ae *AttributeEncoder) Uint64(typ uint16, v uint64) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b := make([]byte, 8)
|
||
|
ae.ByteOrder.PutUint64(b, v)
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: b,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Flag encodes a flag into an Attribute specidied by typ.
|
||
|
func (ae *AttributeEncoder) Flag(typ uint16, v bool) {
|
||
|
// Only set flag on no previous error or v == true.
|
||
|
if ae.err != nil || !v {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Flags have no length or data fields.
|
||
|
ae.attrs = append(ae.attrs, Attribute{Type: typ})
|
||
|
}
|
||
|
|
||
|
// String encodes string s as a null-terminated string into an Attribute
|
||
|
// specified by typ.
|
||
|
func (ae *AttributeEncoder) String(typ uint16, s string) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: nlenc.Bytes(s),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Bytes embeds raw byte data into an Attribute specified by typ.
|
||
|
func (ae *AttributeEncoder) Bytes(typ uint16, b []byte) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: b,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Do is a general purpose function to encode arbitrary data into an attribute
|
||
|
// specified by typ.
|
||
|
//
|
||
|
// Do is especially helpful in encoding nested attributes, attribute arrays,
|
||
|
// or encoding arbitrary types (such as C structures) which don't fit cleanly
|
||
|
// into an unsigned integer value.
|
||
|
func (ae *AttributeEncoder) Do(typ uint16, fn func() ([]byte, error)) {
|
||
|
if ae.err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b, err := fn()
|
||
|
if err != nil {
|
||
|
ae.err = err
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ae.attrs = append(ae.attrs, Attribute{
|
||
|
Type: typ,
|
||
|
Data: b,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Nested embeds data produced by a nested AttributeEncoder and flags that data
|
||
|
// with the Nested flag. When calling Nested, the Encode method should not be
|
||
|
// called on the nested AttributeEncoder.
|
||
|
//
|
||
|
// The nested AttributeEncoder nae inherits the same ByteOrder setting as the
|
||
|
// top-level AttributeEncoder ae.
|
||
|
func (ae *AttributeEncoder) Nested(typ uint16, fn func(nae *AttributeEncoder) error) {
|
||
|
// Because we are wrapping Do, there is no need to check ae.err immediately.
|
||
|
ae.Do(Nested|typ, func() ([]byte, error) {
|
||
|
nae := NewAttributeEncoder()
|
||
|
nae.ByteOrder = ae.ByteOrder
|
||
|
|
||
|
if err := fn(nae); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return nae.Encode()
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Encode returns the encoded bytes representing the attributes.
|
||
|
func (ae *AttributeEncoder) Encode() ([]byte, error) {
|
||
|
if ae.err != nil {
|
||
|
return nil, ae.err
|
||
|
}
|
||
|
|
||
|
return MarshalAttributes(ae.attrs)
|
||
|
}
|