bgpkit_parser/parser/bmp/messages/
initiation_message.rs

1use 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, Clone, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct InitiationMessage {
10    pub tlvs: Vec<InitiationTlv>,
11}
12
13#[derive(Debug, PartialEq, Clone)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct InitiationTlv {
16    pub info_type: InitiationTlvType,
17    pub info_len: u16,
18    pub info: String,
19}
20
21///Type-Length-Value Type
22///
23/// https://www.iana.org/assignments/bmp-parameters/bmp-parameters.xhtml#initiation-peer-up-tlvs
24#[derive(Debug, TryFromPrimitive, IntoPrimitive, PartialEq, Clone, Copy)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[repr(u16)]
27pub enum InitiationTlvType {
28    String = 0,
29    SysDescr = 1,
30    SysName = 2,
31    VrTableName = 3,
32    AdminLabel = 4,
33}
34
35/// Parse BMP initiation message
36///
37/// <https://www.rfc-editor.org/rfc/rfc7854#section-4.3>
38pub fn parse_initiation_message(data: &mut Bytes) -> Result<InitiationMessage, ParserBmpError> {
39    let mut tlvs = vec![];
40
41    while data.remaining() > 4 {
42        let info_type: InitiationTlvType = InitiationTlvType::try_from(data.get_u16())?;
43        let info_len = data.get_u16();
44        if data.remaining() < info_len as usize {
45            // not enough bytes to read
46            break;
47        }
48        let info = data.read_n_bytes_to_string(info_len as usize)?;
49        tlvs.push(InitiationTlv {
50            info_type,
51            info_len,
52            info,
53        });
54    }
55
56    Ok(InitiationMessage { tlvs })
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use bytes::{BufMut, BytesMut};
63
64    #[test]
65    fn test_parse_initiation_message() {
66        let mut buffer = BytesMut::new();
67        buffer.put_u16(1); // InitiationTlvType::SysDescr
68        buffer.put_u16(5); // Length of following info
69        buffer.put_slice(b"Test1"); // Info
70
71        let mut bytes = buffer.freeze();
72
73        match parse_initiation_message(&mut bytes) {
74            Ok(initiation_message) => {
75                for tlv in initiation_message.tlvs {
76                    assert_eq!(tlv.info_type, InitiationTlvType::SysDescr);
77                    assert_eq!(tlv.info_len, 5);
78                    assert_eq!(tlv.info, "Test1".to_string());
79                }
80            }
81            Err(_) => panic!("Failed to parse initiation message"),
82        }
83    }
84
85    #[test]
86    fn test_debug() {
87        let initiation_message = InitiationMessage {
88            tlvs: vec![InitiationTlv {
89                info_type: InitiationTlvType::SysDescr,
90                info_len: 5,
91                info: "Test1".to_string(),
92            }],
93        };
94        assert_eq!(
95            format!("{:?}", initiation_message),
96            "InitiationMessage { tlvs: [InitiationTlv { info_type: SysDescr, info_len: 5, info: \"Test1\" }] }"
97        );
98    }
99}