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}