netlink_packet_core/
error.rs

1// SPDX-License-Identifier: MIT
2
3use std::{fmt, io, mem::size_of};
4
5use byteorder::{ByteOrder, NativeEndian};
6
7use crate::{DecodeError, Emitable, Field, Parseable, Rest};
8
9const CODE: Field = 0..4;
10const PAYLOAD: Rest = 4..;
11const ERROR_HEADER_LEN: usize = PAYLOAD.start;
12
13#[derive(Debug, PartialEq, Eq, Clone)]
14pub struct ErrorBuffer<T> {
15    buffer: T,
16}
17
18impl<T: AsRef<[u8]>> ErrorBuffer<T> {
19    pub fn new(buffer: T) -> ErrorBuffer<T> {
20        ErrorBuffer { buffer }
21    }
22
23    /// Consume the packet, returning the underlying buffer.
24    pub fn into_inner(self) -> T {
25        self.buffer
26    }
27
28    pub fn new_checked(buffer: T) -> Result<Self, DecodeError> {
29        let packet = Self::new(buffer);
30        packet.check_buffer_length()?;
31        Ok(packet)
32    }
33
34    fn check_buffer_length(&self) -> Result<(), DecodeError> {
35        let len = self.buffer.as_ref().len();
36        if len < ERROR_HEADER_LEN {
37            Err(format!(
38                "invalid ErrorBuffer: length is {} but ErrorBuffer are at least {} bytes",
39                len, ERROR_HEADER_LEN
40            )
41            .into())
42        } else {
43            Ok(())
44        }
45    }
46
47    /// Return the error code
48    pub fn code(&self) -> i32 {
49        let data = self.buffer.as_ref();
50        NativeEndian::read_i32(&data[CODE])
51    }
52}
53
54impl<'a, T: AsRef<[u8]> + ?Sized> ErrorBuffer<&'a T> {
55    /// Return a pointer to the payload.
56    pub fn payload(&self) -> &'a [u8] {
57        let data = self.buffer.as_ref();
58        &data[PAYLOAD]
59    }
60}
61
62impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> ErrorBuffer<&'a mut T> {
63    /// Return a mutable pointer to the payload.
64    pub fn payload_mut(&mut self) -> &mut [u8] {
65        let data = self.buffer.as_mut();
66        &mut data[PAYLOAD]
67    }
68}
69
70impl<T: AsRef<[u8]> + AsMut<[u8]>> ErrorBuffer<T> {
71    /// set the error code field
72    pub fn set_code(&mut self, value: i32) {
73        let data = self.buffer.as_mut();
74        NativeEndian::write_i32(&mut data[CODE], value)
75    }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct ErrorMessage {
80    pub code: i32,
81    pub header: Vec<u8>,
82}
83
84pub type AckMessage = ErrorMessage;
85
86impl Emitable for ErrorMessage {
87    fn buffer_len(&self) -> usize {
88        size_of::<i32>() + self.header.len()
89    }
90    fn emit(&self, buffer: &mut [u8]) {
91        let mut buffer = ErrorBuffer::new(buffer);
92        buffer.set_code(self.code);
93        buffer.payload_mut().copy_from_slice(&self.header)
94    }
95}
96
97impl<'buffer, T: AsRef<[u8]> + 'buffer> Parseable<ErrorBuffer<&'buffer T>> for ErrorMessage {
98    fn parse(buf: &ErrorBuffer<&'buffer T>) -> Result<ErrorMessage, DecodeError> {
99        // FIXME: The payload of an error is basically a truncated packet, which requires custom
100        // logic to parse correctly. For now we just return it as a Vec<u8>
101        // let header: NetlinkHeader = {
102        //     NetlinkBuffer::new_checked(self.payload())
103        //         .context("failed to parse netlink header")?
104        //         .parse()
105        //         .context("failed to parse nelink header")?
106        // };
107        Ok(ErrorMessage {
108            code: buf.code(),
109            header: buf.payload().to_vec(),
110        })
111    }
112}
113
114impl ErrorMessage {
115    /// According to [`netlink(7)`](https://linux.die.net/man/7/netlink)
116    /// the `NLMSG_ERROR` return Negative errno or 0 for acknowledgements.
117    ///
118    /// convert into [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
119    /// using the absolute value from errno code
120    pub fn to_io(&self) -> io::Error {
121        io::Error::from_raw_os_error(self.code.abs())
122    }
123}
124
125impl fmt::Display for ErrorMessage {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        fmt::Display::fmt(&self.to_io(), f)
128    }
129}
130
131impl From<ErrorMessage> for io::Error {
132    fn from(e: ErrorMessage) -> io::Error {
133        e.to_io()
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn into_io_error() {
143        let io_err = io::Error::from_raw_os_error(95);
144        let err_msg = ErrorMessage {
145            code: -95,
146            header: vec![],
147        };
148
149        let to_io: io::Error = err_msg.to_io();
150
151        assert_eq!(err_msg.to_string(), io_err.to_string());
152        assert_eq!(to_io.raw_os_error(), io_err.raw_os_error());
153    }
154}