bgpkit_parser/models/bgp/
error.rs

1//! BGP error code module that maintains explicit error codes assigned by IANA.
2//!
3//! The full list of IANA error code assignments for BGP can be viewed at here:
4//! <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3>.
5use log::warn;
6use num_enum::{FromPrimitive, IntoPrimitive};
7
8#[derive(Copy, Clone, Debug, FromPrimitive, IntoPrimitive)]
9#[repr(u8)]
10pub enum BgpErrorCode {
11    Reserved = 0,
12    MessageHeaderError = 1,
13    OpenError = 2,
14    UpdateError = 3,
15    HoldTimerExpired = 4,
16    FiniteStateMachineError = 5,
17    CeaseNotification = 6,
18    RouteFreshError = 7,
19    #[num_enum(catch_all)]
20    Unknown(u8),
21}
22
23/// BGP Error Subcode enum.
24///
25/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-4>
26#[allow(non_camel_case_types)]
27#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub enum BgpError {
30    /// Includes subcode. Currently, no subcodes have been assigned.
31    Reserved(u8),
32    MessageHeaderError(MessageHeaderError),
33    OpenError(OpenError),
34    UpdateError(UpdateError),
35    /// Includes subcode. Currently, no subcodes have been assigned.
36    HoldTimerExpired(u8),
37    FiniteStateMachineError(FiniteStateMachineError),
38    CeaseNotification(CeaseNotification),
39    RouteFreshError(RouteRefreshError),
40    Unknown(u8, u8),
41}
42
43impl BgpError {
44    pub fn new(code: u8, subcode: u8) -> Self {
45        match BgpErrorCode::from(code) {
46            BgpErrorCode::Reserved => BgpError::Reserved(subcode),
47            BgpErrorCode::MessageHeaderError => {
48                BgpError::MessageHeaderError(MessageHeaderError::from(subcode))
49            }
50            BgpErrorCode::OpenError => BgpError::OpenError(OpenError::from(subcode)),
51            BgpErrorCode::UpdateError => BgpError::UpdateError(UpdateError::from(subcode)),
52            BgpErrorCode::HoldTimerExpired => BgpError::HoldTimerExpired(subcode),
53            BgpErrorCode::FiniteStateMachineError => {
54                BgpError::FiniteStateMachineError(FiniteStateMachineError::from(subcode))
55            }
56            BgpErrorCode::CeaseNotification => {
57                BgpError::CeaseNotification(CeaseNotification::from(subcode))
58            }
59            BgpErrorCode::RouteFreshError => {
60                BgpError::RouteFreshError(RouteRefreshError::from(subcode))
61            }
62            BgpErrorCode::Unknown(_) => {
63                warn!(
64                    "error parsing BGP notification error code: {}, subcode: {}",
65                    code, subcode
66                );
67                BgpError::Unknown(code, subcode)
68            }
69        }
70    }
71
72    pub fn get_codes(&self) -> (u8, u8) {
73        match self {
74            BgpError::Reserved(code) => (0, *code),
75            BgpError::MessageHeaderError(v) => (1, (*v).into()),
76            BgpError::OpenError(v) => (2, (*v).into()),
77            BgpError::UpdateError(v) => (3, (*v).into()),
78            BgpError::HoldTimerExpired(v) => (4, *v),
79            BgpError::FiniteStateMachineError(v) => (5, (*v).into()),
80            BgpError::CeaseNotification(v) => (6, (*v).into()),
81            BgpError::RouteFreshError(v) => (7, (*v).into()),
82            BgpError::Unknown(code, subcode) => (*code, *subcode),
83        }
84    }
85}
86
87/// Message Header Error subcodes
88///
89/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-5>
90///
91/// *See source code for number assignment*
92#[allow(non_camel_case_types)]
93#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95#[repr(u8)]
96pub enum MessageHeaderError {
97    UNSPECIFIC = 0,
98    CONNECTION_NOT_SYNCHRONIZED = 1,
99    BAD_MESSAGE_LENGTH = 2,
100    BAD_MESSAGE_TYPE = 3,
101    // 4 - 255: unassigned
102    #[num_enum(catch_all)]
103    Unknown(u8),
104}
105
106/// OPEN Message Error subcodes
107///
108/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-6>
109///
110/// *See source code for number assignment*
111#[allow(non_camel_case_types)]
112#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114#[repr(u8)]
115pub enum OpenError {
116    UNSPECIFIC = 0,
117    UNSUPPORTED_VERSION_NUMBER = 1,
118    BAD_PEER_AS = 2,
119    BAD_BGP_IDENTIFIER = 3,
120    UNSUPPORTED_OPTIONAL_PARAMETER = 4,
121    // 5 -- deprecated
122    UNACCEPTABLE_HOLD_TIME = 6,
123    UNSUPPORTED_CAPACITY = 7,
124    // 8 -- deprecated
125    // 9 -- deprecated
126    // 10 -- deprecated
127    ROLE_MISMATCH = 11,
128    // 12 - 255: unassinged
129    #[num_enum(catch_all)]
130    Unknown(u8),
131}
132
133impl OpenError {
134    pub const fn is_deprecated(&self) -> bool {
135        matches!(self, OpenError::Unknown(5 | 8 | 9 | 10))
136    }
137}
138
139/// UPDATE Message Error subcodes
140///
141/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-finite-state-machine-error-subcodes>
142///
143/// *See source code for number assignment*
144#[allow(non_camel_case_types)]
145#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147#[repr(u8)]
148pub enum UpdateError {
149    UNSPECIFIC = 0,
150    MALFORMED_ATTRIBUTE_LIST = 1,
151    UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2,
152    MISSING_WELL_KNOWN_ATTRIBUTE = 3,
153    ATTRIBUTE_FLAGS_ERROR = 4,
154    ATTRIBUTE_LENGTH_ERROR = 5,
155    INVALID_ORIGIN_ERROR = 6,
156    // 7 - deprecated
157    INVALID_NEXT_HOP_ATTRIBUTE = 8,
158    OPTIONAL_ATTRIBUTE_ERROR = 9,
159    INVALID_NETWORK_FIELD = 10,
160    MALFORMED_AS_PATH = 11,
161    // 12 - 255: unassigned
162    #[num_enum(catch_all)]
163    Unknown(u8),
164}
165
166impl UpdateError {
167    pub const fn is_deprecated(&self) -> bool {
168        matches!(self, UpdateError::Unknown(7))
169    }
170}
171
172/// BGP Finite State Machine Error Subcodes
173///
174/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-finite-state-machine-error-subcodes>
175///
176/// *See source code for number assignment*
177#[allow(non_camel_case_types)]
178#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
180#[repr(u8)]
181pub enum FiniteStateMachineError {
182    UNSPECIFIED = 0,
183    RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_State = 1,
184    RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE = 2,
185    RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE = 3,
186    // 4 - 255: unassigned
187    #[num_enum(catch_all)]
188    Unknown(u8),
189}
190
191/// BGP Cease NOTIFICATION message subcodes
192///
193/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-8>
194///
195/// *See source code for number assignment*
196#[allow(non_camel_case_types)]
197#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199#[repr(u8)]
200pub enum CeaseNotification {
201    RESERVED = 0,
202    MAXIMUM_NUMBER_OF_PREFIXES_REACHED = 1,
203    ADMINISTRATIVE_SHUTDOWN = 2,
204    PEER_DE_CONFIGURED = 3,
205    ADMINISTRATIVE_RESET = 4,
206    CONNECTION_REJECTED = 5,
207    OTHER_CONFIGURATION_CHANGE = 6,
208    CONNECTION_COLLISION_RESOLUTION = 7,
209    OUT_OF_RESOURCES = 8,
210    HARD_RESET = 9,
211    BFD_DOWN = 10, // TEMPORARY - registered 2022-02-23, expires 2023-02-23
212    // 11 - 255: unassigned
213    #[num_enum(catch_all)]
214    Unknown(u8),
215}
216
217/// BGP ROUTE-REFRESH Message Error subcodes
218///
219/// <https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#route-refresh-error-subcodes>
220///
221/// *See source code for number assignment*
222#[allow(non_camel_case_types)]
223#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
224#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
225#[repr(u8)]
226pub enum RouteRefreshError {
227    RESERVED = 0,
228    INVALID_MESSAGE_LENGTH = 1,
229    // 2 - 255: unassigned
230    #[num_enum(catch_all)]
231    Unknown(u8),
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    #[test]
239    fn test_parsing() {
240        assert_eq!(BgpError::new(0, 0), BgpError::Reserved(0));
241
242        assert_eq!(
243            BgpError::new(1, 0),
244            BgpError::MessageHeaderError(MessageHeaderError::UNSPECIFIC)
245        );
246        assert_eq!(
247            BgpError::new(1, 1),
248            BgpError::MessageHeaderError(MessageHeaderError::CONNECTION_NOT_SYNCHRONIZED)
249        );
250        assert_eq!(
251            BgpError::new(1, 2),
252            BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_LENGTH)
253        );
254        assert_eq!(
255            BgpError::new(1, 3),
256            BgpError::MessageHeaderError(MessageHeaderError::BAD_MESSAGE_TYPE)
257        );
258        assert_eq!(
259            BgpError::new(1, 4),
260            BgpError::MessageHeaderError(MessageHeaderError::Unknown(4)),
261        );
262
263        assert_eq!(
264            BgpError::new(2, 0),
265            BgpError::OpenError(OpenError::UNSPECIFIC)
266        );
267        assert_eq!(
268            BgpError::new(2, 1),
269            BgpError::OpenError(OpenError::UNSUPPORTED_VERSION_NUMBER)
270        );
271        assert_eq!(
272            BgpError::new(2, 2),
273            BgpError::OpenError(OpenError::BAD_PEER_AS)
274        );
275        assert_eq!(
276            BgpError::new(2, 3),
277            BgpError::OpenError(OpenError::BAD_BGP_IDENTIFIER)
278        );
279        assert_eq!(
280            BgpError::new(2, 4),
281            BgpError::OpenError(OpenError::UNSUPPORTED_OPTIONAL_PARAMETER)
282        );
283        assert_eq!(
284            BgpError::new(2, 6),
285            BgpError::OpenError(OpenError::UNACCEPTABLE_HOLD_TIME)
286        );
287        assert_eq!(
288            BgpError::new(2, 7),
289            BgpError::OpenError(OpenError::UNSUPPORTED_CAPACITY)
290        );
291        assert_eq!(
292            BgpError::new(2, 11),
293            BgpError::OpenError(OpenError::ROLE_MISMATCH)
294        );
295        // deprecated subcodes
296        for n in [5, 8, 9, 10] {
297            assert_eq!(
298                BgpError::new(2, n),
299                BgpError::OpenError(OpenError::Unknown(n))
300            );
301            assert!(OpenError::Unknown(n).is_deprecated());
302        }
303        assert_eq!(
304            BgpError::new(2, 12),
305            BgpError::OpenError(OpenError::Unknown(12))
306        );
307
308        assert_eq!(
309            BgpError::new(3, 0),
310            BgpError::UpdateError(UpdateError::UNSPECIFIC)
311        );
312        assert_eq!(
313            BgpError::new(3, 1),
314            BgpError::UpdateError(UpdateError::MALFORMED_ATTRIBUTE_LIST)
315        );
316        assert_eq!(
317            BgpError::new(3, 2),
318            BgpError::UpdateError(UpdateError::UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE)
319        );
320        assert_eq!(
321            BgpError::new(3, 3),
322            BgpError::UpdateError(UpdateError::MISSING_WELL_KNOWN_ATTRIBUTE)
323        );
324        assert_eq!(
325            BgpError::new(3, 4),
326            BgpError::UpdateError(UpdateError::ATTRIBUTE_FLAGS_ERROR)
327        );
328        assert_eq!(
329            BgpError::new(3, 5),
330            BgpError::UpdateError(UpdateError::ATTRIBUTE_LENGTH_ERROR)
331        );
332        assert_eq!(
333            BgpError::new(3, 6),
334            BgpError::UpdateError(UpdateError::INVALID_ORIGIN_ERROR)
335        );
336        assert_eq!(
337            BgpError::new(3, 8),
338            BgpError::UpdateError(UpdateError::INVALID_NEXT_HOP_ATTRIBUTE)
339        );
340        assert_eq!(
341            BgpError::new(3, 9),
342            BgpError::UpdateError(UpdateError::OPTIONAL_ATTRIBUTE_ERROR)
343        );
344        assert_eq!(
345            BgpError::new(3, 10),
346            BgpError::UpdateError(UpdateError::INVALID_NETWORK_FIELD)
347        );
348        assert_eq!(
349            BgpError::new(3, 11),
350            BgpError::UpdateError(UpdateError::MALFORMED_AS_PATH)
351        );
352        // deprecated subcodes
353        assert_eq!(
354            BgpError::new(3, 7),
355            BgpError::UpdateError(UpdateError::Unknown(7))
356        );
357        assert!(UpdateError::Unknown(7).is_deprecated());
358
359        assert_eq!(
360            BgpError::new(3, 12),
361            BgpError::UpdateError(UpdateError::Unknown(12))
362        );
363
364        assert_eq!(BgpError::new(4, 0), BgpError::HoldTimerExpired(0));
365        // subcode should not matter here
366        assert_eq!(BgpError::new(4, 1), BgpError::HoldTimerExpired(1));
367
368        assert_eq!(
369            BgpError::new(5, 0),
370            BgpError::FiniteStateMachineError(FiniteStateMachineError::UNSPECIFIED)
371        );
372        assert_eq!(
373            BgpError::new(5, 1),
374            BgpError::FiniteStateMachineError(
375                FiniteStateMachineError::RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_State
376            )
377        );
378        assert_eq!(
379            BgpError::new(5, 2),
380            BgpError::FiniteStateMachineError(
381                FiniteStateMachineError::RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE
382            )
383        );
384        assert_eq!(
385            BgpError::new(5, 3),
386            BgpError::FiniteStateMachineError(
387                FiniteStateMachineError::RECEIVE_UNEXPECTED_MESSAGE_IN_ESTABLISHED_STATE
388            )
389        );
390        assert_eq!(
391            BgpError::new(5, 4),
392            BgpError::FiniteStateMachineError(FiniteStateMachineError::Unknown(4))
393        );
394
395        assert_eq!(
396            BgpError::new(6, 0),
397            BgpError::CeaseNotification(CeaseNotification::RESERVED)
398        );
399        assert_eq!(
400            BgpError::new(6, 1),
401            BgpError::CeaseNotification(CeaseNotification::MAXIMUM_NUMBER_OF_PREFIXES_REACHED)
402        );
403        assert_eq!(
404            BgpError::new(6, 2),
405            BgpError::CeaseNotification(CeaseNotification::ADMINISTRATIVE_SHUTDOWN)
406        );
407        assert_eq!(
408            BgpError::new(6, 3),
409            BgpError::CeaseNotification(CeaseNotification::PEER_DE_CONFIGURED)
410        );
411        assert_eq!(
412            BgpError::new(6, 4),
413            BgpError::CeaseNotification(CeaseNotification::ADMINISTRATIVE_RESET)
414        );
415        assert_eq!(
416            BgpError::new(6, 5),
417            BgpError::CeaseNotification(CeaseNotification::CONNECTION_REJECTED)
418        );
419        assert_eq!(
420            BgpError::new(6, 6),
421            BgpError::CeaseNotification(CeaseNotification::OTHER_CONFIGURATION_CHANGE)
422        );
423        assert_eq!(
424            BgpError::new(6, 7),
425            BgpError::CeaseNotification(CeaseNotification::CONNECTION_COLLISION_RESOLUTION)
426        );
427        assert_eq!(
428            BgpError::new(6, 8),
429            BgpError::CeaseNotification(CeaseNotification::OUT_OF_RESOURCES)
430        );
431        assert_eq!(
432            BgpError::new(6, 9),
433            BgpError::CeaseNotification(CeaseNotification::HARD_RESET)
434        );
435        assert_eq!(
436            BgpError::new(6, 10),
437            BgpError::CeaseNotification(CeaseNotification::BFD_DOWN)
438        );
439        assert_eq!(
440            BgpError::new(6, 11),
441            BgpError::CeaseNotification(CeaseNotification::Unknown(11))
442        );
443
444        assert_eq!(
445            BgpError::new(7, 0),
446            BgpError::RouteFreshError(RouteRefreshError::RESERVED)
447        );
448        assert_eq!(
449            BgpError::new(7, 1),
450            BgpError::RouteFreshError(RouteRefreshError::INVALID_MESSAGE_LENGTH)
451        );
452        assert_eq!(
453            BgpError::new(7, 2),
454            BgpError::RouteFreshError(RouteRefreshError::Unknown(2))
455        );
456
457        assert_eq!(BgpError::new(8, 2), BgpError::Unknown(8, 2));
458    }
459}