1use crate::error::{ParserError, ParserErrorWithBytes};
2use crate::models::*;
3use crate::parser::bgp::attributes::{parse_as_path, parse_nlri, AttributeValidationState};
4use crate::parser::bgp::messages::read_and_validate_bgp_marker;
5use crate::parser::iters::write_mrt_core_dump;
6use crate::parser::mrt::messages::bgp4mp::{bgp4mp_message_payload_len, is_short_zebra_open};
7use crate::parser::mrt::messages::legacy_bgp::{
8 BGP_KEEPALIVE, BGP_NOTIFY, BGP_OPEN, BGP_STATE_CHANGE, BGP_UPDATE,
9};
10use crate::parser::mrt::messages::table_dump_v2::rib_entry_min_len;
11use crate::parser::mrt::mrt_record::raw_record_uses_zebra_compat;
12use crate::parser::{
13 chunk_mrt_record, parse_legacy_bgp, parse_nlri_list, BgpkitParser, Filterable, ReadUtils,
14};
15use bytes::{Buf, Bytes};
16use ipnet::IpNet;
17use log::{error, warn};
18use std::io::Read;
19use std::net::{IpAddr, Ipv4Addr};
20use std::sync::Arc;
21
22#[derive(Default)]
23struct RouteAttributes {
24 as_path: Option<Arc<AsPath>>,
25 announced: Vec<NetworkPrefix>,
26 withdrawn: Vec<NetworkPrefix>,
27}
28
29struct RouteAttributeContext<'a> {
30 afi: Option<Afi>,
31 safi: Option<Safi>,
32 prefixes: Option<&'a [NetworkPrefix]>,
33 is_announcement: Option<bool>,
34 has_standard_nlri: bool,
35}
36
37fn merge_as_path(as_path: Option<AsPath>, as4_path: Option<AsPath>) -> Option<Arc<AsPath>> {
38 let path = match (as_path, as4_path) {
39 (None, None) => None,
40 (Some(path), None) | (None, Some(path)) => Some(path),
41 (Some(path), Some(as4_path)) => Some(AsPath::merge_aspath_as4path(&path, &as4_path)),
42 };
43 path.map(Arc::new)
44}
45
46fn parse_route_attributes(
47 mut data: Bytes,
48 asn_len: &AsnLength,
49 add_path: bool,
50 ctx: RouteAttributeContext<'_>,
51) -> Result<RouteAttributes, ParserError> {
52 let mut validation = AttributeValidationState::new();
53 let mut as_path = None;
54 let mut as4_path = None;
55 let mut announced = Vec::new();
56 let mut withdrawn = Vec::new();
57
58 while data.remaining() >= 3 {
59 let flags = AttrFlags::from_bits_retain(data.read_u8()?);
60 let raw_attr_type = data.read_u8()?;
61 let attr_length = if flags.contains(AttrFlags::EXTENDED) {
62 data.read_u16()? as usize
63 } else {
64 data.read_u8()? as usize
65 };
66 let attr_type = AttrType::from(raw_attr_type);
67 let partial = validation.observe_header(raw_attr_type, attr_type, flags, attr_length);
68
69 if data.remaining() < attr_length {
70 warn!(
71 "{:?} attribute encodes a length ({}) that is longer than the remaining attribute data ({}). Skipping remaining attribute data for BGP message",
72 attr_type,
73 attr_length,
74 data.remaining()
75 );
76 break;
77 }
78
79 let attr_data = data.split_to(attr_length);
80 let result = match attr_type {
81 AttrType::AS_PATH => parse_as_path(attr_data, asn_len).map(|path| {
82 as_path = Some(path);
83 }),
84 AttrType::AS4_PATH => parse_as_path(attr_data, &AsnLength::Bits32).map(|path| {
85 as4_path = Some(path);
86 }),
87 AttrType::MP_REACHABLE_NLRI => parse_nlri(
88 attr_data,
89 &ctx.afi,
90 &ctx.safi,
91 &ctx.prefixes,
92 true,
93 add_path,
94 )
95 .map(|attr| {
96 if let AttributeValue::MpReachNlri(nlri) = attr {
97 announced = nlri.prefixes;
98 }
99 }),
100 AttrType::MP_UNREACHABLE_NLRI => parse_nlri(
101 attr_data,
102 &ctx.afi,
103 &ctx.safi,
104 &ctx.prefixes,
105 false,
106 add_path,
107 )
108 .map(|attr| {
109 if let AttributeValue::MpUnreachNlri(nlri) = attr {
110 withdrawn = nlri.prefixes;
111 }
112 }),
113 _ => Ok(()),
114 };
115
116 if let Err(err) = result {
117 validation.observe_parse_error(attr_type, partial, &err);
118 }
119 }
120
121 let is_announcement = ctx
122 .is_announcement
123 .unwrap_or(ctx.has_standard_nlri || validation.has_attr(AttrType::MP_REACHABLE_NLRI));
124 validation.check_mandatory_attributes(is_announcement, ctx.has_standard_nlri);
125 let _warnings = validation.finish();
126 Ok(RouteAttributes {
127 as_path: merge_as_path(as_path, as4_path),
128 announced,
129 withdrawn,
130 })
131}
132
133fn record_timestamp(common_header: &CommonHeader) -> f64 {
134 match common_header.microsecond_timestamp {
135 Some(microseconds) => common_header.timestamp as f64 + microseconds as f64 / 1_000_000.0,
136 None => common_header.timestamp as f64,
137 }
138}
139
140struct RouteUpdateIter {
141 timestamp: f64,
142 peer_ip: IpAddr,
143 peer_asn: Asn,
144 as_path: Option<Arc<AsPath>>,
145 announced:
146 std::iter::Chain<std::vec::IntoIter<NetworkPrefix>, std::vec::IntoIter<NetworkPrefix>>,
147 withdrawn:
148 std::iter::Chain<std::vec::IntoIter<NetworkPrefix>, std::vec::IntoIter<NetworkPrefix>>,
149 in_withdrawn_phase: bool,
150}
151
152impl RouteUpdateIter {
153 fn next_route(&mut self) -> Option<BgpRouteElem> {
154 if !self.in_withdrawn_phase {
155 if let Some(prefix) = self.announced.next() {
156 return Some(BgpRouteElem {
157 timestamp: self.timestamp,
158 elem_type: ElemType::ANNOUNCE,
159 peer_ip: self.peer_ip,
160 peer_asn: self.peer_asn,
161 prefix,
162 as_path: self.as_path.clone(),
163 });
164 }
165 self.in_withdrawn_phase = true;
166 }
167
168 self.withdrawn.next().map(|prefix| BgpRouteElem {
169 timestamp: self.timestamp,
170 elem_type: ElemType::WITHDRAW,
171 peer_ip: self.peer_ip,
172 peer_asn: self.peer_asn,
173 prefix,
174 as_path: None,
175 })
176 }
177}
178
179#[derive(Clone, Default)]
180struct RoutePeerTable {
181 peers: Arc<[Peer]>,
182}
183
184impl RoutePeerTable {
185 fn get_peer_by_id(&self, peer_index: u16) -> Option<Peer> {
186 self.peers.get(peer_index as usize).copied()
187 }
188}
189
190fn parse_route_peer_table(mut data: Bytes) -> Result<RoutePeerTable, ParserError> {
191 let _collector_bgp_id = data.read_u32()?;
192 let view_name_length = data.read_u16()? as usize;
193 data.has_n_remaining(view_name_length)?;
194 data.advance(view_name_length);
195
196 let peer_count = data.read_u16()? as usize;
197 let mut peers = Vec::with_capacity(peer_count);
198 for _ in 0..peer_count {
199 let peer_type = PeerType::from_bits_retain(data.read_u8()?);
200 let afi = if peer_type.contains(PeerType::ADDRESS_FAMILY_IPV6) {
201 Afi::Ipv6
202 } else {
203 Afi::Ipv4
204 };
205 let asn_len = if peer_type.contains(PeerType::AS_SIZE_32BIT) {
206 AsnLength::Bits32
207 } else {
208 AsnLength::Bits16
209 };
210
211 let peer_bgp_id = data.read_ipv4_address()?;
212 let peer_ip = data.read_address(&afi)?;
213 let peer_asn = data.read_asn(asn_len)?;
214 peers.push(Peer {
215 peer_type,
216 peer_bgp_id,
217 peer_ip,
218 peer_asn,
219 });
220 }
221
222 Ok(RoutePeerTable {
223 peers: Arc::from(peers),
224 })
225}
226
227#[derive(Default)]
228enum RouteRecordIter {
229 #[default]
230 Empty,
231 Update(RouteUpdateIter),
232 TableDump(RouteTableDumpIter),
233 RibAfi(RouteRibAfiIter),
234}
235
236impl RouteRecordIter {
237 fn next_route(&mut self) -> Result<Option<BgpRouteElem>, ParserError> {
238 match self {
239 RouteRecordIter::Empty => Ok(None),
240 RouteRecordIter::Update(iter) => Ok(iter.next_route()),
241 RouteRecordIter::TableDump(iter) => iter.next_route(),
242 RouteRecordIter::RibAfi(iter) => iter.next_route(),
243 }
244 }
245}
246
247struct RouteTableDumpIter {
248 data: Bytes,
249 afi: Afi,
250}
251
252impl RouteTableDumpIter {
253 fn next_route(&mut self) -> Result<Option<BgpRouteElem>, ParserError> {
254 if self.data.is_empty() {
255 return Ok(None);
256 }
257
258 let prefix = match self.afi {
259 Afi::Ipv4 => self.data.read_ipv4_prefix().map(IpNet::V4),
260 Afi::Ipv6 => self.data.read_ipv6_prefix().map(IpNet::V6),
261 Afi::LinkState => unreachable!(),
262 }?;
263 let _status = self.data.read_u8()?;
264 let originated_time = self.data.read_u32()? as f64;
265 let peer_ip = self.data.read_address(&self.afi)?;
266 let peer_asn = Asn::new_16bit(self.data.read_u16()?);
267 let attribute_length = self.data.read_u16()? as usize;
268 self.data.has_n_remaining(attribute_length)?;
269 let attrs = parse_route_attributes(
270 self.data.split_to(attribute_length),
271 &AsnLength::Bits16,
272 false,
273 RouteAttributeContext {
274 afi: None,
275 safi: None,
276 prefixes: None,
277 is_announcement: Some(true),
278 has_standard_nlri: self.afi == Afi::Ipv4,
279 },
280 )?;
281
282 Ok(Some(BgpRouteElem {
283 timestamp: originated_time,
284 elem_type: ElemType::ANNOUNCE,
285 peer_ip,
286 peer_asn,
287 prefix: NetworkPrefix::new(prefix, None),
288 as_path: attrs.as_path,
289 }))
290 }
291}
292
293struct RouteRibAfiIter {
294 data: Bytes,
295 peer_table: RoutePeerTable,
296 afi: Afi,
297 safi: Safi,
298 is_add_path: bool,
299 prefix: NetworkPrefix,
300 remaining_entries: u16,
301}
302
303impl RouteRibAfiIter {
304 fn next_route(&mut self) -> Result<Option<BgpRouteElem>, ParserError> {
305 while self.remaining_entries > 0 {
306 if self.data.remaining() < rib_entry_min_len(self.is_add_path) {
307 warn!("early break due to truncated msg while parsing RIB AFI entries");
308 self.remaining_entries = 0;
309 return Ok(None);
310 }
311
312 self.remaining_entries -= 1;
313 let peer_index = self.data.read_u16()?;
314 let originated_time = self.data.read_u32()? as f64;
315 let _path_id = if self.is_add_path {
316 Some(self.data.read_u32()?)
317 } else {
318 None
319 };
320 let attribute_length = self.data.read_u16()? as usize;
321 if self.data.remaining() < attribute_length {
322 warn!(
323 "early break due to truncated attribute payload while parsing RIB AFI entries: expected {} bytes, have {} bytes available",
324 attribute_length,
325 self.data.remaining()
326 );
327 self.remaining_entries = 0;
328 return Ok(None);
329 }
330
331 let prefixes = [self.prefix];
332 let attrs = parse_route_attributes(
333 self.data.split_to(attribute_length),
334 &AsnLength::Bits32,
335 self.is_add_path,
336 RouteAttributeContext {
337 afi: Some(self.afi),
338 safi: Some(self.safi),
339 prefixes: Some(&prefixes),
340 is_announcement: Some(true),
341 has_standard_nlri: self.afi == Afi::Ipv4,
342 },
343 )?;
344 let Some(peer) = self.peer_table.get_peer_by_id(peer_index) else {
345 error!("peer ID {} not found in peer_index table", peer_index);
346 continue;
347 };
348
349 return Ok(Some(BgpRouteElem {
350 timestamp: originated_time,
351 elem_type: ElemType::ANNOUNCE,
352 peer_ip: peer.peer_ip,
353 peer_asn: peer.peer_asn,
354 prefix: self.prefix,
355 as_path: attrs.as_path,
356 }));
357 }
358
359 Ok(None)
360 }
361}
362
363fn parse_bgp_update_routes(
364 mut input: Bytes,
365 add_path: bool,
366 asn_len: &AsnLength,
367 timestamp: f64,
368 peer_ip: IpAddr,
369 peer_asn: Asn,
370) -> Result<RouteUpdateIter, ParserError> {
371 let withdrawn_len = input.read_u16()? as usize;
372 input.has_n_remaining(withdrawn_len)?;
373 let withdrawn_prefixes = parse_nlri_list(input.split_to(withdrawn_len), add_path, &Afi::Ipv4)?;
374
375 let attribute_length = input.read_u16()? as usize;
376 input.has_n_remaining(attribute_length)?;
377 let attribute_bytes = input.split_to(attribute_length);
378 let announced_prefixes = parse_nlri_list(input, add_path, &Afi::Ipv4)?;
379 let attributes = parse_route_attributes(
380 attribute_bytes,
381 asn_len,
382 add_path,
383 RouteAttributeContext {
384 afi: None,
385 safi: None,
386 prefixes: None,
387 is_announcement: None,
388 has_standard_nlri: !announced_prefixes.is_empty(),
389 },
390 )?;
391
392 Ok(RouteUpdateIter {
393 timestamp,
394 peer_ip,
395 peer_asn,
396 as_path: attributes.as_path,
397 announced: announced_prefixes.into_iter().chain(attributes.announced),
398 withdrawn: withdrawn_prefixes.into_iter().chain(attributes.withdrawn),
399 in_withdrawn_phase: false,
400 })
401}
402
403fn parse_bgp_message_routes(
404 mut data: Bytes,
405 add_path: bool,
406 asn_len: &AsnLength,
407 timestamp: f64,
408 peer_ip: IpAddr,
409 peer_asn: Asn,
410) -> Result<RouteRecordIter, ParserError> {
411 let total_size = data.len();
412 data.has_n_remaining(19)?;
413 read_and_validate_bgp_marker(&mut data)?;
414 let length = data.read_u16()?;
415 if !(19..=65_535).contains(&length) {
416 return Err(ParserError::ParseError(format!(
417 "invalid BGP message length {length}"
418 )));
419 }
420
421 let bgp_msg_length = if length as usize > total_size {
422 total_size - 19
423 } else {
424 length as usize - 19
425 };
426 let msg_type = BgpMessageType::try_from(data.read_u8()?)
427 .map_err(|_| ParserError::ParseError("Unknown BGP Message Type".to_string()))?;
428
429 if matches!(msg_type, BgpMessageType::OPEN | BgpMessageType::KEEPALIVE) && length > 4096 {
430 return Err(ParserError::ParseError(format!(
431 "BGP {msg_type:?} message length {length} exceeds maximum allowed 4096 bytes (RFC 8654)"
432 )));
433 }
434
435 if data.remaining() != bgp_msg_length {
436 warn!(
437 "BGP message length {} does not match the actual length {} (parsing BGP message)",
438 bgp_msg_length,
439 data.remaining()
440 );
441 }
442 data.has_n_remaining(bgp_msg_length)?;
443 let msg_data = data.split_to(bgp_msg_length);
444
445 match msg_type {
446 BgpMessageType::UPDATE => Ok(RouteRecordIter::Update(parse_bgp_update_routes(
447 msg_data, add_path, asn_len, timestamp, peer_ip, peer_asn,
448 )?)),
449 BgpMessageType::OPEN
450 | BgpMessageType::NOTIFICATION
451 | BgpMessageType::KEEPALIVE
452 | BgpMessageType::ROUTE_REFRESH => Ok(RouteRecordIter::Empty),
453 }
454}
455
456fn bgp4mp_asn_len_and_add_path(msg_type: Bgp4MpType) -> Option<(AsnLength, bool)> {
457 match msg_type {
458 Bgp4MpType::Message | Bgp4MpType::MessageLocal => Some((AsnLength::Bits16, false)),
459 Bgp4MpType::MessageAs4 | Bgp4MpType::MessageAs4Local => Some((AsnLength::Bits32, false)),
460 Bgp4MpType::MessageAddpath | Bgp4MpType::MessageLocalAddpath => {
461 Some((AsnLength::Bits16, true))
462 }
463 Bgp4MpType::MessageAs4Addpath | Bgp4MpType::MessageLocalAs4Addpath => {
464 Some((AsnLength::Bits32, true))
465 }
466 Bgp4MpType::StateChange | Bgp4MpType::StateChangeAs4 => None,
467 }
468}
469
470fn parse_bgp4mp_routes(
471 sub_type: u16,
472 mut data: Bytes,
473 timestamp: f64,
474) -> Result<RouteRecordIter, ParserError> {
475 let msg_type = Bgp4MpType::try_from(sub_type)?;
476 let Some((asn_len, add_path)) = bgp4mp_asn_len_and_add_path(msg_type) else {
477 return Ok(RouteRecordIter::Empty);
478 };
479
480 let total_size = data.len();
481 let is_short_zebra_open = is_short_zebra_open(&data, &asn_len);
482 let peer_asn = data.read_asn(asn_len)?;
483 let _local_asn = data.read_asn(asn_len)?;
484 if is_short_zebra_open {
485 return parse_bgp_message_routes(
486 data,
487 add_path,
488 &asn_len,
489 timestamp,
490 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
491 peer_asn,
492 );
493 }
494 let _interface_index = data.read_u16()?;
495 let afi = data.read_afi()?;
496 let should_read = bgp4mp_message_payload_len(&afi, &asn_len, total_size)?;
497 let peer_ip = data.read_address(&afi)?;
498 let _local_ip = data.read_address(&afi)?;
499
500 if should_read != data.remaining() {
501 return Err(ParserError::TruncatedMsg(format!(
502 "truncated bgp4mp message: should read {} bytes, have {} bytes available",
503 should_read,
504 data.remaining()
505 )));
506 }
507
508 parse_bgp_message_routes(data, add_path, &asn_len, timestamp, peer_ip, peer_asn)
509}
510
511fn table_dump_v2_afi_safi(rib_type: TableDumpV2Type) -> Result<(Afi, Safi), ParserError> {
512 match rib_type {
513 TableDumpV2Type::RibIpv4Unicast | TableDumpV2Type::RibIpv4UnicastAddPath => {
514 Ok((Afi::Ipv4, Safi::Unicast))
515 }
516 TableDumpV2Type::RibIpv4Multicast | TableDumpV2Type::RibIpv4MulticastAddPath => {
517 Ok((Afi::Ipv4, Safi::Multicast))
518 }
519 TableDumpV2Type::RibIpv6Unicast | TableDumpV2Type::RibIpv6UnicastAddPath => {
520 Ok((Afi::Ipv6, Safi::Unicast))
521 }
522 TableDumpV2Type::RibIpv6Multicast | TableDumpV2Type::RibIpv6MulticastAddPath => {
523 Ok((Afi::Ipv6, Safi::Multicast))
524 }
525 _ => Err(ParserError::ParseError(format!(
526 "wrong RIB type for parsing: {rib_type:?}"
527 ))),
528 }
529}
530
531fn is_add_path_rib_type(rib_type: TableDumpV2Type) -> bool {
532 matches!(
533 rib_type,
534 TableDumpV2Type::RibIpv4UnicastAddPath
535 | TableDumpV2Type::RibIpv4MulticastAddPath
536 | TableDumpV2Type::RibIpv6UnicastAddPath
537 | TableDumpV2Type::RibIpv6MulticastAddPath
538 )
539}
540
541fn parse_table_dump_routes(sub_type: u16, mut data: Bytes) -> Result<RouteRecordIter, ParserError> {
542 let afi = match sub_type {
543 1 => Afi::Ipv4,
544 2 => Afi::Ipv6,
545 _ => {
546 return Err(ParserError::ParseError(format!(
547 "Invalid subtype found for TABLE_DUMP (V1) message: {sub_type}"
548 )))
549 }
550 };
551
552 let _view_number = data.read_u16()?;
553 let _sequence_number = data.read_u16()?;
554 if data.is_empty() {
555 return Err(ParserError::TruncatedMsg(
556 "TABLE_DUMP record contains no entries".to_string(),
557 ));
558 }
559 Ok(RouteRecordIter::TableDump(RouteTableDumpIter { data, afi }))
560}
561
562fn parse_legacy_bgp_routes(
563 sub_type: u16,
564 mut data: Bytes,
565 timestamp: f64,
566) -> Result<RouteRecordIter, ParserError> {
567 match sub_type {
568 BGP_UPDATE => {
569 let peer_asn = Asn::new_16bit(data.read_u16()?);
570 let peer_ip = IpAddr::V4(data.read_ipv4_address()?);
571 let _local_asn = data.read_u16()?;
572 let _local_ip = data.read_ipv4_address()?;
573 Ok(RouteRecordIter::Update(parse_bgp_update_routes(
574 data,
575 false,
576 &AsnLength::Bits16,
577 timestamp,
578 peer_ip,
579 peer_asn,
580 )?))
581 }
582 BGP_STATE_CHANGE | BGP_OPEN | BGP_NOTIFY | BGP_KEEPALIVE => {
583 parse_legacy_bgp(sub_type, data)?;
584 Ok(RouteRecordIter::Empty)
585 }
586 _ => Err(ParserError::Unsupported(format!(
587 "unsupported legacy BGP subtype: {sub_type}"
588 ))),
589 }
590}
591
592fn parse_table_dump_v2_routes(
593 sub_type: u16,
594 mut data: Bytes,
595 peer_table: &mut Option<RoutePeerTable>,
596) -> Result<RouteRecordIter, ParserError> {
597 let v2_type = TableDumpV2Type::try_from(sub_type)?;
598 match v2_type {
599 TableDumpV2Type::PeerIndexTable => {
600 *peer_table = Some(parse_route_peer_table(data)?);
601 Ok(RouteRecordIter::Empty)
602 }
603 TableDumpV2Type::GeoPeerTable => Ok(RouteRecordIter::Empty),
604 TableDumpV2Type::RibGeneric | TableDumpV2Type::RibGenericAddPath => Err(
605 ParserError::Unsupported("TableDumpV2 RibGeneric is not currently supported".into()),
606 ),
607 rib_type => {
608 let (afi, safi) = table_dump_v2_afi_safi(rib_type)?;
609 let is_add_path = is_add_path_rib_type(rib_type);
610 let _sequence_number = data.read_u32()?;
611 let prefix = data.read_nlri_prefix(&afi, false)?;
612 let entry_count = data.read_u16()?;
613 let Some(peer_table) = peer_table.clone() else {
614 return Err(ParserError::ParseError(
615 "peer table not set for TableDumpV2 RIB entries".to_string(),
616 ));
617 };
618
619 Ok(RouteRecordIter::RibAfi(RouteRibAfiIter {
620 data,
621 peer_table,
622 afi,
623 safi,
624 is_add_path,
625 prefix,
626 remaining_entries: entry_count,
627 }))
628 }
629 }
630}
631
632fn parse_raw_record_route_iter(
633 raw_record: crate::RawMrtRecord,
634 peer_table: &mut Option<RoutePeerTable>,
635) -> Result<RouteRecordIter, ParserError> {
636 let timestamp = record_timestamp(&raw_record.common_header);
637 match raw_record.common_header.entry_type {
638 EntryType::TABLE_DUMP => parse_table_dump_routes(
639 raw_record.common_header.entry_subtype,
640 raw_record.message_bytes,
641 ),
642 EntryType::TABLE_DUMP_V2 => parse_table_dump_v2_routes(
643 raw_record.common_header.entry_subtype,
644 raw_record.message_bytes,
645 peer_table,
646 ),
647 EntryType::BGP4MP | EntryType::BGP4MP_ET => parse_bgp4mp_routes(
648 raw_record.common_header.entry_subtype,
649 raw_record.message_bytes,
650 timestamp,
651 ),
652 EntryType::BGP => parse_legacy_bgp_routes(
653 raw_record.common_header.entry_subtype,
654 raw_record.message_bytes,
655 timestamp,
656 ),
657 v => Err(ParserError::Unsupported(format!(
658 "unsupported MRT type: {v:?}"
659 ))),
660 }
661}
662
663pub struct RouteIterator<R> {
664 parser: BgpkitParser<R>,
665 pending_routes: RouteRecordIter,
666 peer_table: Option<RoutePeerTable>,
667 pending_raw_bytes: Option<Vec<u8>>,
668}
669
670impl<R> RouteIterator<R> {
671 pub(crate) fn new(parser: BgpkitParser<R>) -> Self {
672 Self {
673 parser,
674 pending_routes: RouteRecordIter::Empty,
675 peer_table: None,
676 pending_raw_bytes: None,
677 }
678 }
679}
680
681impl<R: Read> Iterator for RouteIterator<R> {
682 type Item = BgpRouteElem;
683
684 fn next(&mut self) -> Option<Self::Item> {
685 if self.parser.text_dump_iter.is_some() {
687 return None;
688 }
689 loop {
690 match self.pending_routes.next_route() {
691 Ok(Some(route)) => {
692 if route.match_filters(&self.parser.filters) {
693 return Some(route);
694 }
695 continue;
696 }
697 Ok(None) => {
698 self.pending_raw_bytes = None;
699 }
700 Err(err) => {
701 error!("parser error: {}", err);
702 self.pending_routes = RouteRecordIter::Empty;
703 write_mrt_core_dump(self.parser.core_dump, self.pending_raw_bytes.take());
704 if self.parser.core_dump {
705 return None;
706 }
707 continue;
708 }
709 }
710
711 let raw_record = match chunk_mrt_record(&mut self.parser.reader) {
712 Ok(raw_record) => raw_record,
713 Err(e) => match e.error {
714 ParserError::TruncatedMsg(err_str) | ParserError::Unsupported(err_str) => {
715 if self.parser.options.show_warnings {
716 warn!("parser warn: {}", err_str);
717 }
718 write_mrt_core_dump(self.parser.core_dump, e.bytes);
719 continue;
720 }
721 ParserError::ParseError(err_str) => {
722 error!("parser error: {}", err_str);
723 write_mrt_core_dump(self.parser.core_dump, e.bytes);
724 if self.parser.core_dump {
725 return None;
726 }
727 continue;
728 }
729 ParserError::EofExpected => return None,
730 ParserError::IoError(err) | ParserError::EofError(err) => {
731 error!("{:?}", err);
732 write_mrt_core_dump(self.parser.core_dump, e.bytes);
733 return None;
734 }
735 #[cfg(feature = "oneio")]
736 ParserError::OneIoError(_) => return None,
737 ParserError::FilterError(_) => return None,
738 ParserError::InvalidLabeledNlriLength
739 | ParserError::TruncatedLabeledNlri
740 | ParserError::TruncatedPrefix
741 | ParserError::MaxLabelStackDepthExceeded
742 | ParserError::PeerMaxLabelsExceeded
743 | ParserError::InvalidPrefix => {
744 if self.parser.options.show_warnings {
745 warn!("parser warn: labeled NLRI parsing error: {:?}", e.error);
746 }
747 continue;
748 }
749 },
750 };
751
752 let used_zebra_compat = raw_record_uses_zebra_compat(&raw_record);
753 let raw_bytes = raw_record.raw_bytes().to_vec();
754 match parse_raw_record_route_iter(raw_record, &mut self.peer_table) {
755 Ok(routes) => {
756 if used_zebra_compat {
757 self.parser.warn_zebra_compat_once();
758 }
759 self.pending_routes = routes;
760 self.pending_raw_bytes = Some(raw_bytes);
761 }
762 Err(err) => {
763 error!("parser error: {}", err);
764 write_mrt_core_dump(self.parser.core_dump, Some(raw_bytes));
765 if self.parser.core_dump {
766 return None;
767 }
768 continue;
769 }
770 }
771 }
772 }
773}
774
775pub struct FallibleRouteIterator<R> {
776 parser: BgpkitParser<R>,
777 pending_routes: RouteRecordIter,
778 peer_table: Option<RoutePeerTable>,
779 pending_raw_bytes: Option<Vec<u8>>,
780}
781
782impl<R> FallibleRouteIterator<R> {
783 pub(crate) fn new(parser: BgpkitParser<R>) -> Self {
784 Self {
785 parser,
786 pending_routes: RouteRecordIter::Empty,
787 peer_table: None,
788 pending_raw_bytes: None,
789 }
790 }
791}
792
793impl<R: Read> Iterator for FallibleRouteIterator<R> {
794 type Item = Result<BgpRouteElem, ParserErrorWithBytes>;
795
796 fn next(&mut self) -> Option<Self::Item> {
797 if self.parser.text_dump_iter.is_some() {
799 return None;
800 }
801 loop {
802 match self.pending_routes.next_route() {
803 Ok(Some(route)) => {
804 if route.match_filters(&self.parser.filters) {
805 return Some(Ok(route));
806 }
807 continue;
808 }
809 Ok(None) => {
810 self.pending_raw_bytes = None;
811 }
812 Err(error) => {
813 self.pending_routes = RouteRecordIter::Empty;
814 return Some(Err(ParserErrorWithBytes {
815 error,
816 bytes: self.pending_raw_bytes.take(),
817 }));
818 }
819 }
820
821 let raw_record = match chunk_mrt_record(&mut self.parser.reader) {
822 Ok(raw_record) => raw_record,
823 Err(e) if matches!(e.error, ParserError::EofExpected) => return None,
824 Err(e) => return Some(Err(e)),
825 };
826
827 let used_zebra_compat = raw_record_uses_zebra_compat(&raw_record);
828 let raw_bytes = raw_record.raw_bytes().to_vec();
829 match parse_raw_record_route_iter(raw_record, &mut self.peer_table) {
830 Ok(routes) => {
831 if used_zebra_compat {
832 self.parser.warn_zebra_compat_once();
833 }
834 self.pending_routes = routes;
835 self.pending_raw_bytes = Some(raw_bytes);
836 }
837 Err(error) => {
838 return Some(Err(ParserErrorWithBytes {
839 error,
840 bytes: Some(raw_bytes),
841 }))
842 }
843 }
844 }
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851 use crate::parser::iters::write_mrt_core_dump_to_path;
852 use bytes::{BufMut, BytesMut};
853 use std::io::Cursor;
854 use std::net::{Ipv4Addr, Ipv6Addr};
855 use std::str::FromStr;
856
857 fn route_projection(elem: BgpElem) -> BgpRouteElem {
858 BgpRouteElem {
859 timestamp: elem.timestamp,
860 elem_type: elem.elem_type,
861 peer_ip: elem.peer_ip,
862 peer_asn: elem.peer_asn,
863 prefix: elem.prefix,
864 as_path: elem.as_path.map(Arc::new),
865 }
866 }
867
868 fn collect_route_record_iter(
869 mut iter: RouteRecordIter,
870 ) -> Result<Vec<BgpRouteElem>, ParserError> {
871 let mut routes = Vec::new();
872 while let Some(route) = iter.next_route()? {
873 routes.push(route);
874 }
875 Ok(routes)
876 }
877
878 fn route_peer_table_from_peer_index(peer_table: PeerIndexTable) -> RoutePeerTable {
879 let mut peer_ids = peer_table.id_peer_map.keys().copied().collect::<Vec<_>>();
880 peer_ids.sort_unstable();
881 let peers = peer_ids
882 .into_iter()
883 .map(|peer_id| peer_table.id_peer_map[&peer_id])
884 .collect::<Vec<_>>();
885
886 RoutePeerTable {
887 peers: Arc::from(peers),
888 }
889 }
890
891 fn update_record() -> MrtRecord {
892 let mut attributes = Attributes::default();
893 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
894 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([64500, 64501])).into());
895 attributes
896 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
897
898 MrtRecord {
899 common_header: CommonHeader {
900 timestamp: 1_700_000_000,
901 microsecond_timestamp: None,
902 entry_type: EntryType::BGP4MP,
903 entry_subtype: Bgp4MpType::MessageAs4 as u16,
904 length: 0,
905 },
906 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
907 msg_type: Bgp4MpType::MessageAs4,
908 peer_asn: Asn::new_32bit(64496),
909 local_asn: Asn::new_32bit(64497),
910 interface_index: 0,
911 peer_ip: IpAddr::from_str("192.0.2.1").unwrap(),
912 local_ip: IpAddr::from_str("192.0.2.2").unwrap(),
913 bgp_message: BgpMessage::Update(BgpUpdateMessage {
914 withdrawn_prefixes: vec![NetworkPrefix::from_str("198.51.100.0/24").unwrap()],
915 attributes,
916 announced_prefixes: vec![NetworkPrefix::from_str("203.0.113.0/24").unwrap()],
917 }),
918 })),
919 }
920 }
921
922 fn route_attributes(as_path: impl AsRef<[u32]>) -> Attributes {
923 let mut attributes = Attributes::default();
924 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
925 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence(as_path)).into());
926 attributes
927 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
928 attributes
929 }
930
931 fn bgp4mp_record(msg_type: Bgp4MpType, bgp_message: BgpMessage) -> MrtRecord {
932 let asn = if matches!(
933 msg_type,
934 Bgp4MpType::Message
935 | Bgp4MpType::MessageLocal
936 | Bgp4MpType::MessageAddpath
937 | Bgp4MpType::MessageLocalAddpath
938 ) {
939 Asn::new_16bit(64496)
940 } else {
941 Asn::new_32bit(64496)
942 };
943
944 MrtRecord {
945 common_header: CommonHeader {
946 timestamp: 1_700_000_000,
947 microsecond_timestamp: None,
948 entry_type: EntryType::BGP4MP,
949 entry_subtype: msg_type as u16,
950 length: 0,
951 },
952 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
953 msg_type,
954 peer_asn: asn,
955 local_asn: Asn::new_32bit(64497),
956 interface_index: 0,
957 peer_ip: IpAddr::from_str("192.0.2.1").unwrap(),
958 local_ip: IpAddr::from_str("192.0.2.2").unwrap(),
959 bgp_message,
960 })),
961 }
962 }
963
964 fn open_message() -> BgpMessage {
965 BgpMessage::Open(BgpOpenMessage {
966 version: 4,
967 asn: Asn::new_16bit(64496),
968 hold_time: 180,
969 bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
970 extended_length: false,
971 opt_params: vec![],
972 })
973 }
974
975 fn raw_bgp_message(length: u16, msg_type: BgpMessageType, payload: &[u8]) -> Bytes {
976 raw_bgp_message_with_marker([0xff; 16], length, msg_type, payload)
977 }
978
979 fn raw_bgp_message_with_marker(
980 marker: [u8; 16],
981 length: u16,
982 msg_type: BgpMessageType,
983 payload: &[u8],
984 ) -> Bytes {
985 let mut bytes = BytesMut::new();
986 bytes.put_slice(&marker);
987 bytes.put_u16(length);
988 bytes.put_u8(msg_type as u8);
989 bytes.put_slice(payload);
990 bytes.freeze()
991 }
992
993 fn table_dump_record() -> MrtRecord {
994 let mut attributes = Attributes::default();
995 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
996 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([64500, 64501])).into());
997 attributes
998 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
999
1000 MrtRecord {
1001 common_header: CommonHeader {
1002 timestamp: 1_700_000_000,
1003 microsecond_timestamp: None,
1004 entry_type: EntryType::TABLE_DUMP,
1005 entry_subtype: 1,
1006 length: 0,
1007 },
1008 message: MrtMessage::TableDumpMessage(TableDumpMessage {
1009 view_number: 0,
1010 sequence_number: 1,
1011 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1012 status: 1,
1013 originated_time: 1_699_999_998,
1014 peer_ip: IpAddr::from_str("192.0.2.20").unwrap(),
1015 peer_asn: Asn::new_16bit(64496),
1016 attributes,
1017 }),
1018 }
1019 }
1020
1021 fn table_dump_ipv6_record() -> MrtRecord {
1022 let mut attributes = Attributes::default();
1023 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
1024 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([64500, 64501])).into());
1025
1026 MrtRecord {
1027 common_header: CommonHeader {
1028 timestamp: 1_700_000_000,
1029 microsecond_timestamp: None,
1030 entry_type: EntryType::TABLE_DUMP,
1031 entry_subtype: 2,
1032 length: 0,
1033 },
1034 message: MrtMessage::TableDumpMessage(TableDumpMessage {
1035 view_number: 0,
1036 sequence_number: 1,
1037 prefix: NetworkPrefix::from_str("2001:db8::/32").unwrap(),
1038 status: 1,
1039 originated_time: 1_699_999_998,
1040 peer_ip: IpAddr::from_str("2001:db8::20").unwrap(),
1041 peer_asn: Asn::new_16bit(64496),
1042 attributes,
1043 }),
1044 }
1045 }
1046
1047 fn table_dump_v2_records_bytes() -> Vec<u8> {
1048 let peer = Peer::new(
1049 "192.0.2.10".parse().unwrap(),
1050 "192.0.2.11".parse().unwrap(),
1051 Asn::new_32bit(64496),
1052 );
1053 let mut peer_table = PeerIndexTable::default();
1054 let peer_index = peer_table.add_peer(peer).unwrap();
1055
1056 let mut attributes = Attributes::default();
1057 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
1058 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([64500, 64501])).into());
1059 attributes
1060 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
1061
1062 let pit_record = MrtRecord {
1063 common_header: CommonHeader {
1064 timestamp: 1_700_000_000,
1065 microsecond_timestamp: None,
1066 entry_type: EntryType::TABLE_DUMP_V2,
1067 entry_subtype: TableDumpV2Type::PeerIndexTable as u16,
1068 length: 0,
1069 },
1070 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(peer_table)),
1071 };
1072 let rib_record = MrtRecord {
1073 common_header: CommonHeader {
1074 timestamp: 1_700_000_001,
1075 microsecond_timestamp: None,
1076 entry_type: EntryType::TABLE_DUMP_V2,
1077 entry_subtype: TableDumpV2Type::RibIpv4Unicast as u16,
1078 length: 0,
1079 },
1080 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(RibAfiEntries {
1081 rib_type: TableDumpV2Type::RibIpv4Unicast,
1082 sequence_number: 1,
1083 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1084 rib_entries: vec![RibEntry {
1085 peer_index,
1086 originated_time: 1_699_999_999,
1087 path_id: None,
1088 attributes,
1089 }],
1090 })),
1091 };
1092
1093 let mut bytes = pit_record.encode().unwrap().to_vec();
1094 bytes.extend_from_slice(&rib_record.encode().unwrap());
1095 bytes
1096 }
1097
1098 fn table_dump_v2_truncated_attribute_payload() -> (Vec<u8>, Bytes, PeerIndexTable) {
1099 let peer = Peer::new(
1100 "192.0.2.10".parse().unwrap(),
1101 "192.0.2.11".parse().unwrap(),
1102 Asn::new_32bit(64496),
1103 );
1104 let mut peer_table = PeerIndexTable::default();
1105 let peer_index = peer_table.add_peer(peer).unwrap();
1106
1107 let pit_record = MrtRecord {
1108 common_header: CommonHeader {
1109 timestamp: 1_700_000_000,
1110 microsecond_timestamp: None,
1111 entry_type: EntryType::TABLE_DUMP_V2,
1112 entry_subtype: TableDumpV2Type::PeerIndexTable as u16,
1113 length: 0,
1114 },
1115 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(
1116 peer_table.clone(),
1117 )),
1118 };
1119
1120 let first_entry = RibEntry {
1121 peer_index,
1122 originated_time: 1_699_999_999,
1123 path_id: None,
1124 attributes: route_attributes([64500, 64501]),
1125 };
1126
1127 let mut rib_body = BytesMut::new();
1128 rib_body.put_u32(1);
1129 rib_body.extend(NetworkPrefix::from_str("203.0.113.0/24").unwrap().encode());
1130 rib_body.put_u16(2);
1131 rib_body.extend(first_entry.encode().unwrap());
1132 rib_body.put_u16(peer_index);
1133 rib_body.put_u32(1_699_999_998);
1134 rib_body.put_u16(32);
1135 rib_body.put_u8(0);
1136
1137 let rib_body = rib_body.freeze();
1138 let rib_header = CommonHeader {
1139 timestamp: 1_700_000_001,
1140 microsecond_timestamp: None,
1141 entry_type: EntryType::TABLE_DUMP_V2,
1142 entry_subtype: TableDumpV2Type::RibIpv4Unicast as u16,
1143 length: rib_body.len() as u32,
1144 };
1145
1146 let mut bytes = pit_record.encode().unwrap().to_vec();
1147 bytes.extend_from_slice(&rib_header.encode());
1148 bytes.extend_from_slice(&rib_body);
1149
1150 (bytes, rib_body, peer_table)
1151 }
1152
1153 fn assert_filtered_route_projection(bytes: Vec<u8>, filters: &[(&str, &str)]) {
1154 let elem_parser = filters.iter().fold(
1155 BgpkitParser::from_reader(Cursor::new(bytes.clone())),
1156 |parser, (filter_type, filter_value)| {
1157 parser.add_filter(filter_type, filter_value).unwrap()
1158 },
1159 );
1160 let route_parser = filters.iter().fold(
1161 BgpkitParser::from_reader(Cursor::new(bytes)),
1162 |parser, (filter_type, filter_value)| {
1163 parser.add_filter(filter_type, filter_value).unwrap()
1164 },
1165 );
1166
1167 let elem_projection = elem_parser
1168 .into_elem_iter()
1169 .map(route_projection)
1170 .collect::<Vec<_>>();
1171 let routes = route_parser.into_route_iter().collect::<Vec<_>>();
1172
1173 assert_eq!(routes, elem_projection, "filters: {filters:?}");
1174 }
1175
1176 fn assert_route_projection(bytes: Vec<u8>) -> Vec<BgpRouteElem> {
1177 let elem_projection = BgpkitParser::from_reader(Cursor::new(bytes.clone()))
1178 .into_elem_iter()
1179 .map(route_projection)
1180 .collect::<Vec<_>>();
1181 let routes = BgpkitParser::from_reader(Cursor::new(bytes))
1182 .into_route_iter()
1183 .collect::<Vec<_>>();
1184
1185 assert_eq!(routes, elem_projection);
1186 routes
1187 }
1188
1189 #[test]
1190 fn bgp4mp_routes_rejects_link_state_envelope_afi() {
1191 let mut data = BytesMut::new();
1192 data.put_u16(65000);
1193 data.put_u16(65001);
1194 data.put_u16(0);
1195 data.put_u16(Afi::LinkState as u16);
1196 data.put_slice(&BgpMessage::KeepAlive.encode(AsnLength::Bits16).unwrap());
1197
1198 let error =
1199 match parse_bgp4mp_routes(Bgp4MpType::Message as u16, data.freeze(), 1_700_000_000.0) {
1200 Err(error) => error,
1201 Ok(_) => panic!("unexpectedly parsed BGP4MP routes"),
1202 };
1203 assert!(matches!(
1204 error,
1205 ParserError::ParseError(message)
1206 if message == "Link-State AFI is invalid in a BGP4MP envelope"
1207 ));
1208 }
1209
1210 #[test]
1211 fn route_iterator_matches_elem_projection_for_update() {
1212 let bytes = update_record().encode().unwrap().to_vec();
1213 let routes = assert_route_projection(bytes);
1214 assert_eq!(routes.len(), 2);
1215 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1216 assert_eq!(routes[1].elem_type, ElemType::WITHDRAW);
1217 assert!(routes[1].as_path.is_none());
1218 }
1219
1220 #[test]
1221 fn route_iterator_shares_as_path_for_update_announcements() {
1222 let bytes = bgp4mp_record(
1223 Bgp4MpType::MessageAs4,
1224 BgpMessage::Update(BgpUpdateMessage {
1225 withdrawn_prefixes: vec![],
1226 attributes: route_attributes([64500, 64501]),
1227 announced_prefixes: vec![
1228 NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1229 NetworkPrefix::from_str("198.51.100.0/24").unwrap(),
1230 ],
1231 }),
1232 )
1233 .encode()
1234 .unwrap()
1235 .to_vec();
1236
1237 let routes = BgpkitParser::from_reader(Cursor::new(bytes))
1238 .into_route_iter()
1239 .collect::<Vec<_>>();
1240
1241 assert_eq!(routes.len(), 2);
1242 assert!(Arc::ptr_eq(
1243 routes[0].as_path.as_ref().unwrap(),
1244 routes[1].as_path.as_ref().unwrap()
1245 ));
1246 }
1247
1248 #[test]
1249 fn route_iterator_uses_microsecond_timestamps() {
1250 let timestamp = record_timestamp(&CommonHeader {
1251 timestamp: 1_700_000_000,
1252 microsecond_timestamp: Some(123_456),
1253 entry_type: EntryType::BGP4MP_ET,
1254 entry_subtype: Bgp4MpType::MessageAs4 as u16,
1255 length: 0,
1256 });
1257
1258 assert_eq!(timestamp, 1_700_000_000.123_456);
1259 }
1260
1261 #[test]
1262 fn route_iterator_matches_elem_projection_for_mp_update() {
1263 let mut attributes = route_attributes([64500, 64501]);
1264 attributes.add_attr(
1265 AttributeValue::MpReachNlri(Nlri::new_reachable(
1266 NetworkPrefix::from_str("2001:db8::/32").unwrap(),
1267 Some(IpAddr::from_str("2001:db8::1").unwrap()),
1268 ))
1269 .into(),
1270 );
1271 attributes.add_attr(
1272 AttributeValue::MpUnreachNlri(Nlri::new_unreachable(
1273 NetworkPrefix::from_str("2001:db8:1::/48").unwrap(),
1274 ))
1275 .into(),
1276 );
1277
1278 let bytes = bgp4mp_record(
1279 Bgp4MpType::MessageAs4,
1280 BgpMessage::Update(BgpUpdateMessage {
1281 withdrawn_prefixes: vec![],
1282 attributes,
1283 announced_prefixes: vec![],
1284 }),
1285 )
1286 .encode()
1287 .unwrap()
1288 .to_vec();
1289
1290 let routes = assert_route_projection(bytes);
1291 assert_eq!(routes.len(), 2);
1292 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1293 assert_eq!(
1294 routes[0].prefix,
1295 NetworkPrefix::from_str("2001:db8::/32").unwrap()
1296 );
1297 assert_eq!(routes[1].elem_type, ElemType::WITHDRAW);
1298 assert_eq!(
1299 routes[1].prefix,
1300 NetworkPrefix::from_str("2001:db8:1::/48").unwrap()
1301 );
1302 }
1303
1304 #[test]
1305 fn route_iterator_matches_elem_projection_for_non_update_bgp4mp_messages() {
1306 let records = [
1307 bgp4mp_record(Bgp4MpType::Message, open_message()),
1308 bgp4mp_record(
1309 Bgp4MpType::MessageAs4,
1310 BgpMessage::Notification(BgpNotificationMessage {
1311 error: BgpError::Unknown(1, 0),
1312 data: vec![],
1313 }),
1314 ),
1315 bgp4mp_record(Bgp4MpType::MessageAddpath, BgpMessage::KeepAlive),
1316 bgp4mp_record(Bgp4MpType::MessageAs4Addpath, BgpMessage::KeepAlive),
1317 ];
1318 let mut bytes = Vec::new();
1319 for record in records {
1320 bytes.extend_from_slice(&record.encode().unwrap());
1321 }
1322
1323 assert!(assert_route_projection(bytes).is_empty());
1324 }
1325
1326 #[test]
1327 fn route_iterator_matches_elem_projection_for_bgp4mp_16bit_update() {
1328 let bytes = bgp4mp_record(
1329 Bgp4MpType::Message,
1330 BgpMessage::Update(BgpUpdateMessage {
1331 withdrawn_prefixes: vec![],
1332 attributes: route_attributes([64500, 64501]),
1333 announced_prefixes: vec![NetworkPrefix::from_str("203.0.113.0/24").unwrap()],
1334 }),
1335 )
1336 .encode()
1337 .unwrap()
1338 .to_vec();
1339
1340 let routes = assert_route_projection(bytes);
1341 assert_eq!(routes.len(), 1);
1342 assert_eq!(routes[0].peer_asn, Asn::new_16bit(64496));
1343 }
1344
1345 #[test]
1346 fn route_iterator_filters_match_elem_projection_for_update() {
1347 let bytes = update_record().encode().unwrap().to_vec();
1348 let cases: &[&[(&str, &str)]] = &[
1349 &[("peer_ip", "192.0.2.1")],
1350 &[("peer_ip", "192.0.2.99")],
1351 &[("peer_asn", "64496")],
1352 &[("type", "a")],
1353 &[("type", "w")],
1354 &[("type", "!w")],
1355 &[("prefix", "203.0.113.0/24")],
1356 &[("prefix", "198.51.100.0/24")],
1357 &[("prefix_super", "203.0.113.128/25")],
1358 &[("origin_asn", "64501")],
1359 &[("origin_asns", "64496,64501")],
1360 &[("as_path", "64500 64501$")],
1361 &[("ip_version", "4")],
1362 &[("ts_start", "1700000000"), ("ts_end", "1700000000")],
1363 &[("peer_ip", "192.0.2.1"), ("type", "a")],
1364 ];
1365
1366 for filters in cases {
1367 assert_filtered_route_projection(bytes.clone(), filters);
1368 }
1369 }
1370
1371 #[test]
1372 fn selective_attribute_parser_merges_as4_path() {
1373 let mut attributes = Attributes::default();
1374 attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([23456, 64497])).into());
1375 attributes.add_attr(AttributeValue::As4Path(AsPath::from_sequence([65536, 64497])).into());
1376
1377 let attrs = parse_route_attributes(
1378 attributes.encode(AsnLength::Bits16).unwrap(),
1379 &AsnLength::Bits16,
1380 false,
1381 RouteAttributeContext {
1382 afi: None,
1383 safi: None,
1384 prefixes: None,
1385 is_announcement: Some(true),
1386 has_standard_nlri: true,
1387 },
1388 )
1389 .unwrap();
1390
1391 assert_eq!(
1392 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1393 vec![65536, 64497]
1394 );
1395 }
1396
1397 #[test]
1398 fn selective_attribute_parser_handles_as_path_without_as4_path() {
1399 let attrs = parse_route_attributes(
1400 route_attributes([64500, 64501])
1401 .encode(AsnLength::Bits16)
1402 .unwrap(),
1403 &AsnLength::Bits16,
1404 false,
1405 RouteAttributeContext {
1406 afi: None,
1407 safi: None,
1408 prefixes: None,
1409 is_announcement: Some(true),
1410 has_standard_nlri: true,
1411 },
1412 )
1413 .unwrap();
1414
1415 assert_eq!(
1416 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1417 vec![64500, 64501]
1418 );
1419 }
1420
1421 #[test]
1422 fn selective_attribute_parser_handles_as4_path_without_as_path() {
1423 let mut attributes = Attributes::default();
1424 attributes.add_attr(AttributeValue::As4Path(AsPath::from_sequence([65536, 64497])).into());
1425
1426 let attrs = parse_route_attributes(
1427 attributes.encode(AsnLength::Bits16).unwrap(),
1428 &AsnLength::Bits16,
1429 false,
1430 RouteAttributeContext {
1431 afi: None,
1432 safi: None,
1433 prefixes: None,
1434 is_announcement: Some(false),
1435 has_standard_nlri: false,
1436 },
1437 )
1438 .unwrap();
1439
1440 assert_eq!(
1441 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1442 vec![65536, 64497]
1443 );
1444 }
1445
1446 #[test]
1447 fn selective_attribute_parser_handles_no_as_path() {
1448 let attrs = parse_route_attributes(
1449 Bytes::new(),
1450 &AsnLength::Bits16,
1451 false,
1452 RouteAttributeContext {
1453 afi: None,
1454 safi: None,
1455 prefixes: None,
1456 is_announcement: Some(false),
1457 has_standard_nlri: false,
1458 },
1459 )
1460 .unwrap();
1461
1462 assert!(attrs.as_path.is_none());
1463 }
1464
1465 #[test]
1466 fn selective_attribute_parser_handles_extended_and_truncated_attributes() {
1467 let mut extended_as_path = BytesMut::new();
1468 extended_as_path.put_u8((AttrFlags::TRANSITIVE | AttrFlags::EXTENDED).bits());
1469 extended_as_path.put_u8(u8::from(AttrType::AS_PATH));
1470 extended_as_path.put_u16(4);
1471 extended_as_path.put_u8(2);
1472 extended_as_path.put_u8(1);
1473 extended_as_path.put_u16(64500);
1474
1475 let attrs = parse_route_attributes(
1476 extended_as_path.freeze(),
1477 &AsnLength::Bits16,
1478 false,
1479 RouteAttributeContext {
1480 afi: None,
1481 safi: None,
1482 prefixes: None,
1483 is_announcement: Some(false),
1484 has_standard_nlri: false,
1485 },
1486 )
1487 .unwrap();
1488 assert_eq!(
1489 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1490 vec![64500]
1491 );
1492
1493 let attrs = parse_route_attributes(
1494 Bytes::from_static(&[0x40, 2, 5, 0]),
1495 &AsnLength::Bits16,
1496 false,
1497 RouteAttributeContext {
1498 afi: None,
1499 safi: None,
1500 prefixes: None,
1501 is_announcement: Some(false),
1502 has_standard_nlri: false,
1503 },
1504 )
1505 .unwrap();
1506 assert!(attrs.as_path.is_none());
1507 }
1508
1509 #[test]
1510 fn selective_attribute_parser_discards_malformed_as_path() {
1511 let attrs = parse_route_attributes(
1512 Bytes::from_static(&[0x40, 2, 1, 0]),
1513 &AsnLength::Bits16,
1514 false,
1515 RouteAttributeContext {
1516 afi: None,
1517 safi: None,
1518 prefixes: None,
1519 is_announcement: Some(false),
1520 has_standard_nlri: false,
1521 },
1522 )
1523 .unwrap();
1524
1525 assert!(attrs.as_path.is_none());
1526 }
1527
1528 #[test]
1529 fn route_iterator_matches_elem_projection_for_table_dump() {
1530 let bytes = table_dump_record().encode().unwrap().to_vec();
1531 let routes = assert_route_projection(bytes);
1532 assert_eq!(routes.len(), 1);
1533 assert_eq!(routes[0].timestamp, 1_699_999_998.0);
1534 assert_eq!(routes[0].peer_asn, Asn::new_16bit(64496));
1535 }
1536
1537 #[test]
1538 fn route_iterator_matches_elem_projection_for_table_dump_ipv6() {
1539 let bytes = table_dump_ipv6_record().encode().unwrap().to_vec();
1540 let routes = assert_route_projection(bytes);
1541 assert_eq!(routes.len(), 1);
1542 assert_eq!(
1543 routes[0].prefix,
1544 NetworkPrefix::from_str("2001:db8::/32").unwrap()
1545 );
1546 assert_eq!(
1547 routes[0].peer_ip,
1548 IpAddr::from(Ipv6Addr::from_str("2001:db8::20").unwrap())
1549 );
1550 }
1551
1552 #[test]
1553 fn route_iterator_matches_elem_projection_for_table_dump_v2() {
1554 let bytes = table_dump_v2_records_bytes();
1555 let routes = assert_route_projection(bytes);
1556 assert_eq!(routes.len(), 1);
1557 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1558 assert_eq!(
1559 routes[0].as_path.as_ref().unwrap().to_u32_vec_opt(false),
1560 Some(vec![64500, 64501])
1561 );
1562 }
1563
1564 #[test]
1565 fn route_iterator_matches_elem_projection_for_table_dump_v2_ipv6_addpath() {
1566 let peer = Peer::new(
1567 "192.0.2.11".parse().unwrap(),
1568 "2001:db8::10".parse().unwrap(),
1569 Asn::new_32bit(64496),
1570 );
1571 let mut peer_table = PeerIndexTable::default();
1572 let peer_index = peer_table.add_peer(peer).unwrap();
1573
1574 let pit_record = MrtRecord {
1575 common_header: CommonHeader {
1576 timestamp: 1_700_000_000,
1577 microsecond_timestamp: None,
1578 entry_type: EntryType::TABLE_DUMP_V2,
1579 entry_subtype: TableDumpV2Type::PeerIndexTable as u16,
1580 length: 0,
1581 },
1582 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(peer_table)),
1583 };
1584 let rib_record = MrtRecord {
1585 common_header: CommonHeader {
1586 timestamp: 1_700_000_001,
1587 microsecond_timestamp: None,
1588 entry_type: EntryType::TABLE_DUMP_V2,
1589 entry_subtype: TableDumpV2Type::RibIpv6UnicastAddPath as u16,
1590 length: 0,
1591 },
1592 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(RibAfiEntries {
1593 rib_type: TableDumpV2Type::RibIpv6UnicastAddPath,
1594 sequence_number: 1,
1595 prefix: NetworkPrefix::from_str("2001:db8::/32").unwrap(),
1596 rib_entries: vec![RibEntry {
1597 peer_index,
1598 originated_time: 1_699_999_999,
1599 path_id: Some(1234),
1600 attributes: route_attributes([64500, 64501]),
1601 }],
1602 })),
1603 };
1604
1605 let mut bytes = pit_record.encode().unwrap().to_vec();
1606 bytes.extend_from_slice(&rib_record.encode().unwrap());
1607 let routes = assert_route_projection(bytes);
1608 assert_eq!(routes.len(), 1);
1609 assert_eq!(
1610 routes[0].prefix,
1611 NetworkPrefix::from_str("2001:db8::/32").unwrap()
1612 );
1613 }
1614
1615 #[test]
1616 fn route_iterator_matches_elem_projection_for_bgp4mp_ipv6_peer_update() {
1617 let record = MrtRecord {
1618 common_header: CommonHeader {
1619 timestamp: 1_700_000_000,
1620 microsecond_timestamp: None,
1621 entry_type: EntryType::BGP4MP,
1622 entry_subtype: Bgp4MpType::MessageAs4 as u16,
1623 length: 0,
1624 },
1625 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
1626 msg_type: Bgp4MpType::MessageAs4,
1627 peer_asn: Asn::new_32bit(64496),
1628 local_asn: Asn::new_32bit(64497),
1629 interface_index: 0,
1630 peer_ip: IpAddr::from_str("2001:db8::1").unwrap(),
1631 local_ip: IpAddr::from_str("2001:db8::2").unwrap(),
1632 bgp_message: BgpMessage::Update(BgpUpdateMessage {
1633 withdrawn_prefixes: vec![],
1634 attributes: route_attributes([64500, 64501]),
1635 announced_prefixes: vec![NetworkPrefix::from_str("203.0.113.0/24").unwrap()],
1636 }),
1637 })),
1638 };
1639
1640 let routes = assert_route_projection(record.encode().unwrap().to_vec());
1641 assert_eq!(routes.len(), 1);
1642 assert_eq!(
1643 routes[0].peer_ip,
1644 IpAddr::from(Ipv6Addr::from_str("2001:db8::1").unwrap())
1645 );
1646 }
1647
1648 #[test]
1649 fn route_iterator_filters_match_elem_projection_for_table_dump_v2() {
1650 let bytes = table_dump_v2_records_bytes();
1651 let cases: &[&[(&str, &str)]] = &[
1652 &[("peer_ip", "192.0.2.10")],
1653 &[("peer_asn", "64496")],
1654 &[("type", "a")],
1655 &[("type", "w")],
1656 &[("prefix", "203.0.113.0/24")],
1657 &[("prefix_sub", "203.0.112.0/23")],
1658 &[("origin_asn", "64501")],
1659 &[("as_path", "64500 64501$")],
1660 &[("ts_start", "1699999999"), ("ts_end", "1699999999")],
1661 &[("peer_asn", "64496"), ("origin_asn", "64501")],
1662 ];
1663
1664 for filters in cases {
1665 assert_filtered_route_projection(bytes.clone(), filters);
1666 }
1667 }
1668
1669 #[test]
1670 fn route_parser_reports_bgp_message_shape_errors() {
1671 assert!(parse_bgp_message_routes(
1672 raw_bgp_message(18, BgpMessageType::KEEPALIVE, &[]),
1673 false,
1674 &AsnLength::Bits16,
1675 1_700_000_000.0,
1676 "192.0.2.1".parse().unwrap(),
1677 Asn::new_16bit(64496)
1678 )
1679 .is_err());
1680 assert!(parse_bgp_message_routes(
1681 raw_bgp_message(4097, BgpMessageType::OPEN, &[]),
1682 false,
1683 &AsnLength::Bits16,
1684 1_700_000_000.0,
1685 "192.0.2.1".parse().unwrap(),
1686 Asn::new_16bit(64496)
1687 )
1688 .is_err());
1689
1690 let routes = collect_route_record_iter(
1691 parse_bgp_message_routes(
1692 raw_bgp_message(30, BgpMessageType::KEEPALIVE, &[]),
1693 false,
1694 &AsnLength::Bits16,
1695 1_700_000_000.0,
1696 "192.0.2.1".parse().unwrap(),
1697 Asn::new_16bit(64496),
1698 )
1699 .unwrap(),
1700 )
1701 .unwrap();
1702 assert!(routes.is_empty());
1703
1704 let routes = collect_route_record_iter(
1705 parse_bgp_message_routes(
1706 raw_bgp_message(19, BgpMessageType::KEEPALIVE, &[0]),
1707 false,
1708 &AsnLength::Bits16,
1709 1_700_000_000.0,
1710 "192.0.2.1".parse().unwrap(),
1711 Asn::new_16bit(64496),
1712 )
1713 .unwrap(),
1714 )
1715 .unwrap();
1716 assert!(routes.is_empty());
1717
1718 let routes = collect_route_record_iter(
1719 parse_bgp_message_routes(
1720 raw_bgp_message_with_marker([0x00; 16], 19, BgpMessageType::KEEPALIVE, &[]),
1721 false,
1722 &AsnLength::Bits16,
1723 1_700_000_000.0,
1724 "192.0.2.1".parse().unwrap(),
1725 Asn::new_16bit(64496),
1726 )
1727 .unwrap(),
1728 )
1729 .unwrap();
1730 assert!(routes.is_empty());
1731 }
1732
1733 #[test]
1734 fn route_core_dump_write_respects_enabled_flag() {
1735 let dir = tempfile::tempdir().unwrap();
1736 let path = dir.path().join("mrt_core_dump");
1737
1738 write_mrt_core_dump_to_path(false, Some(vec![1, 2, 3]), &path);
1739 assert!(!path.exists());
1740
1741 write_mrt_core_dump_to_path(true, Some(vec![1, 2, 3]), &path);
1742 assert_eq!(std::fs::read(&path).unwrap(), vec![1, 2, 3]);
1743 }
1744
1745 #[test]
1746 fn route_parser_handles_table_dump_v2_error_edges() {
1747 let rib = RibAfiEntries {
1748 rib_type: TableDumpV2Type::RibIpv4Unicast,
1749 sequence_number: 1,
1750 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1751 rib_entries: vec![RibEntry {
1752 peer_index: 99,
1753 originated_time: 1_699_999_999,
1754 path_id: None,
1755 attributes: route_attributes([64500, 64501]),
1756 }],
1757 };
1758 let mut no_peer_table = None;
1759 assert!(parse_table_dump_v2_routes(
1760 TableDumpV2Type::RibIpv4Unicast as u16,
1761 rib.encode().unwrap(),
1762 &mut no_peer_table,
1763 )
1764 .is_err());
1765
1766 let mut empty_peer_table = Some(RoutePeerTable::default());
1767 let routes = collect_route_record_iter(
1768 parse_table_dump_v2_routes(
1769 TableDumpV2Type::RibIpv4Unicast as u16,
1770 rib.encode().unwrap(),
1771 &mut empty_peer_table,
1772 )
1773 .unwrap(),
1774 )
1775 .unwrap();
1776 assert!(routes.is_empty());
1777
1778 let mut truncated = BytesMut::new();
1779 truncated.put_u32(1);
1780 truncated.extend(NetworkPrefix::from_str("203.0.113.0/24").unwrap().encode());
1781 truncated.put_u16(1);
1782 let mut empty_peer_table = Some(RoutePeerTable::default());
1783 let routes = collect_route_record_iter(
1784 parse_table_dump_v2_routes(
1785 TableDumpV2Type::RibIpv4Unicast as u16,
1786 truncated.freeze(),
1787 &mut empty_peer_table,
1788 )
1789 .unwrap(),
1790 )
1791 .unwrap();
1792 assert!(routes.is_empty());
1793
1794 let peer = Peer::new(
1795 "192.0.2.10".parse().unwrap(),
1796 "192.0.2.11".parse().unwrap(),
1797 Asn::new_32bit(64496),
1798 );
1799 let mut peer_table = PeerIndexTable::default();
1800 let peer_index = peer_table.add_peer(peer).unwrap();
1801
1802 let first_entry = RibEntry {
1803 peer_index,
1804 originated_time: 1_699_999_999,
1805 path_id: Some(1234),
1806 attributes: route_attributes([64500, 64501]),
1807 };
1808 let mut add_path_truncated = BytesMut::new();
1809 add_path_truncated.put_u32(1);
1810 add_path_truncated.extend(NetworkPrefix::from_str("203.0.113.0/24").unwrap().encode());
1811 add_path_truncated.put_u16(2);
1812 add_path_truncated.extend(first_entry.encode().unwrap());
1813 add_path_truncated.put_u16(peer_index);
1814 add_path_truncated.put_u32(1_699_999_998);
1815 add_path_truncated.put_u32(5678);
1816
1817 let mut peer_table = Some(route_peer_table_from_peer_index(peer_table));
1818 let routes = collect_route_record_iter(
1819 parse_table_dump_v2_routes(
1820 TableDumpV2Type::RibIpv4UnicastAddPath as u16,
1821 add_path_truncated.freeze(),
1822 &mut peer_table,
1823 )
1824 .unwrap(),
1825 )
1826 .unwrap();
1827 assert_eq!(routes.len(), 1);
1828 assert_eq!(
1829 routes[0].prefix,
1830 NetworkPrefix::from_str("203.0.113.0/24").unwrap()
1831 );
1832 }
1833
1834 #[test]
1835 fn route_parser_preserves_table_dump_v2_routes_before_truncated_attribute_payload() {
1836 let (_bytes, rib_body, peer_table) = table_dump_v2_truncated_attribute_payload();
1837 let mut peer_table = Some(route_peer_table_from_peer_index(peer_table));
1838
1839 let routes = collect_route_record_iter(
1840 parse_table_dump_v2_routes(
1841 TableDumpV2Type::RibIpv4Unicast as u16,
1842 rib_body,
1843 &mut peer_table,
1844 )
1845 .unwrap(),
1846 )
1847 .unwrap();
1848
1849 assert_eq!(routes.len(), 1);
1850 assert_eq!(
1851 routes[0].prefix,
1852 NetworkPrefix::from_str("203.0.113.0/24").unwrap()
1853 );
1854 assert_eq!(
1855 routes[0].as_path.as_ref().unwrap().to_u32_vec_opt(false),
1856 Some(vec![64500, 64501])
1857 );
1858 }
1859
1860 #[test]
1861 fn route_iterators_preserve_table_dump_v2_routes_before_truncated_attribute_payload() {
1862 let (bytes, _rib_body, _peer_table) = table_dump_v2_truncated_attribute_payload();
1863
1864 let routes = BgpkitParser::from_reader(Cursor::new(bytes.clone()))
1865 .into_route_iter()
1866 .collect::<Vec<_>>();
1867 assert_eq!(routes.len(), 1);
1868 assert_eq!(
1869 routes[0].prefix,
1870 NetworkPrefix::from_str("203.0.113.0/24").unwrap()
1871 );
1872
1873 let fallible_routes = BgpkitParser::from_reader(Cursor::new(bytes))
1874 .into_fallible_route_iter()
1875 .collect::<Result<Vec<_>, _>>()
1876 .unwrap();
1877 assert_eq!(fallible_routes, routes);
1878 }
1879
1880 fn table_dump_v2_rib_without_peer_table_record() -> MrtRecord {
1881 MrtRecord {
1882 common_header: CommonHeader {
1883 timestamp: 1_700_000_001,
1884 microsecond_timestamp: None,
1885 entry_type: EntryType::TABLE_DUMP_V2,
1886 entry_subtype: TableDumpV2Type::RibIpv4Unicast as u16,
1887 length: 0,
1888 },
1889 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(RibAfiEntries {
1890 rib_type: TableDumpV2Type::RibIpv4Unicast,
1891 sequence_number: 1,
1892 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1893 rib_entries: vec![RibEntry {
1894 peer_index: 0,
1895 originated_time: 1_699_999_999,
1896 path_id: None,
1897 attributes: route_attributes([64500, 64501]),
1898 }],
1899 })),
1900 }
1901 }
1902
1903 #[test]
1904 fn route_iterator_skips_route_parse_errors() {
1905 let routes = BgpkitParser::from_reader(Cursor::new(
1906 table_dump_v2_rib_without_peer_table_record()
1907 .encode()
1908 .unwrap()
1909 .to_vec(),
1910 ))
1911 .into_route_iter()
1912 .collect::<Vec<_>>();
1913
1914 assert!(routes.is_empty());
1915 }
1916
1917 #[test]
1918 fn fallible_route_iterator_applies_filters_to_cached_routes() {
1919 let routes =
1920 BgpkitParser::from_reader(Cursor::new(update_record().encode().unwrap().to_vec()))
1921 .add_filter("type", "w")
1922 .unwrap()
1923 .into_fallible_route_iter()
1924 .collect::<Result<Vec<_>, _>>()
1925 .unwrap();
1926
1927 assert_eq!(routes.len(), 1);
1928 assert_eq!(routes[0].elem_type, ElemType::WITHDRAW);
1929 }
1930
1931 #[test]
1932 fn fallible_route_iterator_returns_route_parse_errors() {
1933 let bytes = table_dump_v2_rib_without_peer_table_record()
1934 .encode()
1935 .unwrap()
1936 .to_vec();
1937 let mut iter =
1938 BgpkitParser::from_reader(Cursor::new(bytes.clone())).into_fallible_route_iter();
1939
1940 let error = iter.next().unwrap().unwrap_err();
1941 assert_eq!(error.bytes.as_deref(), Some(bytes.as_slice()));
1942 }
1943
1944 #[test]
1945 fn fallible_route_iterator_yields_routes() {
1946 let bytes = update_record().encode().unwrap().to_vec();
1947 let routes = BgpkitParser::from_reader(Cursor::new(bytes))
1948 .into_fallible_route_iter()
1949 .collect::<Result<Vec<_>, _>>()
1950 .unwrap();
1951
1952 assert_eq!(routes.len(), 2);
1953 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1954 assert_eq!(routes[1].elem_type, ElemType::WITHDRAW);
1955 }
1956
1957 #[test]
1958 fn fallible_route_iterator_returns_parse_errors() {
1959 let invalid_data = vec![
1960 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, ];
1966
1967 let mut iter =
1968 BgpkitParser::from_reader(Cursor::new(invalid_data.clone())).into_fallible_route_iter();
1969
1970 let error = iter.next().unwrap().unwrap_err();
1971 assert_eq!(error.bytes.as_deref(), Some(&invalid_data[..12]));
1972 }
1973}