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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
|
package netlink
import (
"errors"
"fmt"
"unsafe"
"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
// Flags for extended acknowledgements.
// Capped indicates the size of a request was capped in an extended
// acknowledgement.
Capped HeaderFlags = 0x100
// AcknowledgeTLVs indicates the presence of netlink extended
// acknowledgement TLVs in a response.
AcknowledgeTLVs HeaderFlags = 0x200
)
// 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 port 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.
// The libnl documentation indicates that type error can
// contain error codes:
// https://www.infradead.org/~tgr/libnl/doc/core.html#core_errmsg.
//
// However, rtnetlink at least seems to also allow errors to occur at the
// end of a multipart message with done/multi and an error number.
var hasHeader bool
switch {
case m.Header.Type == Error:
// Error code followed by nlmsghdr/ext ack attributes.
hasHeader = true
case m.Header.Type == Done && m.Header.Flags&Multi != 0:
// If no data, there must be no error number so just exit early. Some
// of the unit tests hard-coded this but I don't actually know if this
// case occurs in the wild.
if len(m.Data) == 0 {
return nil
}
// Done|Multi potentially followed by ext ack attributes.
default:
// Neither, nothing to do.
return nil
}
// Errno occupies 4 bytes.
const endErrno = 4
if len(m.Data) < endErrno {
return newOpError("receive", errShortErrorMessage)
}
c := nlenc.Int32(m.Data[:endErrno])
if c == 0 {
// 0 indicates no error.
return nil
}
oerr := &OpError{
Op: "receive",
// 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.
Err: newError(-1 * int(c)),
}
// TODO(mdlayher): investigate the Capped flag.
if m.Header.Flags&AcknowledgeTLVs == 0 {
// No extended acknowledgement.
return oerr
}
// Flags indicate an extended acknowledgement. The type/flags combination
// checked above determines the offset where the TLVs occur.
var off int
if hasHeader {
// There is an nlmsghdr preceding the TLVs.
if len(m.Data) < endErrno+nlmsgHeaderLen {
return newOpError("receive", errShortErrorMessage)
}
// The TLVs should be at the offset indicated by the nlmsghdr.length,
// plus the offset where the header began. But make sure the calculated
// offset is still in-bounds.
h := *(*Header)(unsafe.Pointer(&m.Data[endErrno : endErrno+nlmsgHeaderLen][0]))
off = endErrno + int(h.Length)
if len(m.Data) < off {
return newOpError("receive", errShortErrorMessage)
}
} else {
// There is no nlmsghdr preceding the TLVs, parse them directly.
off = endErrno
}
ad, err := NewAttributeDecoder(m.Data[off:])
if err != nil {
// Malformed TLVs, just return the OpError with the info we have.
return oerr
}
for ad.Next() {
switch ad.Type() {
case 1: // unix.NLMSGERR_ATTR_MSG
oerr.Message = ad.String()
case 2: // unix.NLMSGERR_ATTR_OFFS
oerr.Offset = int(ad.Uint32())
}
}
// Explicitly ignore ad.Err: malformed TLVs, just return the OpError with
// the info we have.
return oerr
}
|