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.advance(16);
98 let mut name_len = data.read_u16()?;
99 if name_len > 255 {
100 name_len = 255;
101 }
102 let admin_id = data.read_n_bytes_to_string(name_len as usize)?;
103
104 // read router IP
105 data.advance(16);
106 let ip: IpAddr = if is_router_ipv6 {
107 data.read_ipv6_address()?.into()
108 } else {
109 let ip = data.read_ipv4_address()?;
110 data.advance(12);
111 ip.into()
112 };
113
114 // read router group
115 let group = match data.read_u16()? {
116 0 => "".to_string(),
117 n => data.read_n_bytes_to_string(n as usize)?,
118 };
119
120 // read msg count
121 let row_count = data.read_u32()?;
122 if row_count != 1 {
123 return Err(ParserBmpError::InvalidOpenBmpHeader);
124 }
125
126 Ok(OpenBmpHeader {
127 major_version: version_major,
128 minor_version: version_minor,
129 header_len,
130 msg_len,
131 object_type,
132 timestamp,
133 admin_id,
134 router_ip: ip,
135 router_group: Some(group),
136 })
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_open_bmp_header() {
145 let input = "4f424d500107006400000033800c6184b9c2000c602cbf4f072f3ae149d23486024bc3dadfc4000a69732d63632d626d7031c677060bdd020a9e92be000200de2e3180df3369000000000000000000000000000c726f7574652d76696577733500000001030000003302000000000000000000000000000000000000000000003fda060e00000da30000000061523c36000c0e1c0200000a";
146 let decoded = hex::decode(input).unwrap();
147 let mut data = Bytes::from(decoded);
148 let _header = parse_openbmp_header(&mut data).unwrap();
149 }
150}