1use super::mrt_header::parse_common_header_with_bytes;
2use crate::bmp::messages::{BmpMessage, BmpMessageBody};
3use crate::error::{check_max, EncodingError, ParserError};
4use crate::models::*;
5use crate::parser::{
6 parse_bgp4mp, parse_legacy_bgp, parse_table_dump_messages, parse_table_dump_v2_message,
7 ParserErrorWithBytes,
8};
9use crate::utils::convert_timestamp;
10use bytes::{BufMut, Bytes, BytesMut};
11use log::{debug, warn};
12use std::convert::TryFrom;
13use std::fs::File;
14use std::io::{Read, Write};
15use std::net::IpAddr;
16use std::path::Path;
17use std::str::FromStr;
18
19#[derive(Debug, Clone)]
23pub struct RawMrtRecord {
24 pub common_header: CommonHeader,
25 pub header_bytes: Bytes,
27 pub message_bytes: Bytes,
29}
30
31pub(crate) struct RawMrtRecordError {
33 pub(crate) error: ParserError,
34 pub(crate) common_header: Option<CommonHeader>,
35 pub(crate) bytes: Option<Vec<u8>>,
36}
37
38impl RawMrtRecord {
39 pub fn parse(self) -> Result<MrtRecord, ParserError> {
42 let message = parse_mrt_body(
43 self.common_header.entry_type as u16,
44 self.common_header.entry_subtype,
45 self.message_bytes,
46 )?;
47
48 Ok(MrtRecord {
49 common_header: self.common_header,
50 message,
51 })
52 }
53
54 pub fn raw_bytes(&self) -> Bytes {
67 let mut bytes = BytesMut::with_capacity(self.header_bytes.len() + self.message_bytes.len());
68 bytes.put_slice(&self.header_bytes);
69 bytes.put_slice(&self.message_bytes);
70 bytes.freeze()
71 }
72
73 pub fn write_raw_bytes<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
87 let mut file = File::create(path)?;
88 file.write_all(&self.header_bytes)?;
89 file.write_all(&self.message_bytes)?;
90 Ok(())
91 }
92
93 pub fn append_raw_bytes<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
109 let mut file = std::fs::OpenOptions::new()
110 .create(true)
111 .append(true)
112 .open(path)?;
113 file.write_all(&self.header_bytes)?;
114 file.write_all(&self.message_bytes)?;
115 Ok(())
116 }
117
118 pub fn total_bytes_len(&self) -> usize {
120 self.header_bytes.len() + self.message_bytes.len()
121 }
122}
123
124pub fn chunk_mrt_record(input: &mut impl Read) -> Result<RawMrtRecord, ParserErrorWithBytes> {
125 chunk_mrt_record_with_context(input).map_err(|error| ParserErrorWithBytes {
126 error: error.error,
127 bytes: error.bytes,
128 })
129}
130
131pub(crate) fn chunk_mrt_record_with_context(
133 input: &mut impl Read,
134) -> Result<RawMrtRecord, RawMrtRecordError> {
135 let mut consumed_header = Vec::with_capacity(16);
137 let parsed_header = match parse_common_header_with_bytes(&mut CapturingReader {
138 inner: input,
139 captured: &mut consumed_header,
140 }) {
141 Ok(v) => v,
142 Err(e) => {
143 if let ParserError::EofError(e) = &e {
144 if e.kind() == std::io::ErrorKind::UnexpectedEof && consumed_header.is_empty() {
145 return Err(RawMrtRecordError {
146 error: ParserError::EofExpected,
147 common_header: None,
148 bytes: None,
149 });
150 }
151 }
152 return Err(RawMrtRecordError {
153 error: e,
154 common_header: None,
155 bytes: Some(consumed_header),
156 });
157 }
158 };
159
160 let common_header = parsed_header.header;
161 let header_bytes = parsed_header.raw_bytes;
162
163 const MAX_MRT_MESSAGE_LEN: u32 = 16 * 1024 * 1024; if common_header.length > MAX_MRT_MESSAGE_LEN {
166 return Err(RawMrtRecordError {
167 error: ParserError::Unsupported(format!(
168 "MRT message too large: {} bytes",
169 common_header.length
170 )),
171 common_header: Some(common_header),
172 bytes: Some(header_bytes.to_vec()),
173 });
174 }
175
176 let mut buffer = Vec::with_capacity(common_header.length as usize + 4);
178 if let Err(error) = input
179 .take(common_header.length as u64)
180 .read_to_end(&mut buffer)
181 {
182 return Err(record_io_error(
183 error,
184 common_header,
185 &header_bytes,
186 &buffer,
187 ));
188 }
189 if buffer.len() != common_header.length as usize {
190 return Err(record_io_error(
191 std::io::Error::new(
192 std::io::ErrorKind::UnexpectedEof,
193 format!(
194 "truncated MRT body: expected {} bytes, read {}",
195 common_header.length,
196 buffer.len()
197 ),
198 ),
199 common_header,
200 &header_bytes,
201 &buffer,
202 ));
203 }
204
205 if common_header.entry_type == EntryType::TABLE_DUMP
206 && super::messages::table_dump::needs_legacy_length_correction(
207 common_header.entry_subtype,
208 &buffer,
209 )
210 {
211 let mut correction = Vec::with_capacity(4);
212 if let Err(error) = input.take(4).read_to_end(&mut correction) {
213 buffer.extend_from_slice(&correction);
214 return Err(record_io_error(
215 error,
216 common_header,
217 &header_bytes,
218 &buffer,
219 ));
220 }
221 buffer.extend_from_slice(&correction);
222 if correction.len() != 4 {
223 return Err(record_io_error(
224 std::io::Error::new(
225 std::io::ErrorKind::UnexpectedEof,
226 "truncated historical TABLE_DUMP length correction",
227 ),
228 common_header,
229 &header_bytes,
230 &buffer,
231 ));
232 }
233 debug!(
234 "recovered historical TABLE_DUMP record whose declared length was four bytes short (timestamp={}, subtype={}, declared_length={})",
235 common_header.timestamp,
236 common_header.entry_subtype,
237 common_header.length
238 );
239 }
240
241 Ok(RawMrtRecord {
242 common_header,
243 header_bytes,
244 message_bytes: Bytes::from(buffer),
245 })
246}
247
248struct CapturingReader<'a, R: ?Sized> {
249 inner: &'a mut R,
250 captured: &'a mut Vec<u8>,
251}
252
253impl<R: Read + ?Sized> Read for CapturingReader<'_, R> {
254 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
255 let read = self.inner.read(buffer)?;
256 self.captured.extend_from_slice(&buffer[..read]);
257 Ok(read)
258 }
259}
260
261fn record_io_error(
262 error: std::io::Error,
263 common_header: CommonHeader,
264 header: &[u8],
265 body: &[u8],
266) -> RawMrtRecordError {
267 let mut bytes = Vec::with_capacity(header.len() + body.len());
268 bytes.extend_from_slice(header);
269 bytes.extend_from_slice(body);
270 RawMrtRecordError {
271 error: ParserError::IoError(error),
272 common_header: Some(common_header),
273 bytes: Some(bytes),
274 }
275}
276
277pub fn parse_mrt_record(input: &mut impl Read) -> Result<MrtRecord, ParserErrorWithBytes> {
278 parse_mrt_record_with_zebra_compat(input).map(|(record, _)| record)
279}
280
281pub(crate) fn raw_record_uses_zebra_compat(raw_record: &RawMrtRecord) -> bool {
282 matches!(
283 raw_record.common_header.entry_type,
284 EntryType::BGP4MP | EntryType::BGP4MP_ET
285 ) && crate::parser::mrt::messages::bgp4mp::uses_zebra_compat(
286 raw_record.common_header.entry_subtype,
287 &raw_record.message_bytes,
288 )
289}
290
291pub(crate) fn parse_mrt_record_with_zebra_compat(
292 input: &mut impl Read,
293) -> Result<(MrtRecord, bool), ParserErrorWithBytes> {
294 let raw_record = chunk_mrt_record(input)?;
295 let used_zebra_compat = raw_record_uses_zebra_compat(&raw_record);
296 match raw_record.clone().parse() {
299 Ok(record) => Ok((record, used_zebra_compat)),
300 Err(e) => Err(ParserErrorWithBytes {
301 error: e,
302 bytes: Some(raw_record.raw_bytes().to_vec()),
303 }),
304 }
305}
306
307pub fn parse_mrt_body(
313 entry_type: u16,
314 entry_subtype: u16,
315 data: Bytes,
316) -> Result<MrtMessage, ParserError> {
317 let etype = EntryType::try_from(entry_type)?;
318
319 let message: MrtMessage = match &etype {
320 EntryType::TABLE_DUMP => {
321 let mut messages = parse_table_dump_messages(entry_subtype, data)?;
322 if messages.len() == 1 {
323 MrtMessage::TableDumpMessage(messages.remove(0))
324 } else {
325 MrtMessage::TableDumpMessageBatch(messages)
326 }
327 }
328 EntryType::TABLE_DUMP_V2 => {
329 let msg = parse_table_dump_v2_message(entry_subtype, data);
330 match msg {
331 Ok(msg) => MrtMessage::TableDumpV2Message(msg),
332 Err(e) => {
333 return Err(e);
334 }
335 }
336 }
337 EntryType::BGP4MP | EntryType::BGP4MP_ET => {
338 let msg = parse_bgp4mp(entry_subtype, data);
339 match msg {
340 Ok(msg) => MrtMessage::Bgp4Mp(msg),
341 Err(e) => {
342 return Err(e);
343 }
344 }
345 }
346 EntryType::BGP => MrtMessage::LegacyBgp(parse_legacy_bgp(entry_subtype, data)?),
347 v => {
348 return Err(ParserError::Unsupported(format!(
350 "unsupported MRT type: {v:?}"
351 )));
352 }
353 };
354 Ok(message)
355}
356
357impl MrtRecord {
358 pub fn encode(&self) -> Result<Bytes, EncodingError> {
359 let message_bytes = self.message.encode(self.common_header.entry_subtype)?;
360 let mut new_header = self.common_header;
361 if message_bytes.len() != new_header.length as usize {
362 warn!(
363 "message length {} does not match the length in the header {} (encoding MRT record)",
364 message_bytes.len(),
365 new_header.length
366 );
367 }
368 check_max(
369 "MRT record message length",
370 message_bytes.len(),
371 u32::MAX as usize,
372 )?;
373 new_header.length = message_bytes.len() as u32;
374 let header_bytes = new_header.encode();
375
376 let mut bytes = BytesMut::with_capacity(header_bytes.len() + message_bytes.len());
387 bytes.put_slice(&header_bytes);
388 bytes.put_slice(&message_bytes);
389 Ok(bytes.freeze())
390 }
391}
392
393impl TryFrom<&BmpMessage> for MrtRecord {
394 type Error = String;
395
396 fn try_from(bmp_message: &BmpMessage) -> Result<Self, Self::Error> {
397 let bgp_message = match &bmp_message.message_body {
398 BmpMessageBody::RouteMonitoring(m) => &m.bgp_message,
399 _ => return Err("unsupported bmp message type".to_string()),
400 };
401 let bmp_header = match &bmp_message.per_peer_header {
402 Some(h) => h,
403 None => return Err("missing per peer header".to_string()),
404 };
405
406 let local_ip = match bmp_header.peer_ip {
407 IpAddr::V4(_) => IpAddr::from_str("0.0.0.0").unwrap(),
408 IpAddr::V6(_) => IpAddr::from_str("::").unwrap(),
409 };
410 let local_asn = match bmp_header.peer_asn.is_four_byte() {
411 true => Asn::new_32bit(0),
412 false => Asn::new_16bit(0),
413 };
414
415 let bgp4mp_message = Bgp4MpMessage {
416 msg_type: Bgp4MpType::MessageAs4, peer_asn: bmp_header.peer_asn,
418 local_asn,
419 interface_index: 0,
420 peer_ip: bmp_header.peer_ip,
421 local_ip,
422 bgp_message: bgp_message.clone(),
423 };
424
425 let mrt_message = MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(bgp4mp_message));
426
427 let (seconds, microseconds) = convert_timestamp(bmp_header.timestamp);
428
429 let subtype = Bgp4MpType::MessageAs4 as u16;
430 let encoded_message = mrt_message
431 .encode(subtype)
432 .map_err(|e| format!("cannot encode MRT message: {e}"))?;
433 let mrt_header = CommonHeader {
434 timestamp: seconds,
435 microsecond_timestamp: Some(microseconds),
436 entry_type: EntryType::BGP4MP_ET,
437 entry_subtype: Bgp4MpType::MessageAs4 as u16,
438 length: encoded_message.len() as u32,
439 };
440
441 Ok(MrtRecord {
442 common_header: mrt_header,
443 message: mrt_message,
444 })
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451 use crate::bmp::messages::headers::{BmpPeerType, PeerFlags, PerPeerFlags};
452 use crate::bmp::messages::{BmpCommonHeader, BmpMsgType, BmpPerPeerHeader, RouteMonitoring};
453 use crate::models::{AttributeValue, Origin};
454 use crate::parser::mrt::messages::table_dump::encode_table_dump_batch;
455 use std::io::Cursor;
456 use std::net::Ipv4Addr;
457 use tempfile::tempdir;
458
459 fn table_dump_message(prefix: &str) -> TableDumpMessage {
460 let mut attributes = Attributes::default();
461 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
462 TableDumpMessage {
463 view_number: 0,
464 sequence_number: 1,
465 prefix: prefix.parse().unwrap(),
466 status: 1,
467 originated_time: 946_684_800,
468 peer_ip: IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)),
469 peer_asn: Asn::new_16bit(64512),
470 attributes,
471 }
472 }
473
474 #[test]
475 fn test_raw_mrt_record_raw_bytes() {
476 let header = CommonHeader {
477 timestamp: 1609459200,
478 microsecond_timestamp: None,
479 entry_type: EntryType::BGP4MP,
480 entry_subtype: 4,
481 length: 10,
482 };
483 let header_bytes = Bytes::from_static(&[
484 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, ]);
489 let message_bytes = Bytes::from_static(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
490
491 let raw_record = RawMrtRecord {
492 common_header: header,
493 header_bytes,
494 message_bytes,
495 };
496
497 let mrt_bytes = raw_record.raw_bytes();
498 assert_eq!(mrt_bytes.len(), 22);
500 assert_eq!(raw_record.total_bytes_len(), 22);
501 }
502
503 #[test]
504 fn test_raw_mrt_record_raw_bytes_with_et() {
505 let header = CommonHeader {
506 timestamp: 1609459200,
507 microsecond_timestamp: Some(500000),
508 entry_type: EntryType::BGP4MP_ET,
509 entry_subtype: 4,
510 length: 10,
511 };
512 let header_bytes = Bytes::from_static(&[
513 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x07, 0xa1, 0x20, ]);
519 let message_bytes = Bytes::from_static(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
520
521 let raw_record = RawMrtRecord {
522 common_header: header,
523 header_bytes,
524 message_bytes,
525 };
526
527 let mrt_bytes = raw_record.raw_bytes();
528 assert_eq!(mrt_bytes.len(), 26);
530 assert_eq!(raw_record.total_bytes_len(), 26);
531 }
532
533 #[test]
534 fn test_raw_mrt_record_write_to_file() {
535 let dir = tempdir().unwrap();
536 let file_path = dir.path().join("test_record.mrt");
537
538 let header = CommonHeader {
539 timestamp: 1609459200,
540 microsecond_timestamp: None,
541 entry_type: EntryType::BGP4MP,
542 entry_subtype: 4,
543 length: 5,
544 };
545 let header_bytes = Bytes::from_static(&[
546 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, ]);
551 let message_bytes = Bytes::from_static(&[1, 2, 3, 4, 5]);
552
553 let raw_record = RawMrtRecord {
554 common_header: header,
555 header_bytes,
556 message_bytes,
557 };
558
559 raw_record.write_raw_bytes(&file_path).unwrap();
560
561 let written_bytes = std::fs::read(&file_path).unwrap();
562 assert_eq!(written_bytes.len(), 17); }
564
565 #[test]
566 fn test_raw_mrt_record_append_to_file() {
567 let dir = tempdir().unwrap();
568 let file_path = dir.path().join("test_records.mrt");
569
570 let header = CommonHeader {
571 timestamp: 1609459200,
572 microsecond_timestamp: None,
573 entry_type: EntryType::BGP4MP,
574 entry_subtype: 4,
575 length: 3,
576 };
577 let header_bytes = Bytes::from_static(&[
578 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, ]);
583 let message_bytes = Bytes::from_static(&[1, 2, 3]);
584
585 let raw_record = RawMrtRecord {
586 common_header: header,
587 header_bytes,
588 message_bytes,
589 };
590
591 raw_record.append_raw_bytes(&file_path).unwrap();
592 raw_record.append_raw_bytes(&file_path).unwrap();
593
594 let written_bytes = std::fs::read(&file_path).unwrap();
595 assert_eq!(written_bytes.len(), 30); }
597
598 #[test]
599 fn test_try_from_bmp_message() {
600 let bmp_message = BmpMessage {
601 common_header: BmpCommonHeader {
602 version: 0,
603 msg_len: 0,
604 msg_type: BmpMsgType::RouteMonitoring,
605 },
606 per_peer_header: Some(BmpPerPeerHeader {
607 peer_asn: Asn::new_32bit(0),
608 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
609 peer_bgp_id: Ipv4Addr::from_str("10.0.0.2").unwrap(),
610 timestamp: 0.0,
611 peer_type: BmpPeerType::Global,
612 peer_flags: PerPeerFlags::PeerFlags(PeerFlags::empty()),
613 peer_distinguisher: 0,
614 }),
615 message_body: BmpMessageBody::RouteMonitoring(RouteMonitoring {
616 bgp_message: BgpMessage::KeepAlive,
617 }),
618 };
619
620 let mrt_record = MrtRecord::try_from(&bmp_message).unwrap();
621 assert_eq!(mrt_record.common_header.entry_type, EntryType::BGP4MP_ET);
622 }
623
624 #[test]
625 fn test_parse_mrt_body() {
626 let mut data = BytesMut::new();
627 data.put_u16(0);
628 data.put_u16(0);
629 data.put_u32(0);
630 data.put_u16(0);
631
632 let result = parse_mrt_body(0, 0, data.freeze());
633 assert!(result.is_err());
634 }
635
636 #[test]
637 fn test_mrt_record_encode_updates_header_length() {
638 let record = MrtRecord {
639 common_header: CommonHeader {
640 timestamp: 1609459200,
641 microsecond_timestamp: None,
642 entry_type: EntryType::BGP4MP,
643 entry_subtype: Bgp4MpType::MessageAs4 as u16,
644 length: 0,
645 },
646 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
647 msg_type: Bgp4MpType::MessageAs4,
648 peer_asn: Asn::new_32bit(65000),
649 local_asn: Asn::new_32bit(65001),
650 interface_index: 1,
651 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
652 local_ip: IpAddr::from_str("10.0.0.2").unwrap(),
653 bgp_message: BgpMessage::KeepAlive,
654 })),
655 };
656
657 let encoded = record.encode().unwrap();
658 let mut cursor = Cursor::new(encoded);
659 let parsed = parse_mrt_record(&mut cursor).unwrap();
660 let expected_len = parsed
661 .message
662 .encode(parsed.common_header.entry_subtype)
663 .unwrap()
664 .len() as u32;
665
666 assert_eq!(parsed.common_header.length, expected_len);
667 }
668
669 #[test]
670 fn chunk_recovers_four_byte_short_table_dump_and_preserves_alignment() {
671 let body = encode_table_dump_batch(
672 &[
673 table_dump_message("192.0.2.0/24"),
674 table_dump_message("198.51.100.0/24"),
675 ],
676 1,
677 )
678 .unwrap();
679 let header = CommonHeader {
680 timestamp: 946_684_800,
681 microsecond_timestamp: None,
682 entry_type: EntryType::TABLE_DUMP,
683 entry_subtype: 1,
684 length: (body.len() - 4) as u32,
685 };
686 let next_body = Bytes::from_static(&[0, 1, 192, 0, 2, 1, 0, 2, 192, 0, 2, 2]);
687 let next_header = CommonHeader {
688 timestamp: 946_684_801,
689 microsecond_timestamp: None,
690 entry_type: EntryType::BGP,
691 entry_subtype: 7,
692 length: next_body.len() as u32,
693 };
694
695 let mut wire = BytesMut::new();
696 wire.put_slice(&header.encode());
697 wire.put_slice(&body);
698 wire.put_slice(&next_header.encode());
699 wire.put_slice(&next_body);
700 let mut cursor = Cursor::new(wire.freeze());
701
702 let first = chunk_mrt_record(&mut cursor).unwrap();
703 assert_eq!(first.message_bytes, body);
704 assert!(matches!(
705 first.parse().unwrap().message,
706 MrtMessage::TableDumpMessageBatch(messages) if messages.len() == 2
707 ));
708 let second = chunk_mrt_record(&mut cursor).unwrap();
709 assert_eq!(second.common_header, next_header);
710 assert!(matches!(
711 second.parse().unwrap().message,
712 MrtMessage::LegacyBgp(LegacyBgp::Message(LegacyBgpMessage {
713 bgp_message: BgpMessage::KeepAlive,
714 ..
715 }))
716 ));
717 }
718
719 #[test]
720 fn chunk_does_not_overread_near_match_table_dump() {
721 let body = encode_table_dump_batch(&[table_dump_message("192.0.2.0/24")], 1).unwrap();
722 let declared_length = body.len() - 3;
723 let header = CommonHeader {
724 timestamp: 946_684_800,
725 microsecond_timestamp: None,
726 entry_type: EntryType::TABLE_DUMP,
727 entry_subtype: 1,
728 length: declared_length as u32,
729 };
730 let mut wire = BytesMut::new();
731 wire.put_slice(&header.encode());
732 wire.put_slice(&body);
733 wire.put_slice(&[0xaa; 12]);
734 let mut cursor = Cursor::new(wire.freeze());
735
736 let raw = chunk_mrt_record(&mut cursor).unwrap();
737 assert_eq!(raw.message_bytes.len(), declared_length);
738 assert_eq!(cursor.position(), (12 + declared_length) as u64);
739 let error = raw.parse().unwrap_err();
740 assert!(matches!(error, ParserError::TruncatedMsg(_)));
741 }
742
743 #[test]
744 fn chunk_errors_include_invalid_header_and_partial_body_bytes() {
745 let invalid_header = Bytes::from_static(&[0, 0, 0, 1, 0xff, 0xff, 0, 0, 0, 0, 0, 0]);
746 let error = chunk_mrt_record(&mut Cursor::new(invalid_header.clone())).unwrap_err();
747 assert_eq!(error.bytes.as_deref(), Some(invalid_header.as_ref()));
748
749 let header = CommonHeader {
750 timestamp: 1,
751 microsecond_timestamp: None,
752 entry_type: EntryType::BGP,
753 entry_subtype: 7,
754 length: 5,
755 };
756 let mut wire = BytesMut::new();
757 wire.put_slice(&header.encode());
758 wire.put_slice(&[1, 2]);
759 let error = chunk_mrt_record(&mut Cursor::new(wire.clone().freeze())).unwrap_err();
760 assert_eq!(error.bytes.as_deref(), Some(wire.as_ref()));
761 }
762}