netlink_packet/netlink/
buffer.rs

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