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
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: 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}