bgpkit_parser/
error.rs

1/*!
2error module defines the error types used in bgpkit-parser.
3*/
4use 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}
24
25impl Error for ParserError {}
26
27#[derive(Debug)]
28pub struct ParserErrorWithBytes {
29    pub error: ParserError,
30    pub bytes: Option<Vec<u8>>,
31}
32
33impl Display for ParserErrorWithBytes {
34    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
35        write!(f, "{}", self.error)
36    }
37}
38
39impl Error for ParserErrorWithBytes {}
40
41/// implement Display trait for Error which satistifies the std::error::Error
42/// trait's requirement (must implement Display and Debug traits, Debug already derived)
43impl Display for ParserError {
44    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
45        match self {
46            ParserError::IoError(e) => write!(f, "Error: {e}"),
47            ParserError::EofError(e) => write!(f, "Error: {e}"),
48            ParserError::ParseError(s) => write!(f, "Error: {s}"),
49            ParserError::TruncatedMsg(s) => write!(f, "Error: {s}"),
50            ParserError::Unsupported(s) => write!(f, "Error: {s}"),
51            ParserError::EofExpected => write!(f, "Error: reach end of file"),
52            #[cfg(feature = "oneio")]
53            ParserError::OneIoError(e) => write!(f, "Error: {e}"),
54            ParserError::FilterError(e) => write!(f, "Error: {e}"),
55        }
56    }
57}
58
59#[cfg(feature = "oneio")]
60impl From<OneIoError> for ParserErrorWithBytes {
61    fn from(error: OneIoError) -> Self {
62        ParserErrorWithBytes {
63            error: ParserError::OneIoError(error),
64            bytes: None,
65        }
66    }
67}
68
69#[cfg(feature = "oneio")]
70impl From<OneIoError> for ParserError {
71    fn from(error: OneIoError) -> Self {
72        ParserError::OneIoError(error)
73    }
74}
75
76impl From<ParserError> for ParserErrorWithBytes {
77    fn from(error: ParserError) -> Self {
78        ParserErrorWithBytes { error, bytes: None }
79    }
80}
81
82impl From<io::Error> for ParserError {
83    fn from(io_error: io::Error) -> Self {
84        match io_error.kind() {
85            ErrorKind::UnexpectedEof => ParserError::EofError(io_error),
86            _ => ParserError::IoError(io_error),
87        }
88    }
89}
90
91impl From<TryFromPrimitiveError<Bgp4MpType>> for ParserError {
92    fn from(value: TryFromPrimitiveError<Bgp4MpType>) -> Self {
93        ParserError::ParseError(format!("cannot parse bgp4mp subtype: {}", value.number))
94    }
95}
96
97impl From<TryFromPrimitiveError<BgpState>> for ParserError {
98    fn from(value: TryFromPrimitiveError<BgpState>) -> Self {
99        ParserError::ParseError(format!("cannot parse bgp4mp state: {}", value.number))
100    }
101}
102
103impl From<TryFromPrimitiveError<TableDumpV2Type>> for ParserError {
104    fn from(value: TryFromPrimitiveError<TableDumpV2Type>) -> Self {
105        ParserError::ParseError(format!("cannot parse table dump v2 type: {}", value.number))
106    }
107}
108
109impl From<TryFromPrimitiveError<EntryType>> for ParserError {
110    fn from(value: TryFromPrimitiveError<EntryType>) -> Self {
111        ParserError::ParseError(format!("cannot parse entry type: {}", value.number))
112    }
113}
114
115impl From<TryFromPrimitiveError<Afi>> for ParserError {
116    fn from(value: TryFromPrimitiveError<Afi>) -> Self {
117        ParserError::ParseError(format!("Unknown AFI type: {}", value.number))
118    }
119}
120
121impl From<TryFromPrimitiveError<Safi>> for ParserError {
122    fn from(value: TryFromPrimitiveError<Safi>) -> Self {
123        ParserError::ParseError(format!("Unknown SAFI type: {}", value.number))
124    }
125}
126
127/// BGP validation warnings for RFC 7606 compliant error handling.
128/// These represent non-fatal validation issues that don't prevent parsing.
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub enum BgpValidationWarning {
131    /// Attribute flags conflict with attribute type code (RFC 4271 Section 6.3)
132    AttributeFlagsError {
133        attr_type: AttrType,
134        expected_flags: u8,
135        actual_flags: u8,
136    },
137    /// Attribute length conflicts with expected length (RFC 4271 Section 6.3)
138    AttributeLengthError {
139        attr_type: AttrType,
140        expected_length: Option<usize>,
141        actual_length: usize,
142    },
143    /// Missing well-known mandatory attribute (RFC 4271 Section 6.3)
144    MissingWellKnownAttribute { attr_type: AttrType },
145    /// Unrecognized well-known attribute (RFC 4271 Section 6.3)
146    UnrecognizedWellKnownAttribute { attr_type_code: u8 },
147    /// Invalid origin attribute value (RFC 4271 Section 6.3)
148    InvalidOriginAttribute { value: u8 },
149    /// Invalid next hop attribute (RFC 4271 Section 6.3)
150    InvalidNextHopAttribute { reason: String },
151    /// Malformed AS_PATH attribute (RFC 4271 Section 6.3)
152    MalformedAsPath { reason: String },
153    /// Optional attribute error (RFC 4271 Section 6.3)
154    OptionalAttributeError { attr_type: AttrType, reason: String },
155    /// Attribute appears more than once (RFC 4271 Section 6.3)
156    DuplicateAttribute { attr_type: AttrType },
157    /// Invalid network field in NLRI (RFC 4271 Section 6.3)
158    InvalidNetworkField { reason: String },
159    /// Malformed attribute list (RFC 4271 Section 6.3)
160    MalformedAttributeList { reason: String },
161    /// Partial attribute with errors (RFC 7606)
162    PartialAttributeError { attr_type: AttrType, reason: String },
163}
164
165impl Display for BgpValidationWarning {
166    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
167        match self {
168            BgpValidationWarning::AttributeFlagsError { attr_type, expected_flags, actual_flags } => {
169                write!(f, "Attribute flags error for {attr_type:?}: expected 0x{expected_flags:02x}, got 0x{actual_flags:02x}")
170            }
171            BgpValidationWarning::AttributeLengthError { attr_type, expected_length, actual_length } => {
172                match expected_length {
173                    Some(expected) => write!(f, "Attribute length error for {attr_type:?}: expected {expected}, got {actual_length}"),
174                    None => write!(f, "Attribute length error for {attr_type:?}: invalid length {actual_length}"),
175                }
176            }
177            BgpValidationWarning::MissingWellKnownAttribute { attr_type } => {
178                write!(f, "Missing well-known mandatory attribute: {attr_type:?}")
179            }
180            BgpValidationWarning::UnrecognizedWellKnownAttribute { attr_type_code } => {
181                write!(f, "Unrecognized well-known attribute: type code {attr_type_code}")
182            }
183            BgpValidationWarning::InvalidOriginAttribute { value } => {
184                write!(f, "Invalid origin attribute value: {value}")
185            }
186            BgpValidationWarning::InvalidNextHopAttribute { reason } => {
187                write!(f, "Invalid next hop attribute: {reason}")
188            }
189            BgpValidationWarning::MalformedAsPath { reason } => {
190                write!(f, "Malformed AS_PATH: {reason}")
191            }
192            BgpValidationWarning::OptionalAttributeError { attr_type, reason } => {
193                write!(f, "Optional attribute error for {attr_type:?}: {reason}")
194            }
195            BgpValidationWarning::DuplicateAttribute { attr_type } => {
196                write!(f, "Duplicate attribute: {attr_type:?}")
197            }
198            BgpValidationWarning::InvalidNetworkField { reason } => {
199                write!(f, "Invalid network field: {reason}")
200            }
201            BgpValidationWarning::MalformedAttributeList { reason } => {
202                write!(f, "Malformed attribute list: {reason}")
203            }
204            BgpValidationWarning::PartialAttributeError { attr_type, reason } => {
205                write!(f, "Partial attribute error for {attr_type:?}: {reason}")
206            }
207        }
208    }
209}
210
211/// Result type for BGP attribute parsing that includes validation warnings
212#[derive(Debug, Clone)]
213pub struct BgpValidationResult<T> {
214    pub value: T,
215    pub warnings: Vec<BgpValidationWarning>,
216}
217
218impl<T> BgpValidationResult<T> {
219    pub fn new(value: T) -> Self {
220        Self {
221            value,
222            warnings: Vec::new(),
223        }
224    }
225
226    pub fn with_warnings(value: T, warnings: Vec<BgpValidationWarning>) -> Self {
227        Self { value, warnings }
228    }
229
230    pub fn add_warning(&mut self, warning: BgpValidationWarning) {
231        self.warnings.push(warning);
232    }
233
234    pub fn has_warnings(&self) -> bool {
235        !self.warnings.is_empty()
236    }
237}