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
11pub 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}