netlink_packet_core/
buffer.rs

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