Skip to main content

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