1use crate::models::{Afi, AttrType, Bgp4MpType, BgpState, EntryType, Safi, TableDumpV2Type};
5use num_enum::TryFromPrimitiveError;
6#[cfg(feature = "oneio")]
7use oneio::OneIoError;
8use std::fmt::{Display, Formatter};
9use std::io::ErrorKind;
10use std::{error::Error, fmt, io};
11
12#[derive(Debug)]
13pub enum ParserError {
14 IoError(io::Error),
15 EofError(io::Error),
16 #[cfg(feature = "oneio")]
17 OneIoError(OneIoError),
18 EofExpected,
19 ParseError(String),
20 TruncatedMsg(String),
21 Unsupported(String),
22 FilterError(String),
23 InvalidLabeledNlriLength,
26 TruncatedLabeledNlri,
28 TruncatedPrefix,
30 MaxLabelStackDepthExceeded,
32 PeerMaxLabelsExceeded,
35 InvalidPrefix,
37}
38
39impl Error for ParserError {}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
48#[non_exhaustive]
49pub enum EncodingError {
50 ValueTooLarge {
56 field: &'static str,
57 actual: usize,
58 max: usize,
59 },
60 Unencodable { field: &'static str, reason: String },
64}
65
66impl EncodingError {
67 pub(crate) fn too_large(field: &'static str, actual: usize, max: usize) -> Self {
68 EncodingError::ValueTooLarge { field, actual, max }
69 }
70
71 #[cfg(feature = "parser")]
72 pub(crate) fn unencodable(field: &'static str, reason: impl Into<String>) -> Self {
73 EncodingError::Unencodable {
74 field,
75 reason: reason.into(),
76 }
77 }
78}
79
80pub(crate) fn check_max(
86 field: &'static str,
87 actual: usize,
88 max: usize,
89) -> Result<usize, EncodingError> {
90 if actual > max {
91 return Err(EncodingError::too_large(field, actual, max));
92 }
93 Ok(actual)
94}
95
96impl Display for EncodingError {
97 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
98 match self {
99 EncodingError::ValueTooLarge { field, actual, max } => write!(
100 f,
101 "encoding error: {field} ({actual}) exceeds maximum ({max})"
102 ),
103 EncodingError::Unencodable { field, reason } => {
104 write!(f, "encoding error: {field} cannot be encoded: {reason}")
105 }
106 }
107 }
108}
109
110impl Error for EncodingError {}
111
112#[derive(Debug)]
113pub struct ParserErrorWithBytes {
114 pub error: ParserError,
115 pub bytes: Option<Vec<u8>>,
116}
117
118impl Display for ParserErrorWithBytes {
119 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
120 write!(f, "{}", self.error)
121 }
122}
123
124impl Error for ParserErrorWithBytes {}
125
126impl Display for ParserError {
129 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
130 match self {
131 ParserError::IoError(e) => write!(f, "Error: {e}"),
132 ParserError::EofError(e) => write!(f, "Error: {e}"),
133 ParserError::ParseError(s) => write!(f, "Error: {s}"),
134 ParserError::TruncatedMsg(s) => write!(f, "Error: {s}"),
135 ParserError::Unsupported(s) => write!(f, "Error: {s}"),
136 ParserError::EofExpected => write!(f, "Error: reach end of file"),
137 #[cfg(feature = "oneio")]
138 ParserError::OneIoError(e) => write!(f, "Error: {e}"),
139 ParserError::FilterError(e) => write!(f, "Error: {e}"),
140 ParserError::InvalidLabeledNlriLength => {
141 write!(f, "Error: invalid labeled NLRI length field")
142 }
143 ParserError::TruncatedLabeledNlri => write!(f, "Error: truncated labeled NLRI"),
144 ParserError::TruncatedPrefix => write!(f, "Error: truncated prefix in NLRI"),
145 ParserError::MaxLabelStackDepthExceeded => write!(
146 f,
147 "Error: max label stack depth exceeded without finding BoS bit"
148 ),
149 ParserError::PeerMaxLabelsExceeded => write!(
150 f,
151 "Error: received more labels than peer advertised maximum"
152 ),
153 ParserError::InvalidPrefix => write!(f, "Error: invalid prefix in NLRI"),
154 }
155 }
156}
157
158#[cfg(feature = "oneio")]
159impl From<OneIoError> for ParserErrorWithBytes {
160 fn from(error: OneIoError) -> Self {
161 ParserErrorWithBytes {
162 error: ParserError::OneIoError(error),
163 bytes: None,
164 }
165 }
166}
167
168#[cfg(feature = "oneio")]
169impl From<OneIoError> for ParserError {
170 fn from(error: OneIoError) -> Self {
171 ParserError::OneIoError(error)
172 }
173}
174
175impl From<ParserError> for ParserErrorWithBytes {
176 fn from(error: ParserError) -> Self {
177 ParserErrorWithBytes { error, bytes: None }
178 }
179}
180
181impl From<io::Error> for ParserError {
182 fn from(io_error: io::Error) -> Self {
183 match io_error.kind() {
184 ErrorKind::UnexpectedEof => ParserError::EofError(io_error),
185 _ => ParserError::IoError(io_error),
186 }
187 }
188}
189
190impl From<TryFromPrimitiveError<Bgp4MpType>> for ParserError {
191 fn from(value: TryFromPrimitiveError<Bgp4MpType>) -> Self {
192 ParserError::ParseError(format!("cannot parse bgp4mp subtype: {}", value.number))
193 }
194}
195
196impl From<TryFromPrimitiveError<BgpState>> for ParserError {
197 fn from(value: TryFromPrimitiveError<BgpState>) -> Self {
198 ParserError::ParseError(format!("cannot parse bgp4mp state: {}", value.number))
199 }
200}
201
202impl From<TryFromPrimitiveError<TableDumpV2Type>> for ParserError {
203 fn from(value: TryFromPrimitiveError<TableDumpV2Type>) -> Self {
204 ParserError::ParseError(format!("cannot parse table dump v2 type: {}", value.number))
205 }
206}
207
208impl From<TryFromPrimitiveError<EntryType>> for ParserError {
209 fn from(value: TryFromPrimitiveError<EntryType>) -> Self {
210 ParserError::ParseError(format!("cannot parse entry type: {}", value.number))
211 }
212}
213
214impl From<TryFromPrimitiveError<Afi>> for ParserError {
215 fn from(value: TryFromPrimitiveError<Afi>) -> Self {
216 ParserError::ParseError(format!("Unknown AFI type: {}", value.number))
217 }
218}
219
220impl From<TryFromPrimitiveError<Safi>> for ParserError {
221 fn from(value: TryFromPrimitiveError<Safi>) -> Self {
222 ParserError::ParseError(format!("Unknown SAFI type: {}", value.number))
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq)]
232#[cfg_attr(feature = "serde", derive(serde::Serialize))]
233#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
234#[non_exhaustive]
235pub enum BgpValidationWarning {
236 AttributeFlagsError {
238 attr_type: AttrType,
239 expected_flags: u8,
240 actual_flags: u8,
241 },
242 AttributeLengthError {
244 attr_type: AttrType,
245 expected_length: Option<usize>,
246 actual_length: usize,
247 },
248 MissingWellKnownAttribute { attr_type: AttrType },
250 UnrecognizedWellKnownAttribute { attr_type_code: u8 },
252 InvalidOriginAttribute { value: u8 },
254 InvalidNextHopAttribute { reason: String },
256 MalformedAsPath { reason: String },
258 OptionalAttributeError { attr_type: AttrType, reason: String },
260 DuplicateAttribute { attr_type: AttrType },
262 InvalidNetworkField { reason: String },
264 MalformedAttributeList { reason: String },
266 PartialAttributeError { attr_type: AttrType, reason: String },
268 MalformedNlri {
278 nlri_type: &'static str,
280 reason: String,
282 raw_bytes: Vec<u8>,
284 },
285 UnknownRouteRefreshSubtype { subtype: u8 },
289 InvalidRouteRefreshLength {
295 subtype: u8,
296 length: usize,
298 },
299}
300
301impl Display for BgpValidationWarning {
302 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
303 match self {
304 BgpValidationWarning::AttributeFlagsError { attr_type, expected_flags, actual_flags } => {
305 write!(f, "Attribute flags error for {attr_type:?}: expected 0x{expected_flags:02x}, got 0x{actual_flags:02x}")
306 }
307 BgpValidationWarning::AttributeLengthError { attr_type, expected_length, actual_length } => {
308 match expected_length {
309 Some(expected) => write!(f, "Attribute length error for {attr_type:?}: expected {expected}, got {actual_length}"),
310 None => write!(f, "Attribute length error for {attr_type:?}: invalid length {actual_length}"),
311 }
312 }
313 BgpValidationWarning::MissingWellKnownAttribute { attr_type } => {
314 write!(f, "Missing well-known mandatory attribute: {attr_type:?}")
315 }
316 BgpValidationWarning::UnrecognizedWellKnownAttribute { attr_type_code } => {
317 write!(f, "Unrecognized well-known attribute: type code {attr_type_code}")
318 }
319 BgpValidationWarning::InvalidOriginAttribute { value } => {
320 write!(f, "Invalid origin attribute value: {value}")
321 }
322 BgpValidationWarning::InvalidNextHopAttribute { reason } => {
323 write!(f, "Invalid next hop attribute: {reason}")
324 }
325 BgpValidationWarning::MalformedAsPath { reason } => {
326 write!(f, "Malformed AS_PATH: {reason}")
327 }
328 BgpValidationWarning::OptionalAttributeError { attr_type, reason } => {
329 write!(f, "Optional attribute error for {attr_type:?}: {reason}")
330 }
331 BgpValidationWarning::DuplicateAttribute { attr_type } => {
332 write!(f, "Duplicate attribute: {attr_type:?}")
333 }
334 BgpValidationWarning::InvalidNetworkField { reason } => {
335 write!(f, "Invalid network field: {reason}")
336 }
337 BgpValidationWarning::MalformedAttributeList { reason } => {
338 write!(f, "Malformed attribute list: {reason}")
339 }
340 BgpValidationWarning::PartialAttributeError { attr_type, reason } => {
341 write!(f, "Partial attribute error for {attr_type:?}: {reason}")
342 }
343 BgpValidationWarning::MalformedNlri { nlri_type, reason, .. } => {
344 write!(f, "Malformed NLRI ({nlri_type}): {reason}")
345 }
346 BgpValidationWarning::UnknownRouteRefreshSubtype { subtype } => {
347 write!(f, "Unknown ROUTE-REFRESH message subtype: {subtype}")
348 }
349 BgpValidationWarning::InvalidRouteRefreshLength { subtype, length } => {
350 write!(f, "Invalid ROUTE-REFRESH message length for subtype {subtype}: body is {length} bytes, expected 4")
351 }
352 }
353 }
354}
355
356#[derive(Debug, Clone)]
358pub struct BgpValidationResult<T> {
359 pub value: T,
360 pub warnings: Vec<BgpValidationWarning>,
361}
362
363impl<T> BgpValidationResult<T> {
364 pub fn new(value: T) -> Self {
365 Self {
366 value,
367 warnings: Vec::new(),
368 }
369 }
370
371 pub fn with_warnings(value: T, warnings: Vec<BgpValidationWarning>) -> Self {
372 Self { value, warnings }
373 }
374
375 pub fn add_warning(&mut self, warning: BgpValidationWarning) {
376 self.warnings.push(warning);
377 }
378
379 pub fn has_warnings(&self) -> bool {
380 !self.warnings.is_empty()
381 }
382}