netlink_packet_core/buffer.rs
1// SPDX-License-Identifier: MIT
2
3use byteorder::{ByteOrder, NativeEndian};
4
5use crate::{DecodeError, Field, Rest};
6
7const LENGTH: Field = 0..4;
8const MESSAGE_TYPE: Field = 4..6;
9const FLAGS: Field = 6..8;
10const SEQUENCE_NUMBER: Field = 8..12;
11const PORT_NUMBER: Field = 12..16;
12const PAYLOAD: Rest = 16..;
13
14/// Length of a Netlink packet header
15pub const NETLINK_HEADER_LEN: usize = PAYLOAD.start;
16
17// Prevent some doctest snippers to be formatted, since we cannot add
18// the attribute directly in the doctest
19#[rustfmt::skip]
20#[derive(Debug, PartialEq, Eq, Clone)]
21/// A raw Netlink buffer that provides getters and setter for the various header fields, and to
22/// retrieve the payloads.
23///
24/// # Example: reading a packet
25///
26/// ```rust
27/// use netlink_packet_core::{NetlinkBuffer, NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT};
28///
29/// const RTM_GETLINK: u16 = 18;
30///
31/// fn main() {
32/// // Artificially create an array of bytes that represents a netlink packet.
33/// // Normally, we would read it from a socket.
34/// let buffer = vec![
35/// 0x28, 0x00, 0x00, 0x00, // length = 40
36/// 0x12, 0x00, // message type = 18 (RTM_GETLINK)
37/// 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching
38/// 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540
39/// 0x00, 0x00, 0x00, 0x00, // port id = 0
40/// // payload
41/// 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43/// 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00];
44///
45/// // Wrap the storage into a NetlinkBuffer
46/// let packet = NetlinkBuffer::new_checked(&buffer[..]).unwrap();
47///
48/// // Check that the different accessor return the expected values
49/// assert_eq!(packet.length(), 40);
50/// assert_eq!(packet.message_type(), RTM_GETLINK);
51/// assert_eq!(packet.sequence_number(), 1526271540);
52/// assert_eq!(packet.port_number(), 0);
53/// assert_eq!(packet.payload_length(), 24);
54/// assert_eq!(packet.payload(), &buffer[16..]);
55/// assert_eq!(
56/// Into::<u16>::into(packet.flags()),
57/// NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH
58/// );
59/// }
60/// ```
61///
62/// # Example: writing a packet
63///
64/// ```rust
65/// use netlink_packet_core::{NetlinkBuffer, NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT};
66///
67/// const RTM_GETLINK: u16 = 18;
68///
69/// fn main() {
70/// // The packet we want to write.
71/// let expected_buffer = vec![
72/// 0x28, 0x00, 0x00, 0x00, // length = 40
73/// 0x12, 0x00, // message type = 18 (RTM_GETLINK)
74/// 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching
75/// 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540
76/// 0x00, 0x00, 0x00, 0x00, // port id = 0
77/// // payload
78/// 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80/// 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00];
81///
82/// // Create a storage that is big enough for our packet
83/// let mut buf = vec![0; 40];
84/// // the extra scope is to restrict the scope of the borrow
85/// {
86/// // Create a NetlinkBuffer.
87/// let mut packet = NetlinkBuffer::new(&mut buf);
88/// // Set the various fields
89/// packet.set_length(40);
90/// packet.set_message_type(RTM_GETLINK);
91/// packet.set_sequence_number(1526271540);
92/// packet.set_port_number(0);
93/// packet.set_flags(From::from(NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH));
94/// // we kind of cheat here to keep the example short
95/// packet.payload_mut().copy_from_slice(&expected_buffer[16..]);
96/// }
97/// // Check that the storage contains the expected values
98/// assert_eq!(&buf[..], &expected_buffer[..]);
99/// }
100/// ```
101///
102/// Note that in this second example we don't call
103/// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked) because the length field is
104/// initialized to 0, so `new_checked()` would return an error.
105pub struct NetlinkBuffer<T> {
106 pub buffer: T,
107}
108
109// Prevent some doc strings to be formatted, since we cannot add the
110// attribute directly in the doctest
111#[rustfmt::skip]
112impl<T: AsRef<[u8]>> NetlinkBuffer<T> {
113 /// Create a new `NetlinkBuffer` that uses the given buffer as storage. Note that when calling
114 /// this method no check is performed, so trying to access fields may panic. If you're not sure
115 /// the given buffer contains a valid netlink packet, use
116 /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked) instead.
117 pub fn new(buffer: T) -> NetlinkBuffer<T> {
118 NetlinkBuffer { buffer }
119 }
120
121 // Prevent some doc strings to be formatted, since we cannot add
122 // the attribute directly in the doctest
123 #[rustfmt::skip]
124 /// Check the length of the given buffer and make sure it's big enough so that trying to access
125 /// packet fields won't panic. If the buffer is big enough, create a new `NewlinkBuffer` that
126 /// uses this buffer as storage.
127 ///
128 /// # Example
129 ///
130 /// With a buffer that does not even contain a full header:
131 ///
132 /// ```rust
133 /// use netlink_packet_core::NetlinkBuffer;
134 /// static BYTES: [u8; 4] = [0x28, 0x00, 0x00, 0x00];
135 /// assert!(NetlinkBuffer::new_checked(&BYTES[..]).is_err());
136 /// ```
137 ///
138 /// Here is a slightly more tricky error, where technically, the buffer is big enough to
139 /// contains a valid packet. Here, accessing the packet header fields would not panic but
140 /// accessing the payload would, so `new_checked` also checks the length field in the packet
141 /// header:
142 ///
143 /// ```rust
144 /// use netlink_packet_core::NetlinkBuffer;
145 /// // The buffer is 24 bytes long. It contains a valid header but a truncated payload
146 /// static BYTES: [u8; 24] = [
147 /// // The length field says the buffer is 40 bytes long
148 /// 0x28, 0x00, 0x00, 0x00,
149 /// 0x12, 0x00, // message type
150 /// 0x01, 0x03, // flags
151 /// 0x34, 0x0e, 0xf9, 0x5a, // sequence number
152 /// 0x00, 0x00, 0x00, 0x00, // port id
153 /// // payload
154 /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
155 /// assert!(NetlinkBuffer::new_checked(&BYTES[..]).is_err());
156 /// ```
157 pub fn new_checked(buffer: T) -> Result<NetlinkBuffer<T>, DecodeError> {
158 let packet = Self::new(buffer);
159 packet.check_buffer_length()?;
160 Ok(packet)
161 }
162
163 fn check_buffer_length(&self) -> Result<(), DecodeError> {
164 let len = self.buffer.as_ref().len();
165 if len < PORT_NUMBER.end {
166 Err(format!(
167 "invalid netlink buffer: length is {} but netlink packets are at least {} bytes",
168 len, PORT_NUMBER.end
169 )
170 .into())
171 } else if len < self.length() as usize {
172 Err(format!(
173 "invalid netlink buffer: length field says {} the buffer is {} bytes long",
174 self.length(),
175 len
176 )
177 .into())
178 } else if (self.length() as usize) < PORT_NUMBER.end {
179 Err(format!(
180 "invalid netlink buffer: length field says {} but netlink packets are at least {} bytes",
181 self.length(),
182 len
183 ).into())
184 } else {
185 Ok(())
186 }
187 }
188
189 /// Return the payload length.
190 ///
191 /// # Panic
192 ///
193 /// This panic is the underlying storage is too small or if the `length` field in the header is
194 /// set to a value that exceeds the storage length (see
195 /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
196 pub fn payload_length(&self) -> usize {
197 let total_length = self.length() as usize;
198 let payload_offset = PAYLOAD.start;
199 // This may panic!
200 total_length - payload_offset
201 }
202
203 /// Consume the packet, returning the underlying buffer.
204 pub fn into_inner(self) -> T {
205 self.buffer
206 }
207
208 /// Return the `length` field
209 ///
210 /// # Panic
211 ///
212 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
213 pub fn length(&self) -> u32 {
214 let data = self.buffer.as_ref();
215 NativeEndian::read_u32(&data[LENGTH])
216 }
217
218 /// Return the `type` field
219 ///
220 /// # Panic
221 ///
222 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
223 pub fn message_type(&self) -> u16 {
224 let data = self.buffer.as_ref();
225 NativeEndian::read_u16(&data[MESSAGE_TYPE])
226 }
227
228 /// Return the `flags` field
229 ///
230 /// # Panic
231 ///
232 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
233 pub fn flags(&self) -> u16 {
234 let data = self.buffer.as_ref();
235 NativeEndian::read_u16(&data[FLAGS])
236 }
237
238 /// Return the `sequence_number` field
239 ///
240 /// # Panic
241 ///
242 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
243 pub fn sequence_number(&self) -> u32 {
244 let data = self.buffer.as_ref();
245 NativeEndian::read_u32(&data[SEQUENCE_NUMBER])
246 }
247
248 /// Return the `port_number` field
249 ///
250 /// # Panic
251 ///
252 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
253 pub fn port_number(&self) -> u32 {
254 let data = self.buffer.as_ref();
255 NativeEndian::read_u32(&data[PORT_NUMBER])
256 }
257}
258
259impl<T: AsRef<[u8]> + AsMut<[u8]>> NetlinkBuffer<T> {
260 /// Set the packet header `length` field
261 ///
262 /// # Panic
263 ///
264 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
265 pub fn set_length(&mut self, value: u32) {
266 let data = self.buffer.as_mut();
267 NativeEndian::write_u32(&mut data[LENGTH], value)
268 }
269
270 /// Set the packet header `message_type` field
271 ///
272 /// # Panic
273 ///
274 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
275 pub fn set_message_type(&mut self, value: u16) {
276 let data = self.buffer.as_mut();
277 NativeEndian::write_u16(&mut data[MESSAGE_TYPE], value)
278 }
279
280 /// Set the packet header `flags` field
281 ///
282 /// # Panic
283 ///
284 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
285 pub fn set_flags(&mut self, value: u16) {
286 let data = self.buffer.as_mut();
287 NativeEndian::write_u16(&mut data[FLAGS], value)
288 }
289
290 /// Set the packet header `sequence_number` field
291 ///
292 /// # Panic
293 ///
294 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
295 pub fn set_sequence_number(&mut self, value: u32) {
296 let data = self.buffer.as_mut();
297 NativeEndian::write_u32(&mut data[SEQUENCE_NUMBER], value)
298 }
299
300 /// Set the packet header `port_number` field
301 ///
302 /// # Panic
303 ///
304 /// This panic is the underlying storage is too small (see [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
305 pub fn set_port_number(&mut self, value: u32) {
306 let data = self.buffer.as_mut();
307 NativeEndian::write_u32(&mut data[PORT_NUMBER], value)
308 }
309}
310
311impl<'a, T: AsRef<[u8]> + ?Sized> NetlinkBuffer<&'a T> {
312 /// Return a pointer to the packet payload.
313 ///
314 /// # Panic
315 ///
316 /// This panic is the underlying storage is too small or if the `length` field in the header is
317 /// set to a value that exceeds the storage length (see
318 /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
319 pub fn payload(&self) -> &'a [u8] {
320 let range = PAYLOAD.start..self.length() as usize;
321 let data = self.buffer.as_ref();
322 &data[range]
323 }
324}
325
326impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NetlinkBuffer<&'a mut T> {
327 /// Return a mutable pointer to the payload.
328 ///
329 /// # Panic
330 ///
331 /// This panic is the underlying storage is too small or if the `length` field in the header is
332 /// set to a value that exceeds the storage length (see
333 /// [`new_checked()`](struct.NetlinkBuffer.html#method.new_checked))
334 pub fn payload_mut(&mut self) -> &mut [u8] {
335 let range = PAYLOAD.start..self.length() as usize;
336 let data = self.buffer.as_mut();
337 &mut data[range]
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use crate::{
344 constants::{NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT},
345 NetlinkBuffer,
346 };
347
348 const RTM_GETLINK: u16 = 18;
349
350 // a packet captured with tcpdump that was sent when running `ip link show`
351 #[rustfmt::skip]
352 static IP_LINK_SHOW_PKT: [u8; 40] = [
353 0x28, 0x00, 0x00, 0x00, // length = 40
354 0x12, 0x00, // message type = 18 (RTM_GETLINK)
355 0x01, 0x03, // flags = Request + Specify Tree Root + Return All Matching
356 0x34, 0x0e, 0xf9, 0x5a, // sequence number = 1526271540
357 0x00, 0x00, 0x00, 0x00, // port id = 0
358 // payload
359 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
361 0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00];
362
363 #[test]
364 fn packet_read() {
365 let packet = NetlinkBuffer::new(&IP_LINK_SHOW_PKT[..]);
366 assert_eq!(packet.length(), 40);
367 assert_eq!(packet.message_type(), RTM_GETLINK);
368 assert_eq!(packet.sequence_number(), 1526271540);
369 assert_eq!(packet.port_number(), 0);
370 let flags = packet.flags();
371 assert!(flags & NLM_F_ROOT == NLM_F_ROOT);
372 assert!(flags & NLM_F_REQUEST == NLM_F_REQUEST);
373 assert!(flags & NLM_F_MATCH == NLM_F_MATCH);
374 assert_eq!(flags, NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH);
375 assert_eq!(packet.payload_length(), 24);
376 assert_eq!(packet.payload(), &IP_LINK_SHOW_PKT[16..]);
377 }
378
379 #[test]
380 fn packet_build() {
381 let mut buf = vec![0; 40];
382 {
383 let mut packet = NetlinkBuffer::new(&mut buf);
384 packet.set_length(40);
385 packet.set_message_type(RTM_GETLINK);
386 packet.set_sequence_number(1526271540);
387 packet.set_port_number(0);
388 packet.set_flags((NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH));
389 packet
390 .payload_mut()
391 .copy_from_slice(&IP_LINK_SHOW_PKT[16..]);
392 }
393 assert_eq!(&buf[..], &IP_LINK_SHOW_PKT[..]);
394 }
395}