netlink_socket2/
error.rs

1use std::{fmt, io, sync::Arc};
2
3use netlink_bindings::{
4    builtin::{IterableNlmsgerrAttrs, NlmsgerrAttrs, PushNlmsghdr},
5    traits::LookupFn,
6    utils::ErrorContext,
7};
8
9use crate::RECV_BUF_SIZE;
10
11// For an error type to be convenient to use it has to be unconstrained by
12// lifetime bounds and generics, so it can be freely passed around in a call
13// chain, therefore the data buffers are ref counter.
14pub struct ReplyError {
15    pub(crate) code: io::Error,
16    pub(crate) reply_buf: Option<Arc<[u8; RECV_BUF_SIZE]>>,
17    pub(crate) ext_ack_bounds: (u32, u32),
18    pub(crate) request_bounds: (u32, u32),
19    pub(crate) lookup: LookupFn,
20    pub(crate) chained_name: Option<&'static str>,
21}
22
23impl From<ErrorContext> for ReplyError {
24    fn from(value: ErrorContext) -> Self {
25        Self {
26            code: io::Error::other(value),
27            reply_buf: None,
28            request_bounds: (0, 0),
29            ext_ack_bounds: (0, 0),
30            lookup: |_, _, _| Default::default(),
31            chained_name: None,
32        }
33    }
34}
35
36impl From<io::Error> for ReplyError {
37    fn from(value: io::Error) -> Self {
38        Self {
39            code: value,
40            reply_buf: None,
41            request_bounds: (0, 0),
42            ext_ack_bounds: (0, 0),
43            lookup: |_, _, _| Default::default(),
44            chained_name: None,
45        }
46    }
47}
48
49impl ReplyError {
50    pub fn as_io_error(&self) -> &io::Error {
51        &self.code
52    }
53
54    pub fn ext_ack(&self) -> Option<IterableNlmsgerrAttrs<'_>> {
55        let Some(reply_buf) = &self.reply_buf else {
56            return None;
57        };
58        let (l, r) = self.ext_ack_bounds;
59        Some(NlmsgerrAttrs::new(&reply_buf[l as usize..r as usize]))
60    }
61
62    pub fn request(&self) -> Option<&[u8]> {
63        let Some(reply_buf) = &self.reply_buf else {
64            return None;
65        };
66        let (l, r) = self.request_bounds;
67        Some(&reply_buf[l as usize..r as usize])
68    }
69
70    pub(crate) fn has_context(&self) -> bool {
71        let mut res = false;
72        let (l, r) = self.ext_ack_bounds;
73        res |= l != r;
74
75        let (l, r) = self.request_bounds;
76        res |= l != r;
77
78        res
79    }
80}
81
82impl From<ReplyError> for io::Error {
83    fn from(value: ReplyError) -> Self {
84        value.code
85    }
86}
87
88impl std::error::Error for ReplyError {}
89
90impl fmt::Debug for ReplyError {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        fmt::Display::fmt(self, f)
93    }
94}
95
96impl fmt::Display for ReplyError {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        write!(f, "{}", self.code)?;
99
100        let Some(ext_ack) = self.ext_ack() else {
101            return Ok(());
102        };
103
104        if let Ok(msg) = ext_ack.get_msg() {
105            f.write_str(": ")?;
106            match msg.to_str() {
107                Ok(m) => write!(f, "{m}")?,
108                Err(_) => write!(f, "{msg:?}")?,
109            }
110        }
111
112        if let Some(chained) = self.chained_name {
113            write!(f, " in {chained:?}")?;
114        }
115
116        if let Ok(missing_offset) = ext_ack.get_missing_nest() {
117            let missing_attr = ext_ack.get_missing_type().ok();
118
119            let (trace, attr) = (self.lookup)(
120                self.request().unwrap(),
121                missing_offset as usize - PushNlmsghdr::len(),
122                missing_attr,
123            );
124
125            if let Some(attr) = attr {
126                write!(f, ": missing {attr:?}")?;
127            }
128            for (attrs, _) in trace.iter() {
129                write!(f, " in {attrs:?}")?;
130            }
131        }
132
133        if let Ok(invalid_offset) = ext_ack.get_offset() {
134            let (trace, _) = (self.lookup)(
135                self.request().unwrap(),
136                invalid_offset as usize - PushNlmsghdr::len(),
137                None,
138            );
139
140            if let Some((attr, _)) = trace.first() {
141                write!(f, ": attribute {attr:?}")?;
142            }
143            for (attrs, _) in trace.iter().skip(1) {
144                write!(f, " in {attrs:?}")?;
145            }
146            if let Ok(policy) = ext_ack.get_policy() {
147                write!(f, ": {policy:?}")?;
148            }
149        }
150
151        if ext_ack.get_buf().is_empty() {
152            write!(f, " (no extended ack)")?;
153        }
154
155        Ok(())
156    }
157}