message.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package netlink
  2. import (
  3. "errors"
  4. "fmt"
  5. "unsafe"
  6. "github.com/mdlayher/netlink/nlenc"
  7. )
  8. // Flags which may apply to netlink attribute types when communicating with
  9. // certain netlink families.
  10. const (
  11. Nested uint16 = 0x8000
  12. NetByteOrder uint16 = 0x4000
  13. // attrTypeMask masks off Type bits used for the above flags.
  14. attrTypeMask uint16 = 0x3fff
  15. )
  16. // Various errors which may occur when attempting to marshal or unmarshal
  17. // a Message to and from its binary form.
  18. var (
  19. errIncorrectMessageLength = errors.New("netlink message header length incorrect")
  20. errShortMessage = errors.New("not enough data to create a netlink message")
  21. errUnalignedMessage = errors.New("input data is not properly aligned for netlink message")
  22. )
  23. // HeaderFlags specify flags which may be present in a Header.
  24. type HeaderFlags uint16
  25. const (
  26. // General netlink communication flags.
  27. // Request indicates a request to netlink.
  28. Request HeaderFlags = 1
  29. // Multi indicates a multi-part message, terminated by Done on the
  30. // last message.
  31. Multi HeaderFlags = 2
  32. // Acknowledge requests that netlink reply with an acknowledgement
  33. // using Error and, if needed, an error code.
  34. Acknowledge HeaderFlags = 4
  35. // Echo requests that netlink echo this request back to the sender.
  36. Echo HeaderFlags = 8
  37. // DumpInterrupted indicates that a dump was inconsistent due to a
  38. // sequence change.
  39. DumpInterrupted HeaderFlags = 16
  40. // DumpFiltered indicates that a dump was filtered as requested.
  41. DumpFiltered HeaderFlags = 32
  42. // Flags used to retrieve data from netlink.
  43. // Root requests that netlink return a complete table instead of a
  44. // single entry.
  45. Root HeaderFlags = 0x100
  46. // Match requests that netlink return a list of all matching entries.
  47. Match HeaderFlags = 0x200
  48. // Atomic requests that netlink send an atomic snapshot of its entries.
  49. // Requires CAP_NET_ADMIN or an effective UID of 0.
  50. Atomic HeaderFlags = 0x400
  51. // Dump requests that netlink return a complete list of all entries.
  52. Dump HeaderFlags = Root | Match
  53. // Flags used to create objects.
  54. // Replace indicates request replaces an existing matching object.
  55. Replace HeaderFlags = 0x100
  56. // Excl indicates request does not replace the object if it already exists.
  57. Excl HeaderFlags = 0x200
  58. // Create indicates request creates an object if it doesn't already exist.
  59. Create HeaderFlags = 0x400
  60. // Append indicates request adds to the end of the object list.
  61. Append HeaderFlags = 0x800
  62. // Flags for extended acknowledgements.
  63. // Capped indicates the size of a request was capped in an extended
  64. // acknowledgement.
  65. Capped HeaderFlags = 0x100
  66. // AcknowledgeTLVs indicates the presence of netlink extended
  67. // acknowledgement TLVs in a response.
  68. AcknowledgeTLVs HeaderFlags = 0x200
  69. )
  70. // String returns the string representation of a HeaderFlags.
  71. func (f HeaderFlags) String() string {
  72. names := []string{
  73. "request",
  74. "multi",
  75. "acknowledge",
  76. "echo",
  77. "dumpinterrupted",
  78. "dumpfiltered",
  79. }
  80. var s string
  81. left := uint(f)
  82. for i, name := range names {
  83. if f&(1<<uint(i)) != 0 {
  84. if s != "" {
  85. s += "|"
  86. }
  87. s += name
  88. left ^= (1 << uint(i))
  89. }
  90. }
  91. if s == "" && left == 0 {
  92. s = "0"
  93. }
  94. if left > 0 {
  95. if s != "" {
  96. s += "|"
  97. }
  98. s += fmt.Sprintf("%#x", left)
  99. }
  100. return s
  101. }
  102. // HeaderType specifies the type of a Header.
  103. type HeaderType uint16
  104. const (
  105. // Noop indicates that no action was taken.
  106. Noop HeaderType = 0x1
  107. // Error indicates an error code is present, which is also used to indicate
  108. // success when the code is 0.
  109. Error HeaderType = 0x2
  110. // Done indicates the end of a multi-part message.
  111. Done HeaderType = 0x3
  112. // Overrun indicates that data was lost from this message.
  113. Overrun HeaderType = 0x4
  114. )
  115. // String returns the string representation of a HeaderType.
  116. func (t HeaderType) String() string {
  117. switch t {
  118. case Noop:
  119. return "noop"
  120. case Error:
  121. return "error"
  122. case Done:
  123. return "done"
  124. case Overrun:
  125. return "overrun"
  126. default:
  127. return fmt.Sprintf("unknown(%d)", t)
  128. }
  129. }
  130. // NB: the memory layout of Header and Linux's syscall.NlMsgHdr must be
  131. // exactly the same. Cannot reorder, change data type, add, or remove fields.
  132. // Named types of the same size (e.g. HeaderFlags is a uint16) are okay.
  133. // A Header is a netlink header. A Header is sent and received with each
  134. // Message to indicate metadata regarding a Message.
  135. type Header struct {
  136. // Length of a Message, including this Header.
  137. Length uint32
  138. // Contents of a Message.
  139. Type HeaderType
  140. // Flags which may be used to modify a request or response.
  141. Flags HeaderFlags
  142. // The sequence number of a Message.
  143. Sequence uint32
  144. // The port ID of the sending process.
  145. PID uint32
  146. }
  147. // A Message is a netlink message. It contains a Header and an arbitrary
  148. // byte payload, which may be decoded using information from the Header.
  149. //
  150. // Data is often populated with netlink attributes. For easy encoding and
  151. // decoding of attributes, see the AttributeDecoder and AttributeEncoder types.
  152. type Message struct {
  153. Header Header
  154. Data []byte
  155. }
  156. // MarshalBinary marshals a Message into a byte slice.
  157. func (m Message) MarshalBinary() ([]byte, error) {
  158. ml := nlmsgAlign(int(m.Header.Length))
  159. if ml < nlmsgHeaderLen || ml != int(m.Header.Length) {
  160. return nil, errIncorrectMessageLength
  161. }
  162. b := make([]byte, ml)
  163. nlenc.PutUint32(b[0:4], m.Header.Length)
  164. nlenc.PutUint16(b[4:6], uint16(m.Header.Type))
  165. nlenc.PutUint16(b[6:8], uint16(m.Header.Flags))
  166. nlenc.PutUint32(b[8:12], m.Header.Sequence)
  167. nlenc.PutUint32(b[12:16], m.Header.PID)
  168. copy(b[16:], m.Data)
  169. return b, nil
  170. }
  171. // UnmarshalBinary unmarshals the contents of a byte slice into a Message.
  172. func (m *Message) UnmarshalBinary(b []byte) error {
  173. if len(b) < nlmsgHeaderLen {
  174. return errShortMessage
  175. }
  176. if len(b) != nlmsgAlign(len(b)) {
  177. return errUnalignedMessage
  178. }
  179. // Don't allow misleading length
  180. m.Header.Length = nlenc.Uint32(b[0:4])
  181. if int(m.Header.Length) != len(b) {
  182. return errShortMessage
  183. }
  184. m.Header.Type = HeaderType(nlenc.Uint16(b[4:6]))
  185. m.Header.Flags = HeaderFlags(nlenc.Uint16(b[6:8]))
  186. m.Header.Sequence = nlenc.Uint32(b[8:12])
  187. m.Header.PID = nlenc.Uint32(b[12:16])
  188. m.Data = b[16:]
  189. return nil
  190. }
  191. // checkMessage checks a single Message for netlink errors.
  192. func checkMessage(m Message) error {
  193. // NB: All non-nil errors returned from this function *must* be of type
  194. // OpError in order to maintain the appropriate contract with callers of
  195. // this package.
  196. // The libnl documentation indicates that type error can
  197. // contain error codes:
  198. // https://www.infradead.org/~tgr/libnl/doc/core.html#core_errmsg.
  199. //
  200. // However, rtnetlink at least seems to also allow errors to occur at the
  201. // end of a multipart message with done/multi and an error number.
  202. var hasHeader bool
  203. switch {
  204. case m.Header.Type == Error:
  205. // Error code followed by nlmsghdr/ext ack attributes.
  206. hasHeader = true
  207. case m.Header.Type == Done && m.Header.Flags&Multi != 0:
  208. // If no data, there must be no error number so just exit early. Some
  209. // of the unit tests hard-coded this but I don't actually know if this
  210. // case occurs in the wild.
  211. if len(m.Data) == 0 {
  212. return nil
  213. }
  214. // Done|Multi potentially followed by ext ack attributes.
  215. default:
  216. // Neither, nothing to do.
  217. return nil
  218. }
  219. // Errno occupies 4 bytes.
  220. const endErrno = 4
  221. if len(m.Data) < endErrno {
  222. return newOpError("receive", errShortErrorMessage)
  223. }
  224. c := nlenc.Int32(m.Data[:endErrno])
  225. if c == 0 {
  226. // 0 indicates no error.
  227. return nil
  228. }
  229. oerr := &OpError{
  230. Op: "receive",
  231. // Error code is a negative integer, convert it into an OS-specific raw
  232. // system call error, but do not wrap with os.NewSyscallError to signify
  233. // that this error was produced by a netlink message; not a system call.
  234. Err: newError(-1 * int(c)),
  235. }
  236. // TODO(mdlayher): investigate the Capped flag.
  237. if m.Header.Flags&AcknowledgeTLVs == 0 {
  238. // No extended acknowledgement.
  239. return oerr
  240. }
  241. // Flags indicate an extended acknowledgement. The type/flags combination
  242. // checked above determines the offset where the TLVs occur.
  243. var off int
  244. if hasHeader {
  245. // There is an nlmsghdr preceding the TLVs.
  246. if len(m.Data) < endErrno+nlmsgHeaderLen {
  247. return newOpError("receive", errShortErrorMessage)
  248. }
  249. // The TLVs should be at the offset indicated by the nlmsghdr.length,
  250. // plus the offset where the header began. But make sure the calculated
  251. // offset is still in-bounds.
  252. h := *(*Header)(unsafe.Pointer(&m.Data[endErrno : endErrno+nlmsgHeaderLen][0]))
  253. off = endErrno + int(h.Length)
  254. if len(m.Data) < off {
  255. return newOpError("receive", errShortErrorMessage)
  256. }
  257. } else {
  258. // There is no nlmsghdr preceding the TLVs, parse them directly.
  259. off = endErrno
  260. }
  261. ad, err := NewAttributeDecoder(m.Data[off:])
  262. if err != nil {
  263. // Malformed TLVs, just return the OpError with the info we have.
  264. return oerr
  265. }
  266. for ad.Next() {
  267. switch ad.Type() {
  268. case 1: // unix.NLMSGERR_ATTR_MSG
  269. oerr.Message = ad.String()
  270. case 2: // unix.NLMSGERR_ATTR_OFFS
  271. oerr.Offset = int(ad.Uint32())
  272. }
  273. }
  274. // Explicitly ignore ad.Err: malformed TLVs, just return the OpError with
  275. // the info we have.
  276. return oerr
  277. }