Skip to main content

netlink_socket2/
error.rs

1use std::{fmt, io, sync::Arc};
2
3use netlink_bindings::{
4    builtin::{IterableNlmsgerrAttrs, NlmsgerrAttrs, Nlmsghdr},
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: Option<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: None,
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: None,
44            chained_name: None,
45        }
46    }
47}
48
49impl From<ReplyError> for io::Error {
50    fn from(value: ReplyError) -> Self {
51        value.code
52    }
53}
54
55impl ReplyError {
56    pub fn as_io_error(&self) -> &io::Error {
57        &self.code
58    }
59
60    pub fn ext_ack(&self) -> Option<IterableNlmsgerrAttrs<'_>> {
61        let Some(reply_buf) = &self.reply_buf else {
62            return None;
63        };
64        let (l, r) = self.ext_ack_bounds;
65        Some(NlmsgerrAttrs::new(&reply_buf[l as usize..r as usize]))
66    }
67
68    pub fn request(&self) -> Option<&[u8]> {
69        let Some(reply_buf) = &self.reply_buf else {
70            return None;
71        };
72        let (l, r) = self.request_bounds;
73        Some(&reply_buf[l as usize..r as usize])
74    }
75
76    pub(crate) fn has_context(&self) -> bool {
77        let mut res = false;
78        let (l, r) = self.ext_ack_bounds;
79        res |= l != r;
80
81        let (l, r) = self.request_bounds;
82        res |= l != r;
83
84        res
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 lookup = self.lookup.unwrap_or(|_, _, _| Default::default());
120            let (trace, attr) = lookup(
121                self.request().unwrap(),
122                missing_offset as usize - Nlmsghdr::len(),
123                missing_attr,
124            );
125
126            if let Some(attr) = attr {
127                write!(f, ": missing {attr:?}")?;
128            }
129            for (attrs, _) in trace.iter() {
130                write!(f, " in {attrs:?}")?;
131            }
132        }
133
134        if let Ok(invalid_offset) = ext_ack.get_offset() {
135            let lookup = self.lookup.unwrap_or(|_, _, _| Default::default());
136            let (trace, _) = lookup(
137                self.request().unwrap(),
138                invalid_offset as usize - Nlmsghdr::len(),
139                None,
140            );
141
142            if let Some((attr, _)) = trace.first() {
143                write!(f, ": attribute {attr:?}")?;
144            }
145            for (attrs, _) in trace.iter().skip(1) {
146                write!(f, " in {attrs:?}")?;
147            }
148            if let Ok(policy) = ext_ack.get_policy() {
149                write!(f, ": {policy:?}")?;
150            }
151        }
152
153        if ext_ack.get_buf().is_empty() {
154            write!(f, " (no extended ack)")?;
155        }
156
157        Ok(())
158    }
159}