1use super::mrt_header::frame_common_header;
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
19const MAX_MRT_MESSAGE_LEN: u32 = 16 * 1024 * 1024; #[derive(Debug, Clone)]
26pub struct RawMrtRecord {
27 pub common_header: CommonHeader,
28 pub header_bytes: Bytes,
30 pub message_bytes: Bytes,
32}
33
34pub(crate) struct RawMrtRecordError {
36 pub(crate) error: ParserError,
37 pub(crate) common_header: Option<CommonHeader>,
38 pub(crate) bytes: Option<Vec<u8>>,
39 pub(crate) record_consumed: bool,
42}
43
44impl RawMrtRecord {
45 pub fn parse(self) -> Result<MrtRecord, ParserError> {
48 let message = parse_mrt_body(
49 self.common_header.entry_type as u16,
50 self.common_header.entry_subtype,
51 self.message_bytes,
52 )?;
53
54 Ok(MrtRecord {
55 common_header: self.common_header,
56 message,
57 })
58 }
59
60 pub fn raw_bytes(&self) -> Bytes {
73 let mut bytes = BytesMut::with_capacity(self.header_bytes.len() + self.message_bytes.len());
74 bytes.put_slice(&self.header_bytes);
75 bytes.put_slice(&self.message_bytes);
76 bytes.freeze()
77 }
78
79 pub fn write_raw_bytes<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
93 let mut file = File::create(path)?;
94 file.write_all(&self.header_bytes)?;
95 file.write_all(&self.message_bytes)?;
96 Ok(())
97 }
98
99 pub fn append_raw_bytes<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
115 let mut file = std::fs::OpenOptions::new()
116 .create(true)
117 .append(true)
118 .open(path)?;
119 file.write_all(&self.header_bytes)?;
120 file.write_all(&self.message_bytes)?;
121 Ok(())
122 }
123
124 pub fn total_bytes_len(&self) -> usize {
126 self.header_bytes.len() + self.message_bytes.len()
127 }
128}
129
130pub fn chunk_mrt_record(input: &mut impl Read) -> Result<RawMrtRecord, ParserErrorWithBytes> {
131 chunk_mrt_record_with_context(input).map_err(|error| ParserErrorWithBytes {
132 error: error.error,
133 bytes: error.bytes,
134 })
135}
136
137pub(crate) fn chunk_mrt_record_with_context(
139 input: &mut impl Read,
140) -> Result<RawMrtRecord, RawMrtRecordError> {
141 let mut consumed_header = Vec::with_capacity(16);
143 let framed = match frame_common_header(&mut CapturingReader {
144 inner: input,
145 captured: &mut consumed_header,
146 }) {
147 Ok(framed) => framed,
148 Err(error) => {
149 if error.kind() == std::io::ErrorKind::UnexpectedEof && consumed_header.is_empty() {
150 return Err(RawMrtRecordError {
151 error: ParserError::EofExpected,
152 common_header: None,
153 bytes: None,
154 record_consumed: false,
155 });
156 }
157 return Err(RawMrtRecordError {
158 error: error.into(),
159 common_header: None,
160 bytes: Some(consumed_header),
161 record_consumed: false,
162 });
163 }
164 };
165
166 let header_bytes = framed.raw_bytes;
167 let parsed_header = framed.header.as_ref().ok().copied();
168
169 if framed.body_length > MAX_MRT_MESSAGE_LEN {
171 return Err(RawMrtRecordError {
172 error: ParserError::OversizedRecord {
173 length: framed.body_length,
174 },
175 common_header: parsed_header,
176 bytes: Some(header_bytes.to_vec()),
177 record_consumed: false,
178 });
179 }
180
181 let mut buffer = Vec::with_capacity(framed.body_length as usize + 4);
183 if let Err(error) = input
184 .take(framed.body_length as u64)
185 .read_to_end(&mut buffer)
186 {
187 return Err(record_io_error(
188 error,
189 parsed_header,
190 &header_bytes,
191 &buffer,
192 ));
193 }
194 if buffer.len() != framed.body_length as usize {
195 return Err(record_io_error(
196 std::io::Error::new(
197 std::io::ErrorKind::UnexpectedEof,
198 format!(
199 "truncated MRT body: expected {} bytes, read {}",
200 framed.body_length,
201 buffer.len()
202 ),
203 ),
204 parsed_header,
205 &header_bytes,
206 &buffer,
207 ));
208 }
209
210 let common_header = match framed.header {
212 Ok(common_header) => common_header,
213 Err(error) => {
214 return Err(RawMrtRecordError {
215 error,
216 common_header: None,
217 bytes: Some([&header_bytes[..], &buffer].concat()),
218 record_consumed: true,
219 })
220 }
221 };
222
223 if common_header.entry_type == EntryType::TABLE_DUMP
224 && super::messages::table_dump::needs_legacy_length_correction(
225 common_header.entry_subtype,
226 &buffer,
227 )
228 {
229 let mut correction = Vec::with_capacity(4);
230 if let Err(error) = input.take(4).read_to_end(&mut correction) {
231 buffer.extend_from_slice(&correction);
232 return Err(record_io_error(
233 error,
234 Some(common_header),
235 &header_bytes,
236 &buffer,
237 ));
238 }
239 buffer.extend_from_slice(&correction);
240 if correction.len() != 4 {
241 return Err(record_io_error(
242 std::io::Error::new(
243 std::io::ErrorKind::UnexpectedEof,
244 "truncated historical TABLE_DUMP length correction",
245 ),
246 Some(common_header),
247 &header_bytes,
248 &buffer,
249 ));
250 }
251 debug!(
252 "recovered historical TABLE_DUMP record whose declared length was four bytes short (timestamp={}, subtype={}, declared_length={})",
253 common_header.timestamp,
254 common_header.entry_subtype,
255 common_header.length
256 );
257 }
258
259 Ok(RawMrtRecord {
260 common_header,
261 header_bytes,
262 message_bytes: Bytes::from(buffer),
263 })
264}
265
266struct CapturingReader<'a, R: ?Sized> {
267 inner: &'a mut R,
268 captured: &'a mut Vec<u8>,
269}
270
271impl<R: Read + ?Sized> Read for CapturingReader<'_, R> {
272 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
273 let read = self.inner.read(buffer)?;
274 self.captured.extend_from_slice(&buffer[..read]);
275 Ok(read)
276 }
277}
278
279fn record_io_error(
280 error: std::io::Error,
281 common_header: Option<CommonHeader>,
282 header: &[u8],
283 body: &[u8],
284) -> RawMrtRecordError {
285 let mut bytes = Vec::with_capacity(header.len() + body.len());
286 bytes.extend_from_slice(header);
287 bytes.extend_from_slice(body);
288 RawMrtRecordError {
289 error: ParserError::IoError(error),
290 common_header,
291 bytes: Some(bytes),
292 record_consumed: false,
293 }
294}
295
296pub fn parse_mrt_record(input: &mut impl Read) -> Result<MrtRecord, ParserErrorWithBytes> {
297 parse_mrt_record_with_zebra_compat(input).map(|(record, _)| record)
298}
299
300pub(crate) fn raw_record_uses_zebra_compat(raw_record: &RawMrtRecord) -> bool {
301 matches!(
302 raw_record.common_header.entry_type,
303 EntryType::BGP4MP | EntryType::BGP4MP_ET
304 ) && crate::parser::mrt::messages::bgp4mp::uses_zebra_compat(
305 raw_record.common_header.entry_subtype,
306 &raw_record.message_bytes,
307 )
308}
309
310pub(crate) fn parse_mrt_record_with_zebra_compat(
311 input: &mut impl Read,
312) -> Result<(MrtRecord, bool), ParserErrorWithBytes> {
313 let raw_record = chunk_mrt_record(input)?;
314 parse_chunked_record_with_zebra_compat(raw_record)
315}
316
317pub(crate) fn parse_chunked_record_with_zebra_compat(
319 raw_record: RawMrtRecord,
320) -> Result<(MrtRecord, bool), ParserErrorWithBytes> {
321 let used_zebra_compat = raw_record_uses_zebra_compat(&raw_record);
322 match raw_record.clone().parse() {
325 Ok(record) => Ok((record, used_zebra_compat)),
326 Err(e) => Err(ParserErrorWithBytes {
327 error: e,
328 bytes: Some(raw_record.raw_bytes().to_vec()),
329 }),
330 }
331}
332
333pub fn parse_mrt_body(
339 entry_type: u16,
340 entry_subtype: u16,
341 data: Bytes,
342) -> Result<MrtMessage, ParserError> {
343 let etype = EntryType::try_from(entry_type)?;
344
345 let message: MrtMessage = match &etype {
346 EntryType::TABLE_DUMP => {
347 let mut messages = parse_table_dump_messages(entry_subtype, data)?;
348 if messages.len() == 1 {
349 MrtMessage::TableDumpMessage(messages.remove(0))
350 } else {
351 MrtMessage::TableDumpMessageBatch(messages)
352 }
353 }
354 EntryType::TABLE_DUMP_V2 => {
355 let msg = parse_table_dump_v2_message(entry_subtype, data);
356 match msg {
357 Ok(msg) => MrtMessage::TableDumpV2Message(msg),
358 Err(e) => {
359 return Err(e);
360 }
361 }
362 }
363 EntryType::BGP4MP | EntryType::BGP4MP_ET => {
364 let msg = parse_bgp4mp(entry_subtype, data);
365 match msg {
366 Ok(msg) => MrtMessage::Bgp4Mp(msg),
367 Err(e) => {
368 return Err(e);
369 }
370 }
371 }
372 EntryType::BGP => MrtMessage::LegacyBgp(parse_legacy_bgp(entry_subtype, data)?),
373 v => {
374 return Err(ParserError::Unsupported(format!(
376 "unsupported MRT type: {v:?}"
377 )));
378 }
379 };
380 Ok(message)
381}
382
383impl MrtRecord {
384 pub fn encode(&self) -> Result<Bytes, EncodingError> {
385 let message_bytes = self.message.encode(self.common_header.entry_subtype)?;
386 let mut new_header = self.common_header;
387 if message_bytes.len() != new_header.length as usize {
388 warn!(
389 "message length {} does not match the length in the header {} (encoding MRT record)",
390 message_bytes.len(),
391 new_header.length
392 );
393 }
394 check_max(
395 "MRT record message length",
396 message_bytes.len(),
397 u32::MAX as usize,
398 )?;
399 new_header.length = message_bytes.len() as u32;
400 let header_bytes = new_header.encode();
401
402 let mut bytes = BytesMut::with_capacity(header_bytes.len() + message_bytes.len());
413 bytes.put_slice(&header_bytes);
414 bytes.put_slice(&message_bytes);
415 Ok(bytes.freeze())
416 }
417}
418
419impl TryFrom<&BmpMessage> for MrtRecord {
420 type Error = String;
421
422 fn try_from(bmp_message: &BmpMessage) -> Result<Self, Self::Error> {
423 let bgp_message = match &bmp_message.message_body {
424 BmpMessageBody::RouteMonitoring(m) => &m.bgp_message,
425 _ => return Err("unsupported bmp message type".to_string()),
426 };
427 let bmp_header = match &bmp_message.per_peer_header {
428 Some(h) => h,
429 None => return Err("missing per peer header".to_string()),
430 };
431
432 let local_ip = match bmp_header.peer_ip {
433 IpAddr::V4(_) => IpAddr::from_str("0.0.0.0").unwrap(),
434 IpAddr::V6(_) => IpAddr::from_str("::").unwrap(),
435 };
436 let local_asn = match bmp_header.peer_asn.is_four_byte() {
437 true => Asn::new_32bit(0),
438 false => Asn::new_16bit(0),
439 };
440
441 let bgp4mp_message = Bgp4MpMessage {
442 msg_type: Bgp4MpType::MessageAs4, peer_asn: bmp_header.peer_asn,
444 local_asn,
445 interface_index: 0,
446 peer_ip: bmp_header.peer_ip,
447 local_ip,
448 bgp_message: bgp_message.clone(),
449 };
450
451 let mrt_message = MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(bgp4mp_message));
452
453 let (seconds, microseconds) = convert_timestamp(bmp_header.timestamp);
454
455 let subtype = Bgp4MpType::MessageAs4 as u16;
456 let encoded_message = mrt_message
457 .encode(subtype)
458 .map_err(|e| format!("cannot encode MRT message: {e}"))?;
459 let mrt_header = CommonHeader {
460 timestamp: seconds,
461 microsecond_timestamp: Some(microseconds),
462 entry_type: EntryType::BGP4MP_ET,
463 entry_subtype: Bgp4MpType::MessageAs4 as u16,
464 length: encoded_message.len() as u32,
465 };
466
467 Ok(MrtRecord {
468 common_header: mrt_header,
469 message: mrt_message,
470 })
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477 use crate::bmp::messages::headers::{BmpPeerType, PeerFlags, PerPeerFlags};
478 use crate::bmp::messages::{BmpCommonHeader, BmpMsgType, BmpPerPeerHeader, RouteMonitoring};
479 use crate::models::{AttributeValue, Origin};
480 use crate::parser::mrt::messages::table_dump::encode_table_dump_batch;
481 use std::io::Cursor;
482 use std::net::Ipv4Addr;
483 use tempfile::tempdir;
484
485 fn table_dump_message(prefix: &str) -> TableDumpMessage {
486 let mut attributes = Attributes::default();
487 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
488 TableDumpMessage {
489 view_number: 0,
490 sequence_number: 1,
491 prefix: prefix.parse().unwrap(),
492 status: 1,
493 originated_time: 946_684_800,
494 peer_ip: IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)),
495 peer_asn: Asn::new_16bit(64512),
496 attributes,
497 }
498 }
499
500 #[test]
501 fn test_raw_mrt_record_raw_bytes() {
502 let header = CommonHeader {
503 timestamp: 1609459200,
504 microsecond_timestamp: None,
505 entry_type: EntryType::BGP4MP,
506 entry_subtype: 4,
507 length: 10,
508 };
509 let header_bytes = Bytes::from_static(&[
510 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, ]);
515 let message_bytes = Bytes::from_static(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
516
517 let raw_record = RawMrtRecord {
518 common_header: header,
519 header_bytes,
520 message_bytes,
521 };
522
523 let mrt_bytes = raw_record.raw_bytes();
524 assert_eq!(mrt_bytes.len(), 22);
526 assert_eq!(raw_record.total_bytes_len(), 22);
527 }
528
529 #[test]
530 fn test_raw_mrt_record_raw_bytes_with_et() {
531 let header = CommonHeader {
532 timestamp: 1609459200,
533 microsecond_timestamp: Some(500000),
534 entry_type: EntryType::BGP4MP_ET,
535 entry_subtype: 4,
536 length: 10,
537 };
538 let header_bytes = Bytes::from_static(&[
539 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x07, 0xa1, 0x20, ]);
545 let message_bytes = Bytes::from_static(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
546
547 let raw_record = RawMrtRecord {
548 common_header: header,
549 header_bytes,
550 message_bytes,
551 };
552
553 let mrt_bytes = raw_record.raw_bytes();
554 assert_eq!(mrt_bytes.len(), 26);
556 assert_eq!(raw_record.total_bytes_len(), 26);
557 }
558
559 #[test]
560 fn test_raw_mrt_record_write_to_file() {
561 let dir = tempdir().unwrap();
562 let file_path = dir.path().join("test_record.mrt");
563
564 let header = CommonHeader {
565 timestamp: 1609459200,
566 microsecond_timestamp: None,
567 entry_type: EntryType::BGP4MP,
568 entry_subtype: 4,
569 length: 5,
570 };
571 let header_bytes = Bytes::from_static(&[
572 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, ]);
577 let message_bytes = Bytes::from_static(&[1, 2, 3, 4, 5]);
578
579 let raw_record = RawMrtRecord {
580 common_header: header,
581 header_bytes,
582 message_bytes,
583 };
584
585 raw_record.write_raw_bytes(&file_path).unwrap();
586
587 let written_bytes = std::fs::read(&file_path).unwrap();
588 assert_eq!(written_bytes.len(), 17); }
590
591 #[test]
592 fn test_raw_mrt_record_append_to_file() {
593 let dir = tempdir().unwrap();
594 let file_path = dir.path().join("test_records.mrt");
595
596 let header = CommonHeader {
597 timestamp: 1609459200,
598 microsecond_timestamp: None,
599 entry_type: EntryType::BGP4MP,
600 entry_subtype: 4,
601 length: 3,
602 };
603 let header_bytes = Bytes::from_static(&[
604 0x5f, 0xee, 0x6a, 0x80, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, ]);
609 let message_bytes = Bytes::from_static(&[1, 2, 3]);
610
611 let raw_record = RawMrtRecord {
612 common_header: header,
613 header_bytes,
614 message_bytes,
615 };
616
617 raw_record.append_raw_bytes(&file_path).unwrap();
618 raw_record.append_raw_bytes(&file_path).unwrap();
619
620 let written_bytes = std::fs::read(&file_path).unwrap();
621 assert_eq!(written_bytes.len(), 30); }
623
624 #[test]
625 fn test_try_from_bmp_message() {
626 let bmp_message = BmpMessage {
627 common_header: BmpCommonHeader {
628 version: 0,
629 msg_len: 0,
630 msg_type: BmpMsgType::RouteMonitoring,
631 },
632 per_peer_header: Some(BmpPerPeerHeader {
633 peer_asn: Asn::new_32bit(0),
634 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
635 peer_bgp_id: Ipv4Addr::from_str("10.0.0.2").unwrap(),
636 timestamp: 0.0,
637 peer_type: BmpPeerType::Global,
638 peer_flags: PerPeerFlags::PeerFlags(PeerFlags::empty()),
639 peer_distinguisher: 0,
640 }),
641 message_body: BmpMessageBody::RouteMonitoring(RouteMonitoring {
642 bgp_message: BgpMessage::KeepAlive,
643 }),
644 };
645
646 let mrt_record = MrtRecord::try_from(&bmp_message).unwrap();
647 assert_eq!(mrt_record.common_header.entry_type, EntryType::BGP4MP_ET);
648 }
649
650 #[test]
651 fn test_parse_mrt_body() {
652 let mut data = BytesMut::new();
653 data.put_u16(0);
654 data.put_u16(0);
655 data.put_u32(0);
656 data.put_u16(0);
657
658 let result = parse_mrt_body(0, 0, data.freeze());
659 assert!(result.is_err());
660 }
661
662 #[test]
663 fn test_mrt_record_encode_updates_header_length() {
664 let record = MrtRecord {
665 common_header: CommonHeader {
666 timestamp: 1609459200,
667 microsecond_timestamp: None,
668 entry_type: EntryType::BGP4MP,
669 entry_subtype: Bgp4MpType::MessageAs4 as u16,
670 length: 0,
671 },
672 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
673 msg_type: Bgp4MpType::MessageAs4,
674 peer_asn: Asn::new_32bit(65000),
675 local_asn: Asn::new_32bit(65001),
676 interface_index: 1,
677 peer_ip: IpAddr::from_str("10.0.0.1").unwrap(),
678 local_ip: IpAddr::from_str("10.0.0.2").unwrap(),
679 bgp_message: BgpMessage::KeepAlive,
680 })),
681 };
682
683 let encoded = record.encode().unwrap();
684 let mut cursor = Cursor::new(encoded);
685 let parsed = parse_mrt_record(&mut cursor).unwrap();
686 let expected_len = parsed
687 .message
688 .encode(parsed.common_header.entry_subtype)
689 .unwrap()
690 .len() as u32;
691
692 assert_eq!(parsed.common_header.length, expected_len);
693 }
694
695 #[test]
696 fn chunk_recovers_four_byte_short_table_dump_and_preserves_alignment() {
697 let body = encode_table_dump_batch(
698 &[
699 table_dump_message("192.0.2.0/24"),
700 table_dump_message("198.51.100.0/24"),
701 ],
702 1,
703 )
704 .unwrap();
705 let header = CommonHeader {
706 timestamp: 946_684_800,
707 microsecond_timestamp: None,
708 entry_type: EntryType::TABLE_DUMP,
709 entry_subtype: 1,
710 length: (body.len() - 4) as u32,
711 };
712 let next_body = Bytes::from_static(&[0, 1, 192, 0, 2, 1, 0, 2, 192, 0, 2, 2]);
713 let next_header = CommonHeader {
714 timestamp: 946_684_801,
715 microsecond_timestamp: None,
716 entry_type: EntryType::BGP,
717 entry_subtype: 7,
718 length: next_body.len() as u32,
719 };
720
721 let mut wire = BytesMut::new();
722 wire.put_slice(&header.encode());
723 wire.put_slice(&body);
724 wire.put_slice(&next_header.encode());
725 wire.put_slice(&next_body);
726 let mut cursor = Cursor::new(wire.freeze());
727
728 let first = chunk_mrt_record(&mut cursor).unwrap();
729 assert_eq!(first.message_bytes, body);
730 assert!(matches!(
731 first.parse().unwrap().message,
732 MrtMessage::TableDumpMessageBatch(messages) if messages.len() == 2
733 ));
734 let second = chunk_mrt_record(&mut cursor).unwrap();
735 assert_eq!(second.common_header, next_header);
736 assert!(matches!(
737 second.parse().unwrap().message,
738 MrtMessage::LegacyBgp(LegacyBgp::Message(LegacyBgpMessage {
739 bgp_message: BgpMessage::KeepAlive,
740 ..
741 }))
742 ));
743 }
744
745 #[test]
746 fn chunk_does_not_overread_near_match_table_dump() {
747 let body = encode_table_dump_batch(&[table_dump_message("192.0.2.0/24")], 1).unwrap();
748 let declared_length = body.len() - 3;
749 let header = CommonHeader {
750 timestamp: 946_684_800,
751 microsecond_timestamp: None,
752 entry_type: EntryType::TABLE_DUMP,
753 entry_subtype: 1,
754 length: declared_length as u32,
755 };
756 let mut wire = BytesMut::new();
757 wire.put_slice(&header.encode());
758 wire.put_slice(&body);
759 wire.put_slice(&[0xaa; 12]);
760 let mut cursor = Cursor::new(wire.freeze());
761
762 let raw = chunk_mrt_record(&mut cursor).unwrap();
763 assert_eq!(raw.message_bytes.len(), declared_length);
764 assert_eq!(cursor.position(), (12 + declared_length) as u64);
765 let error = raw.parse().unwrap_err();
766 assert!(matches!(error, ParserError::TruncatedMsg(_)));
767 }
768
769 #[test]
770 fn chunk_errors_include_invalid_header_and_partial_body_bytes() {
771 let invalid_header = Bytes::from_static(&[0, 0, 0, 1, 0xff, 0xff, 0, 0, 0, 0, 0, 0]);
772 let error = chunk_mrt_record(&mut Cursor::new(invalid_header.clone())).unwrap_err();
773 assert_eq!(error.bytes.as_deref(), Some(invalid_header.as_ref()));
774
775 let header = CommonHeader {
776 timestamp: 1,
777 microsecond_timestamp: None,
778 entry_type: EntryType::BGP,
779 entry_subtype: 7,
780 length: 5,
781 };
782 let mut wire = BytesMut::new();
783 wire.put_slice(&header.encode());
784 wire.put_slice(&[1, 2]);
785 let error = chunk_mrt_record(&mut Cursor::new(wire.clone().freeze())).unwrap_err();
786 assert_eq!(error.bytes.as_deref(), Some(wire.as_ref()));
787 }
788
789 fn unknown_type_record() -> Vec<u8> {
793 let mut wire = BytesMut::new();
794 wire.put_u32(946_684_800);
795 wire.put_u16(0x7777);
796 wire.put_u16(0);
797 wire.put_u32(20);
798 wire.put_slice(&[0, 0, 0, 1, 0, 5, 0, 7, 0, 0, 0, 32]);
799 wire.put_slice(&[0xaa; 8]);
800 wire.to_vec()
801 }
802
803 fn keepalive_record() -> (CommonHeader, Vec<u8>) {
804 let body = vec![0, 1, 192, 0, 2, 1, 0, 2, 192, 0, 2, 2];
805 let header = CommonHeader {
806 timestamp: 946_684_801,
807 microsecond_timestamp: None,
808 entry_type: EntryType::BGP,
809 entry_subtype: 7,
810 length: body.len() as u32,
811 };
812 let mut wire = header.encode().to_vec();
813 wire.extend_from_slice(&body);
814 (header, wire)
815 }
816
817 #[test]
818 fn chunk_skips_body_of_unknown_entry_type() {
819 let unknown = unknown_type_record();
820 let (next_header, next_wire) = keepalive_record();
821 let mut wire = unknown.clone();
822 wire.extend_from_slice(&next_wire);
823 let mut cursor = Cursor::new(wire);
824
825 let error = chunk_mrt_record(&mut cursor).unwrap_err();
826 assert!(
827 matches!(&error.error, ParserError::ParseError(msg) if msg.contains("entry type")),
828 "unexpected error: {}",
829 error.error
830 );
831 assert_eq!(error.bytes.as_deref(), Some(unknown.as_slice()));
832 assert_eq!(cursor.position(), unknown.len() as u64);
833
834 let next = chunk_mrt_record(&mut cursor).unwrap();
835 assert_eq!(next.common_header, next_header);
836 assert!(matches!(
837 next.parse().unwrap().message,
838 MrtMessage::LegacyBgp(LegacyBgp::Message(LegacyBgpMessage {
839 bgp_message: BgpMessage::KeepAlive,
840 ..
841 }))
842 ));
843 assert!(matches!(
844 chunk_mrt_record(&mut cursor).unwrap_err().error,
845 ParserError::EofExpected
846 ));
847 }
848
849 #[test]
850 fn chunk_skips_body_of_et_header_with_short_length() {
851 let mut wire = BytesMut::new();
852 wire.put_u32(1);
853 wire.put_u16(EntryType::BGP4MP_ET as u16);
854 wire.put_u16(4);
855 wire.put_u32(2);
856 wire.put_slice(&[0xaa; 2]);
857 let rejected = wire.to_vec();
858 let (next_header, next_wire) = keepalive_record();
859 wire.put_slice(&next_wire);
860 let mut cursor = Cursor::new(wire.to_vec());
861
862 let error = chunk_mrt_record_with_context(&mut cursor).unwrap_err();
863 assert!(matches!(error.error, ParserError::ParseError(_)));
864 assert!(error.record_consumed);
865 assert_eq!(error.bytes.as_deref(), Some(rejected.as_slice()));
866 assert_eq!(
867 chunk_mrt_record(&mut cursor).unwrap().common_header,
868 next_header
869 );
870 }
871
872 #[test]
873 fn chunk_reports_truncated_body_of_unknown_entry_type() {
874 let unknown = unknown_type_record();
875 let truncated = &unknown[..unknown.len() - 5];
876 let mut cursor = Cursor::new(truncated.to_vec());
877
878 let error = chunk_mrt_record_with_context(&mut cursor).unwrap_err();
879 assert!(matches!(
880 &error.error,
881 ParserError::IoError(io_error) if io_error.kind() == std::io::ErrorKind::UnexpectedEof
882 ));
883 assert!(error.common_header.is_none());
884 assert!(!error.record_consumed);
885 assert_eq!(error.bytes.as_deref(), Some(truncated));
886 assert!(matches!(
887 chunk_mrt_record(&mut cursor).unwrap_err().error,
888 ParserError::EofExpected
889 ));
890 }
891
892 #[test]
893 fn chunk_reports_oversized_length_without_reading_body() {
894 let mut wire = BytesMut::new();
895 wire.put_u32(1);
896 wire.put_u16(EntryType::BGP as u16);
897 wire.put_u16(7);
898 wire.put_u32(MAX_MRT_MESSAGE_LEN + 1);
899 wire.put_slice(&[0xaa; 32]);
900 let mut cursor = Cursor::new(wire.to_vec());
901
902 let error = chunk_mrt_record(&mut cursor).unwrap_err();
903 assert!(matches!(
904 error.error,
905 ParserError::OversizedRecord { length } if length == MAX_MRT_MESSAGE_LEN + 1
906 ));
907 assert_eq!(error.bytes.as_deref(), Some(&wire[..12]));
908 assert_eq!(cursor.position(), 12);
909 }
910
911 #[test]
912 fn chunk_reports_oversized_length_for_unknown_entry_type() {
913 let mut wire = BytesMut::new();
914 wire.put_u32(1);
915 wire.put_u16(0x7777);
916 wire.put_u16(0);
917 wire.put_u32(u32::MAX);
918 wire.put_slice(&[0xaa; 32]);
919 let mut cursor = Cursor::new(wire.to_vec());
920
921 let error = chunk_mrt_record(&mut cursor).unwrap_err();
922 assert!(matches!(
923 error.error,
924 ParserError::OversizedRecord { length: u32::MAX }
925 ));
926 assert_eq!(error.bytes.as_deref(), Some(&wire[..12]));
927 assert_eq!(cursor.position(), 12);
928 }
929}