Skip to main content

netlink_packet_core/
error.rs

1// SPDX-License-Identifier: MIT
2
3use std::{fmt, io, mem::size_of, num::NonZeroI32};
4
5use crate::{emit_i32, parse_i32, Emitable, Field, Parseable, Rest};
6
7const CODE: Field = 0..4;
8const PAYLOAD: Rest = 4..;
9const ERROR_HEADER_LEN: usize = PAYLOAD.start;
10
11pub trait ErrorContext<T: std::fmt::Display> {
12    /// Attach context to an error.
13    fn context(self, msg: T) -> Self;
14
15    /// Attach context lazily, so the message is only built when
16    /// there is actually an error to attach it to.
17    fn with_context<F>(self, f: F) -> Self
18    where
19        F: FnOnce() -> T,
20        Self: Sized,
21    {
22        self.context(f())
23    }
24}
25
26#[derive(Debug)]
27pub struct DecodeError {
28    msg: String,
29}
30
31impl<T: std::fmt::Display> ErrorContext<T> for DecodeError {
32    fn context(self, msg: T) -> Self {
33        Self {
34            msg: format!("{} caused by {}", msg, self.msg),
35        }
36    }
37}
38
39impl<T, M> ErrorContext<M> for Result<T, DecodeError>
40where
41    M: std::fmt::Display,
42{
43    fn context(self, msg: M) -> Result<T, DecodeError> {
44        match self {
45            Ok(t) => Ok(t),
46            Err(e) => Err(e.context(msg)),
47        }
48    }
49
50    /// Only render the error on the Err case
51    fn with_context<F>(self, f: F) -> Result<T, DecodeError>
52    where
53        F: FnOnce() -> M,
54    {
55        match self {
56            Ok(t) => Ok(t),
57            Err(e) => Err(e.context(f())),
58        }
59    }
60}
61
62impl From<&str> for DecodeError {
63    fn from(msg: &str) -> Self {
64        Self {
65            msg: msg.to_string(),
66        }
67    }
68}
69
70impl From<String> for DecodeError {
71    fn from(msg: String) -> Self {
72        Self { msg }
73    }
74}
75
76impl From<std::string::FromUtf8Error> for DecodeError {
77    fn from(err: std::string::FromUtf8Error) -> Self {
78        Self {
79            msg: format!("Invalid UTF-8 sequence: {}", err),
80        }
81    }
82}
83
84impl std::fmt::Display for DecodeError {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        write!(f, "{}", self.msg)
87    }
88}
89
90impl std::error::Error for DecodeError {}
91
92impl DecodeError {
93    pub fn invalid_buffer(
94        name: &str,
95        received: usize,
96        minimum_length: usize,
97    ) -> Self {
98        Self {
99            msg: format!(
100                "Invalid buffer {name}. Expected at least {minimum_length} \
101                 bytes, received {received} bytes"
102            ),
103        }
104    }
105    pub fn invalid_mac_address(received: usize) -> Self {
106        Self {
107            msg: format!(
108                "Invalid MAC address. Expected 6 bytes, received {received} \
109                 bytes"
110            ),
111        }
112    }
113
114    pub fn invalid_ip_address(received: usize) -> Self {
115        Self {
116            msg: format!(
117                "Invalid IP address. Expected 4 or 16 bytes, received \
118                 {received} bytes"
119            ),
120        }
121    }
122
123    pub fn invalid_number(expected: usize, received: usize) -> Self {
124        Self {
125            msg: format!(
126                "Invalid number. Expected {expected} bytes, received \
127                 {received} bytes"
128            ),
129        }
130    }
131
132    pub fn nla_buffer_too_small(buffer_len: usize, nla_len: usize) -> Self {
133        Self {
134            msg: format!(
135                "buffer has length {buffer_len}, but an NLA header is \
136                 {nla_len} bytes"
137            ),
138        }
139    }
140
141    pub fn nla_length_mismatch(buffer_len: usize, nla_len: usize) -> Self {
142        Self {
143            msg: format!(
144                "buffer has length: {buffer_len}, but the NLA is {nla_len} \
145                 bytes"
146            ),
147        }
148    }
149
150    pub fn nla_invalid_length(buffer_len: usize, nla_len: usize) -> Self {
151        Self {
152            msg: format!(
153                "NLA has invalid length: {nla_len} (should be at least \
154                 {buffer_len} bytes)"
155            ),
156        }
157    }
158
159    pub fn buffer_too_small(buffer_len: usize, value_len: usize) -> Self {
160        Self {
161            msg: format!(
162                "Buffer too small: {buffer_len} (should be at least \
163                 {value_len} bytes"
164            ),
165        }
166    }
167}
168
169#[derive(Debug, PartialEq, Eq, Clone)]
170#[non_exhaustive]
171pub struct ErrorBuffer<T> {
172    buffer: T,
173}
174
175impl<T: AsRef<[u8]>> ErrorBuffer<T> {
176    pub fn new(buffer: T) -> ErrorBuffer<T> {
177        ErrorBuffer { buffer }
178    }
179
180    /// Consume the packet, returning the underlying buffer.
181    pub fn into_inner(self) -> T {
182        self.buffer
183    }
184
185    pub fn new_checked(buffer: T) -> Result<Self, DecodeError> {
186        let packet = Self::new(buffer);
187        packet
188            .check_buffer_length()
189            .context("invalid ErrorBuffer length")?;
190        Ok(packet)
191    }
192
193    fn check_buffer_length(&self) -> Result<(), DecodeError> {
194        let len = self.buffer.as_ref().len();
195        if len < ERROR_HEADER_LEN {
196            Err(DecodeError {
197                msg: format!(
198                    "invalid ErrorBuffer: length is {len} but ErrorBuffer are \
199                     at least {ERROR_HEADER_LEN} bytes"
200                ),
201            })
202        } else {
203            Ok(())
204        }
205    }
206
207    /// Return the error code.
208    ///
209    /// Returns `None` when there is no error to report (the message is an ACK),
210    /// or a `Some(e)` if there is a non-zero error code `e` to report (the
211    /// message is a NACK).
212    pub fn code(&self) -> Option<NonZeroI32> {
213        let data = self.buffer.as_ref();
214        NonZeroI32::new(parse_i32(&data[CODE]).unwrap())
215    }
216}
217
218impl<'a, T: AsRef<[u8]> + ?Sized> ErrorBuffer<&'a T> {
219    /// Return a pointer to the payload.
220    pub fn payload(&self) -> &'a [u8] {
221        let data = self.buffer.as_ref();
222        &data[PAYLOAD]
223    }
224}
225
226impl<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> ErrorBuffer<&mut T> {
227    /// Return a mutable pointer to the payload.
228    pub fn payload_mut(&mut self) -> &mut [u8] {
229        let data = self.buffer.as_mut();
230        &mut data[PAYLOAD]
231    }
232}
233
234impl<T: AsRef<[u8]> + AsMut<[u8]>> ErrorBuffer<T> {
235    /// set the error code field
236    pub fn set_code(&mut self, value: i32) {
237        let data = self.buffer.as_mut();
238        emit_i32(&mut data[CODE], value).unwrap();
239    }
240}
241
242/// An `NLMSG_ERROR` message.
243///
244/// Per [RFC 3549 section 2.3.2.2], this message carries the return code for a
245/// request which will indicate either success (an ACK) or failure (a NACK).
246///
247/// [RFC 3549 section 2.3.2.2]: https://datatracker.ietf.org/doc/html/rfc3549#section-2.3.2.2
248#[derive(Debug, Default, Clone, PartialEq, Eq)]
249#[non_exhaustive]
250pub struct ErrorMessage {
251    /// The error code.
252    ///
253    /// Holds `None` when there is no error to report (the message is an ACK),
254    /// or a `Some(e)` if there is a non-zero error code `e` to report (the
255    /// message is a NACK).
256    ///
257    /// See [Netlink message types] for details.
258    ///
259    /// [Netlink message types]: https://kernel.org/doc/html/next/userspace-api/netlink/intro.html#netlink-message-types
260    pub code: Option<NonZeroI32>,
261    /// The original request's header.
262    pub header: Vec<u8>,
263}
264
265impl Emitable for ErrorMessage {
266    fn buffer_len(&self) -> usize {
267        size_of::<i32>() + self.header.len()
268    }
269    fn emit(&self, buffer: &mut [u8]) {
270        let mut buffer = ErrorBuffer::new(buffer);
271        buffer.set_code(self.raw_code());
272        buffer.payload_mut().copy_from_slice(&self.header)
273    }
274}
275
276impl<T: AsRef<[u8]>> Parseable<ErrorBuffer<&T>> for ErrorMessage {
277    fn parse(buf: &ErrorBuffer<&T>) -> Result<ErrorMessage, DecodeError> {
278        // FIXME: The payload of an error is basically a truncated packet, which
279        // requires custom logic to parse correctly. For now we just
280        // return it as a Vec<u8> let header: NetlinkHeader = {
281        //     NetlinkBuffer::new_checked(self.payload())
282        //         .context("failed to parse netlink header")?
283        //         .parse()
284        //         .context("failed to parse nelink header")?
285        // };
286        Ok(ErrorMessage {
287            code: buf.code(),
288            header: buf.payload().to_vec(),
289        })
290    }
291}
292
293impl ErrorMessage {
294    /// Returns the raw error code.
295    pub fn raw_code(&self) -> i32 {
296        self.code.map_or(0, NonZeroI32::get)
297    }
298
299    /// According to [`netlink(7)`](https://linux.die.net/man/7/netlink)
300    /// the `NLMSG_ERROR` return Negative errno or 0 for acknowledgements.
301    ///
302    /// convert into [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
303    /// using the absolute value from errno code
304    pub fn to_io(&self) -> io::Error {
305        io::Error::from_raw_os_error(self.raw_code().abs())
306    }
307}
308
309impl fmt::Display for ErrorMessage {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        fmt::Display::fmt(&self.to_io(), f)
312    }
313}
314
315impl From<ErrorMessage> for io::Error {
316    fn from(e: ErrorMessage) -> io::Error {
317        e.to_io()
318    }
319}
320
321// test data are using hard coded little endian byte order, not for big-endian
322#[cfg(not(target_endian = "big"))]
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    #[test]
328    fn into_io_error() {
329        let io_err = io::Error::from_raw_os_error(95);
330        let err_msg = ErrorMessage {
331            code: NonZeroI32::new(-95),
332            header: vec![],
333        };
334
335        let to_io: io::Error = err_msg.to_io();
336
337        assert_eq!(err_msg.to_string(), io_err.to_string());
338        assert_eq!(to_io.raw_os_error(), io_err.raw_os_error());
339    }
340
341    #[test]
342    fn parse_ack() {
343        let bytes = vec![0, 0, 0, 0];
344        let msg = ErrorBuffer::new_checked(&bytes)
345            .and_then(|buf| ErrorMessage::parse(&buf))
346            .expect("failed to parse NLMSG_ERROR");
347        assert_eq!(
348            ErrorMessage {
349                code: None,
350                header: Vec::new()
351            },
352            msg
353        );
354        assert_eq!(msg.raw_code(), 0);
355    }
356
357    #[test]
358    fn parse_nack() {
359        // SAFETY: value is non-zero.
360        const ERROR_CODE: NonZeroI32 = NonZeroI32::new(-1234).unwrap();
361        let mut bytes = vec![0, 0, 0, 0];
362        emit_i32(&mut bytes, ERROR_CODE.get()).unwrap();
363        let msg = ErrorBuffer::new_checked(&bytes)
364            .and_then(|buf| ErrorMessage::parse(&buf))
365            .expect("failed to parse NLMSG_ERROR");
366        assert_eq!(
367            ErrorMessage {
368                code: Some(ERROR_CODE),
369                header: Vec::new()
370            },
371            msg
372        );
373        assert_eq!(msg.raw_code(), ERROR_CODE.get());
374    }
375}