1use 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#[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 Reserved(u8),
32 MessageHeaderError(MessageHeaderError),
33 OpenError(OpenError),
34 UpdateError(UpdateError),
35 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#[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 #[num_enum(catch_all)]
103 Unknown(u8),
104}
105
106#[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 UNACCEPTABLE_HOLD_TIME = 6,
123 UNSUPPORTED_CAPACITY = 7,
124 ROLE_MISMATCH = 11,
128 #[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#[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 INVALID_NEXT_HOP_ATTRIBUTE = 8,
158 OPTIONAL_ATTRIBUTE_ERROR = 9,
159 INVALID_NETWORK_FIELD = 10,
160 MALFORMED_AS_PATH = 11,
161 #[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#[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 #[num_enum(catch_all)]
188 Unknown(u8),
189}
190
191#[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, #[num_enum(catch_all)]
214 Unknown(u8),
215}
216
217#[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 #[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 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 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 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}