bgpkit_parser/parser/bmp/messages/
termination_message.rs1use crate::parser::bmp::error::ParserBmpError;
2use crate::parser::ReadUtils;
3use bytes::{Buf, Bytes};
4use num_enum::{IntoPrimitive, TryFromPrimitive};
5use std::convert::TryFrom;
6
7#[derive(Debug, PartialEq, Clone)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct TerminationMessage {
10 pub tlvs: Vec<TerminationTlv>,
11}
12
13#[derive(Debug, PartialEq, Clone)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct TerminationTlv {
16 pub info_type: TerminationTlvType,
17 pub info_len: u16,
18 pub info_value: TerminationTlvValue,
19}
20
21#[derive(Debug, PartialEq, Clone)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub enum TerminationTlvValue {
24 String(String),
25 Reason(TerminationReason),
26}
27
28#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[repr(u16)]
31pub enum TerminationReason {
32 AdministrativelyClosed = 0,
33 UnspecifiedReason = 1,
34 OutOfResources = 2,
35 RedundantConnection = 3,
36 PermanentlyAdministrativelyClosed = 4,
37}
38
39#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[repr(u16)]
45pub enum TerminationTlvType {
46 String = 0,
47 Reason = 1,
48}
49
50pub fn parse_termination_message(data: &mut Bytes) -> Result<TerminationMessage, ParserBmpError> {
51 let mut tlvs = vec![];
52
53 while data.remaining() > 4 {
54 let info_type: TerminationTlvType = TerminationTlvType::try_from(data.read_u16()?)?;
55 let info_len = data.read_u16()?;
56 if data.remaining() < info_len as usize {
57 break;
59 }
60 let info_value = match info_type {
61 TerminationTlvType::String => {
62 let info = data.read_n_bytes_to_string(info_len as usize)?;
63 TerminationTlvValue::String(info)
64 }
65 TerminationTlvType::Reason => {
66 let reason = TerminationReason::try_from(data.read_u16()?)?;
67 TerminationTlvValue::Reason(reason)
68 }
69 };
70 tlvs.push(TerminationTlv {
71 info_type,
72 info_len,
73 info_value,
74 })
75 }
76
77 Ok(TerminationMessage { tlvs })
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use bytes::Bytes;
84
85 #[test]
86 fn test_parse_termination_message() {
87 let mut data = Bytes::copy_from_slice(&[
89 0, 0, 0, 5, 67, 79, 68, 69, 83, 0, 1, 0, 2, 0, 1, ]);
96
97 let result = parse_termination_message(&mut data);
99 match result {
100 Ok(termination_message) => {
101 assert_eq!(termination_message.tlvs.len(), 2);
102
103 assert_eq!(
105 termination_message.tlvs[0].info_type,
106 TerminationTlvType::String
107 );
108 assert_eq!(termination_message.tlvs[0].info_len, 5);
109 assert_eq!(
110 termination_message.tlvs[0].info_value,
111 TerminationTlvValue::String("CODES".to_string())
112 );
113
114 assert_eq!(
116 termination_message.tlvs[1].info_type,
117 TerminationTlvType::Reason
118 );
119 assert_eq!(termination_message.tlvs[1].info_len, 2);
120 assert_eq!(
121 termination_message.tlvs[1].info_value,
122 TerminationTlvValue::Reason(TerminationReason::UnspecifiedReason)
123 );
124 }
125 Err(e) => panic!("Failed to parse: {}", e),
126 }
127 }
128}