forked from mdlayher/netlink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
attribute.go
166 lines (129 loc) · 4.07 KB
/
attribute.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package netlink
import (
"errors"
"github.com/mdlayher/netlink/nlenc"
)
var (
// errInvalidAttribute specifies if an Attribute's length is incorrect.
errInvalidAttribute = errors.New("invalid attribute; length too short or too large")
// errInvalidAttributeFlags specifies if an Attribute's flag configuration is invalid.
// From a comment in Linux/include/uapi/linux/netlink.h, Nested and NetByteOrder are mutually exclusive.
errInvalidAttributeFlags = errors.New("invalid attribute; type cannot have both nested and net byte order flags")
)
// 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.
Type uint16
// An arbitrary payload which is specified by Type.
Data []byte
// Whether the attribute's data contains nested attributes. Note that not
// all netlink families set this value. The programmer should consult
// documentation and inspect an attribute's data to determine if nested
// attributes are present.
Nested bool
// Whether the attribute's data is in network (true) or native (false) byte order.
NetByteOrder bool
}
// #define NLA_F_NESTED
const nlaNested uint16 = 0x8000
// #define NLA_F_NET_BYTE_ORDER
const nlaNetByteOrder uint16 = 0x4000
// Masks all bits except for Nested and NetByteOrder.
const nlaTypeMask = ^(nlaNested | nlaNetByteOrder)
// MarshalBinary marshals an Attribute into a byte slice.
func (a Attribute) MarshalBinary() ([]byte, error) {
if int(a.Length) < nlaHeaderLen {
return nil, errInvalidAttribute
}
if a.NetByteOrder && a.Nested {
return nil, errInvalidAttributeFlags
}
b := make([]byte, nlaAlign(int(a.Length)))
nlenc.PutUint16(b[0:2], a.Length)
switch {
case a.Nested:
nlenc.PutUint16(b[2:4], a.Type|nlaNested)
case a.NetByteOrder:
nlenc.PutUint16(b[2:4], a.Type|nlaNetByteOrder)
default:
nlenc.PutUint16(b[2:4], a.Type)
}
copy(b[nlaHeaderLen:], a.Data)
return b, nil
}
// UnmarshalBinary unmarshals the contents of a byte slice into an Attribute.
func (a *Attribute) UnmarshalBinary(b []byte) error {
if len(b) < nlaHeaderLen {
return errInvalidAttribute
}
a.Length = nlenc.Uint16(b[0:2])
// Only hold the rightmost 14 bits in Type
a.Type = nlenc.Uint16(b[2:4]) & nlaTypeMask
// Boolean flags extracted from the two leftmost bits of Type
a.Nested = (nlenc.Uint16(b[2:4]) & nlaNested) > 0
a.NetByteOrder = (nlenc.Uint16(b[2:4]) & nlaNetByteOrder) > 0
if nlaAlign(int(a.Length)) > len(b) {
return errInvalidAttribute
}
if a.NetByteOrder && a.Nested {
return errInvalidAttributeFlags
}
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.
func MarshalAttributes(attrs []Attribute) ([]byte, error) {
var c int
for _, a := range attrs {
c += nlaAlign(len(a.Data))
}
b := make([]byte, 0, c)
for _, a := range attrs {
if a.Length == 0 {
a.Length = uint16(nlaHeaderLen + len(a.Data))
}
ab, err := a.MarshalBinary()
if err != nil {
return nil, err
}
b = append(b, ab...)
}
return b, nil
}
// UnmarshalAttributes unpacks a slice of Attributes from a single byte slice.
func UnmarshalAttributes(b []byte) ([]Attribute, error) {
var attrs []Attribute
var i int
for {
if len(b[i:]) == 0 {
break
}
var a Attribute
if err := (&a).UnmarshalBinary(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
}