1use super::mrt_header::parse_common_header_with_bytes;
2use crate::bmp::messages::{BmpMessage, BmpMessageBody};
3use crate::error::ParserError;
4use crate::models::*;
5use crate::parser::{
6 parse_bgp4mp, parse_table_dump_message, parse_table_dump_v2_message, ParserErrorWithBytes,
7};
8use crate::utils::convert_timestamp;
9use bytes::{BufMut, Bytes, BytesMut};
10use log::warn;
11use std::convert::TryFrom;
12use std::fs::File;
13use std::io::{Read, Write};
14use std::net::IpAddr;
15use std::path::Path;
16use std::str::FromStr;
17
18#[derive(Debug, Clone)]
22pub struct RawMrtRecord {
23 pub common_header: CommonHeader,
24 pub header_bytes: Bytes,
26 pub message_bytes: Bytes,
28}
29
30impl RawMrtRecord {
31 pub fn parse(self) -> Result<MrtRecord, ParserError> {
34 let message = parse_mrt_body(
35 self.common_header.entry_type as u16,
36 self.common_header.entry_subtype,
37 self.message_bytes,
38 )?;
39
40 Ok(MrtRecord {
41 common_header: self.common_header,
42 message,
43 })
44 }
45
46 pub fn raw_bytes(&self) -> Bytes {
59 let mut bytes = BytesMut::with_capacity(self.header_bytes.len() + self.message_bytes.len());
60 bytes.put_slice(&self.header_bytes);
61 bytes.put_slice(&self.message_bytes);
62 bytes.freeze()
63 }
64
65 pub fn write_raw_bytes<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
79 let mut file = File::create(path)?;
80 file.write_all(&self.header_bytes)?;
81 file.write_all(&self.message_bytes)?;
82 Ok(())
83 }
84
85 pub fn append_raw_bytes<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
101 let mut file = std::fs::OpenOptions::new()
102 .create(true)
103 .append(true)
104 .open(path)?;
105 file.write_all(&self.header_bytes)?;
106 file.write_all(&self.message_bytes)?;
107 Ok(())
108 }
109
110 pub fn total_bytes_len(&self) -> usize {
112 self.header_bytes.len() + self.message_bytes.len()
113 }
114}
115
116pub fn chunk_mrt_record(input: &mut impl Read) -> Result<RawMrtRecord, ParserErrorWithBytes> {
117 let parsed_header = match parse_common_header_with_bytes(input) {
119 Ok(v) => v,
120 Err(e) => {
121 if let ParserError::EofError(e) = &e {
122 if e.kind() == std::io::ErrorKind::UnexpectedEof {
123 return Err(ParserErrorWithBytes::from(ParserError::EofExpected));
124 }
125 }
126 return Err(ParserErrorWithBytes {
127 error: e,
128 bytes: None,
129 });
130 }
131 };
132
133 let common_header = parsed_header.header;
134 let header_bytes = parsed_header.raw_bytes;
135
136 const MAX_MRT_MESSAGE_LEN: u32 = 16 * 1024 * 1024; if common_header.length > MAX_MRT_MESSAGE_LEN {
139 return Err(ParserErrorWithBytes::from(ParserError::Unsupported(
140 format!("MRT message too large: {} bytes", common_header.length),
141 )));
142 }
143
144 let mut buffer = BytesMut::zeroed(common_header.length as usize);
146 match input
147 .take(common_header.length as u64)
148 .read_exact(&mut buffer)
149 {
150 Ok(_) => {}
151 Err(e) => {
152 return Err(ParserErrorWithBytes {
153 error: ParserError::IoError(e),
154 bytes: None,
155 })
156 }
157 }
158
159 Ok(RawMrtRecord {
160 common_header,
161 header_bytes,
162 message_bytes: buffer.freeze(),
163 })
164}
165
166pub fn parse_mrt_record(input: &mut impl Read) -> Result<MrtRecord, ParserErrorWithBytes> {
167 let raw_record = chunk_mrt_record(input)?;
168 match raw_record.clone().parse() {
171 Ok(record) => Ok(record),
172 Err(e) => Err(ParserErrorWithBytes {
173 error: e,
174 bytes: Some(raw_record.raw_bytes().to_vec()),
175 }),
176 }
177}
178
179pub fn parse_mrt_body(
185 entry_type: u16,
186 entry_subtype: u16,
187 data: Bytes,
188) -> Result<MrtMessage, ParserError> {
189 let etype = EntryType::try_from(entry_type)?;
190
191 let message: MrtMessage = match &etype {
192 EntryType::TABLE_DUMP => {
193 let msg = parse_table_dump_message(entry_subtype, data);
194 match msg {
195 Ok(msg) => MrtMessage::TableDumpMessage(msg),
196 Err(e) => {
197 return Err(e);
198 }
199 }
200 }
201 EntryType::TABLE_DUMP_V2 => {
202 let msg = parse_table_dump_v2_message(entry_subtype, data);
203 match msg {
204 Ok(msg) => MrtMessage::TableDumpV2Message(msg),
205 Err(e) => {
206 return Err(e);
207 }
208 }
209 }
210 EntryType::BGP4MP | EntryType::BGP4MP_ET => {
211 let msg = parse_bgp4mp(entry_subtype, data);
212 match msg {
213 Ok(msg) => MrtMessage::Bgp4Mp(msg),
214 Err(e) => {
215 return Err(e);
216 }
217 }
218 }
219 v => {
220 return Err(ParserError::Unsupported(format!(
222 "unsupported MRT type: {v:?}"
223 )));
224 }
225 };
226 Ok(message)
227}
228
229impl MrtRecord {
230 pub fn encode(&self) -> Bytes {
231 let message_bytes = self.message.encode(self.common_header.entry_subtype);
232 let mut new_header = self.common_header;
233 if message_bytes.len() != new_header.length as usize {
234 warn!(
235 "message length {} does not match the length in the header {} (encoding MRT record)",
236 message_bytes.len(),
237 new_header.length
238 );
239 }
240 new_header.length = message_bytes.len() as u32;
241 let header_bytes = new_header.encode();
242
243 let mut bytes = BytesMut::with_capacity(header_bytes.len() + message_bytes.len());
254 bytes.put_slice(&header_bytes);
255 bytes.put_slice(&message_bytes);
256 bytes.freeze()
257 }
258}
259
260impl TryFrom<&BmpMessage> for MrtRecord {
261 type Error = String;
262
263 fn try_from(bmp_message: &BmpMessage) -> Result<Self, Self::Error> {
264 let bgp_message = match &bmp_message.message_body {
265 BmpMessageBody::RouteMonitoring(m) => &m.bgp_message,
266 _ => return Err("unsupported bmp message type".to_string()),
267 };
268 let bmp_header = match &bmp_message.per_peer_header {
269 Some(h) => h,
270 None => return Err("missing per peer header".to_string()),
271 };
272
273 let local_ip = match bmp_header.peer_ip {
274 IpAddr::V4(_) => IpAddr::from_str("0.0.0.0").unwrap(),
275 IpAddr::V6(_) => IpAddr::from_str("::").unwrap(),
276 };
277 let local_asn = match bmp_header.peer_asn.is_four_byte() {
278 true => Asn::new_32bit(0),
279 false => Asn::new_16bit(0),
280 };
281
282 let bgp4mp_message = Bgp4MpMessage {
283 msg_type: Bgp4MpType::MessageAs4, peer_asn: bmp_header.peer_asn,
285 local_asn,
286 interface_index: 0,
287 peer_ip: bmp_header.peer_ip,
288 local_ip,
289 bgp_message: bgp_message.clone(),
290 };
291
292 let mrt_message = MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(bgp4mp_message));
293
294 let (seconds, microseconds) = convert_timestamp(bmp_header.timestamp);
295
296 let subtype = Bgp4MpType::MessageAs4 as u16;
297 let mrt_header = CommonHeader {
298 timestamp: seconds,
299 microsecond_timestamp: Some(microseconds),
300 entry_type: EntryType::BGP4MP_ET,
301 entry_subtype: Bgp4MpType::MessageAs4 as u16,
302 length: mrt_message.encode(subtype).len() as u32,
303 };
304
305 Ok(MrtRecord {
306 common_header: mrt_header,
307 message: mrt_message,
308 })
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use crate::bmp::messages::headers::{BmpPeerType, PeerFlags, PerPeerFlags};
316 use crate::bmp::messages::{BmpCommonHeader, BmpMsgType, BmpPerPeerHeader, RouteMonitoring};
317 use std::io::Cursor;
318 use std::net::Ipv4Addr;
319 use tempfile::tempdir;
320
321 #[test]
322 fn test_raw_mrt_record_raw_bytes() {
323 let header = CommonHeader {
324 timestamp: 1609459200,
325 microsecond_timestamp: None,
326 entry_type: EntryType::BGP4MP,
327 entry_subtype: 4,
328 length: 10,
329 };
330 let header_bytes = Bytes::from_static(&[
331 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, ]);
336 let message_bytes = Bytes::from_static(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
337
338 let raw_record = RawMrtRecord {
339 common_header: header,
340 header_bytes,
341 message_bytes,
342 };
343
344 let mrt_bytes = raw_record.raw_bytes();
345 assert_eq!(mrt_bytes.len(), 22);
347 assert_eq!(raw_record.total_bytes_len(), 22);
348 }
349
350 #[test]
351 fn test_raw_mrt_record_raw_bytes_with_et() {
352 let header = CommonHeader {
353 timestamp: 1609459200,
354 microsecond_timestamp: Some(500000),
355 entry_type: EntryType::BGP4MP_ET,
356 entry_subtype: 4,
357 length: 10,
358 };
359 let header_bytes = Bytes::from_static(&[
360 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x07, 0xa1, 0x20, ]);
366 let message_bytes = Bytes::from_static(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
367
368 let raw_record = RawMrtRecord {
369 common_header: header,
370 header_bytes,
371 message_bytes,
372 };
373
374 let mrt_bytes = raw_record.raw_bytes();
375 assert_eq!(mrt_bytes.len(), 26);
377 assert_eq!(raw_record.total_bytes_len(), 26);
378 }
379
380 #[test]
381 fn test_raw_mrt_record_write_to_file() {
382 let dir = tempdir().unwrap();
383 let file_path = dir.path().join("test_record.mrt");
384
385 let header = CommonHeader {
386 timestamp: 1609459200,
387 microsecond_timestamp: None,
388 entry_type: EntryType::BGP4MP,
389 entry_subtype: 4,
390 length: 5,
391 };
392 let header_bytes = Bytes::from_static(&[
393 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, ]);
398 let message_bytes = Bytes::from_static(&[1, 2, 3, 4, 5]);
399
400 let raw_record = RawMrtRecord {
401 common_header: header,
402 header_bytes,
403 message_bytes,
404 };
405
406 raw_record.write_raw_bytes(&file_path).unwrap();
407
408 let written_bytes = std::fs::read(&file_path).unwrap();
409 assert_eq!(written_bytes.len(), 17); }
411
412 #[test]
413 fn test_raw_mrt_record_append_to_file() {
414 let dir = tempdir().unwrap();
415 let file_path = dir.path().join("test_records.mrt");
416
417 let header = CommonHeader {
418 timestamp: 1609459200,
419 microsecond_timestamp: None,
420 entry_type: EntryType::BGP4MP,
421 entry_subtype: 4,
422 length: 3,
423 };
424 let header_bytes = Bytes::from_static(&[
425 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, ]);
430 let message_bytes = Bytes::from_static(&[1, 2, 3]);
431
432 let raw_record = RawMrtRecord {
433 common_header: header,
434 header_bytes,
435 message_bytes,
436 };
437
438 raw_record.append_raw_bytes(&file_path).unwrap();
439 raw_record.append_raw_bytes(&file_path).unwrap();
440
441 let written_bytes = std::fs::read(&file_path).unwrap();
442 assert_eq!(written_bytes.len(), 30); }
444
445 #[test]
446 fn test_try_from_bmp_message() {
447 let bmp_message = BmpMessage {
448 common_header: BmpCommonHeader {
449 version: 0,
450 msg_len: 0,
451 msg_type: BmpMsgType::RouteMonitoring,
452 },
453 per_peer_header: Some(BmpPerPeerHeader {
454 peer_asn: Asn::new_32bit(0),
455 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
456 peer_bgp_id: Ipv4Addr::from_str("10.0.0.2").unwrap(),
457 timestamp: 0.0,
458 peer_type: BmpPeerType::Global,
459 peer_flags: PerPeerFlags::PeerFlags(PeerFlags::empty()),
460 peer_distinguisher: 0,
461 }),
462 message_body: BmpMessageBody::RouteMonitoring(RouteMonitoring {
463 bgp_message: BgpMessage::KeepAlive,
464 }),
465 };
466
467 let mrt_record = MrtRecord::try_from(&bmp_message).unwrap();
468 assert_eq!(mrt_record.common_header.entry_type, EntryType::BGP4MP_ET);
469 }
470
471 #[test]
472 fn test_parse_mrt_body() {
473 let mut data = BytesMut::new();
474 data.put_u16(0);
475 data.put_u16(0);
476 data.put_u32(0);
477 data.put_u16(0);
478
479 let result = parse_mrt_body(0, 0, data.freeze());
480 assert!(result.is_err());
481 }
482
483 #[test]
484 fn test_mrt_record_encode_updates_header_length() {
485 let record = MrtRecord {
486 common_header: CommonHeader {
487 timestamp: 1609459200,
488 microsecond_timestamp: None,
489 entry_type: EntryType::BGP4MP,
490 entry_subtype: Bgp4MpType::MessageAs4 as u16,
491 length: 0,
492 },
493 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
494 msg_type: Bgp4MpType::MessageAs4,
495 peer_asn: Asn::new_32bit(65000),
496 local_asn: Asn::new_32bit(65001),
497 interface_index: 1,
498 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
499 local_ip: IpAddr::from_str("10.0.0.2").unwrap(),
500 bgp_message: BgpMessage::KeepAlive,
501 })),
502 };
503
504 let encoded = record.encode();
505 let mut cursor = Cursor::new(encoded);
506 let parsed = parse_mrt_record(&mut cursor).unwrap();
507 let expected_len = parsed
508 .message
509 .encode(parsed.common_header.entry_subtype)
510 .len() as u32;
511
512 assert_eq!(parsed.common_header.length, expected_len);
513 }
514}