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}