bgpkit_parser/parser/bmp/
openbmp.rs

1use crate::parser::bmp::error::ParserBmpError;
2use crate::parser::ReadUtils;
3use bytes::{Buf, Bytes};
4use std::net::IpAddr;
5
6///
7/// ```text
8///   0                   1                   2                   3
9///   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11///  |                   Magic Number (0x4F424D50)                   |
12///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13///  |   Major Ver.  |   Minor Ver.  |         Header Length         |
14///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15///  |                        Message Length                         |
16///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17///  |     Flags     |   Obj. Type   |
18///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19///  |                   Coll. Timestamp (seconds)                   |
20///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21///  |                 Coll. Timestamp (microseconds)                |
22///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23///  |                   Collector Hash (16 bytes)                   |
24///  ~                                                               ~
25///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26///  |     Coll. Admin ID Length     |
27///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28///  |                   Coll. Admin ID (variable)                   |
29///  ~                                                               ~
30///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31///  |                    Router Hash (16 bytes)                     |
32///  ~                                                               ~
33///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34///  |                     Router IP (16 bytes)                      |
35///  ~                                                               ~
36///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37///  |      Router Group Length      |
38///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39///  |                    Router Group (variable)                    |
40///  ~                                                               ~
41///  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42///  |                           Row Count                           |
43///  +---------------------------------------------------------------+
44/// ```
45#[derive(Debug)]
46pub struct OpenBmpHeader {
47    pub major_version: u8,
48    pub minor_version: u8,
49    /// Total number of bytes in the header (including version and header length fields)
50    pub header_len: u16,
51    /// Number of "data" bytes following the header
52    pub msg_len: u32,
53    pub object_type: u8,
54    pub timestamp: f64,
55    pub admin_id: String,
56    pub router_ip: IpAddr,
57    pub router_group: Option<String>,
58}
59
60pub fn parse_openbmp_header(data: &mut Bytes) -> Result<OpenBmpHeader, ParserBmpError> {
61    // read magic number
62    let magic_number = data.read_n_bytes_to_string(4)?;
63    if magic_number != "OBMP" {
64        return Err(ParserBmpError::InvalidOpenBmpHeader);
65    }
66
67    // read version numbers
68    let version_major = data.read_u8()?;
69    let version_minor = data.read_u8()?;
70    if (version_major, version_minor) != (1, 7) {
71        return Err(ParserBmpError::InvalidOpenBmpHeader);
72    }
73
74    // read msg lengths
75    let header_len = data.read_u16()?;
76    let msg_len = data.read_u32()?;
77
78    // read flags
79    let flags = data.read_u8()?;
80    let (is_router_msg, is_router_ipv6) = (flags & 0x80 != 0, flags & 0x40 != 0);
81    if !is_router_msg {
82        return Err(ParserBmpError::UnsupportedOpenBmpMessage);
83    }
84
85    // read object type
86    let object_type = data.read_u8()?;
87    if object_type != 12 {
88        return Err(ParserBmpError::UnsupportedOpenBmpMessage);
89    }
90
91    // read_timestamp
92    let t_sec = data.read_u32()?;
93    let t_usec = data.read_u32()?;
94    let timestamp = t_sec as f64 + (t_usec as f64) / 1_000_000.0;
95
96    // read admin-id
97    data.has_n_remaining(16)?;
98    data.advance(16);
99    let mut name_len = data.read_u16()?;
100    if name_len > 255 {
101        name_len = 255;
102    }
103    let admin_id = data.read_n_bytes_to_string(name_len as usize)?;
104
105    // read router IP
106    data.has_n_remaining(16)?;
107    data.advance(16);
108    let ip: IpAddr = if is_router_ipv6 {
109        data.read_ipv6_address()?.into()
110    } else {
111        let ip = data.read_ipv4_address()?;
112        data.has_n_remaining(12)?;
113        data.advance(12);
114        ip.into()
115    };
116
117    // read router group
118    let group = match data.read_u16()? {
119        0 => "".to_string(),
120        n => data.read_n_bytes_to_string(n as usize)?,
121    };
122
123    // read msg count
124    let row_count = data.read_u32()?;
125    if row_count != 1 {
126        return Err(ParserBmpError::InvalidOpenBmpHeader);
127    }
128
129    Ok(OpenBmpHeader {
130        major_version: version_major,
131        minor_version: version_minor,
132        header_len,
133        msg_len,
134        object_type,
135        timestamp,
136        admin_id,
137        router_ip: ip,
138        router_group: Some(group),
139    })
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_open_bmp_header() {
148        let input = "4f424d500107006400000033800c6184b9c2000c602cbf4f072f3ae149d23486024bc3dadfc4000a69732d63632d626d7031c677060bdd020a9e92be000200de2e3180df3369000000000000000000000000000c726f7574652d76696577733500000001030000003302000000000000000000000000000000000000000000003fda060e00000da30000000061523c36000c0e1c0200000a";
149        let decoded = hex::decode(input).unwrap();
150        let mut data = Bytes::from(decoded);
151        let _header = parse_openbmp_header(&mut data).unwrap();
152    }
153}