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}