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}