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(
895 AttributeValue::AsPath {
896 path: AsPath::from_sequence([64500, 64501]),
897 is_as4: false,
898 }
899 .into(),
900 );
901 attributes
902 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
903
904 MrtRecord {
905 common_header: CommonHeader {
906 timestamp: 1_700_000_000,
907 microsecond_timestamp: None,
908 entry_type: EntryType::BGP4MP,
909 entry_subtype: Bgp4MpType::MessageAs4 as u16,
910 length: 0,
911 },
912 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
913 msg_type: Bgp4MpType::MessageAs4,
914 peer_asn: Asn::new_32bit(64496),
915 local_asn: Asn::new_32bit(64497),
916 interface_index: 0,
917 peer_ip: IpAddr::from_str("192.0.2.1").unwrap(),
918 local_ip: IpAddr::from_str("192.0.2.2").unwrap(),
919 bgp_message: BgpMessage::Update(BgpUpdateMessage {
920 withdrawn_prefixes: vec![NetworkPrefix::from_str("198.51.100.0/24").unwrap()],
921 attributes,
922 announced_prefixes: vec![NetworkPrefix::from_str("203.0.113.0/24").unwrap()],
923 }),
924 })),
925 }
926 }
927
928 fn route_attributes(as_path: impl AsRef<[u32]>) -> Attributes {
929 let mut attributes = Attributes::default();
930 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
931 attributes.add_attr(
932 AttributeValue::AsPath {
933 path: AsPath::from_sequence(as_path),
934 is_as4: false,
935 }
936 .into(),
937 );
938 attributes
939 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
940 attributes
941 }
942
943 fn bgp4mp_record(msg_type: Bgp4MpType, bgp_message: BgpMessage) -> MrtRecord {
944 let asn = if matches!(
945 msg_type,
946 Bgp4MpType::Message
947 | Bgp4MpType::MessageLocal
948 | Bgp4MpType::MessageAddpath
949 | Bgp4MpType::MessageLocalAddpath
950 ) {
951 Asn::new_16bit(64496)
952 } else {
953 Asn::new_32bit(64496)
954 };
955
956 MrtRecord {
957 common_header: CommonHeader {
958 timestamp: 1_700_000_000,
959 microsecond_timestamp: None,
960 entry_type: EntryType::BGP4MP,
961 entry_subtype: msg_type as u16,
962 length: 0,
963 },
964 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
965 msg_type,
966 peer_asn: asn,
967 local_asn: Asn::new_32bit(64497),
968 interface_index: 0,
969 peer_ip: IpAddr::from_str("192.0.2.1").unwrap(),
970 local_ip: IpAddr::from_str("192.0.2.2").unwrap(),
971 bgp_message,
972 })),
973 }
974 }
975
976 fn open_message() -> BgpMessage {
977 BgpMessage::Open(BgpOpenMessage {
978 version: 4,
979 asn: Asn::new_16bit(64496),
980 hold_time: 180,
981 bgp_identifier: Ipv4Addr::new(192, 0, 2, 1),
982 extended_length: false,
983 opt_params: vec![],
984 })
985 }
986
987 fn raw_bgp_message(length: u16, msg_type: BgpMessageType, payload: &[u8]) -> Bytes {
988 raw_bgp_message_with_marker([0xff; 16], length, msg_type, payload)
989 }
990
991 fn raw_bgp_message_with_marker(
992 marker: [u8; 16],
993 length: u16,
994 msg_type: BgpMessageType,
995 payload: &[u8],
996 ) -> Bytes {
997 let mut bytes = BytesMut::new();
998 bytes.put_slice(&marker);
999 bytes.put_u16(length);
1000 bytes.put_u8(msg_type as u8);
1001 bytes.put_slice(payload);
1002 bytes.freeze()
1003 }
1004
1005 fn table_dump_record() -> MrtRecord {
1006 let mut attributes = Attributes::default();
1007 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
1008 attributes.add_attr(
1009 AttributeValue::AsPath {
1010 path: AsPath::from_sequence([64500, 64501]),
1011 is_as4: false,
1012 }
1013 .into(),
1014 );
1015 attributes
1016 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
1017
1018 MrtRecord {
1019 common_header: CommonHeader {
1020 timestamp: 1_700_000_000,
1021 microsecond_timestamp: None,
1022 entry_type: EntryType::TABLE_DUMP,
1023 entry_subtype: 1,
1024 length: 0,
1025 },
1026 message: MrtMessage::TableDumpMessage(TableDumpMessage {
1027 view_number: 0,
1028 sequence_number: 1,
1029 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1030 status: 1,
1031 originated_time: 1_699_999_998,
1032 peer_ip: IpAddr::from_str("192.0.2.20").unwrap(),
1033 peer_asn: Asn::new_16bit(64496),
1034 attributes,
1035 }),
1036 }
1037 }
1038
1039 fn table_dump_ipv6_record() -> MrtRecord {
1040 let mut attributes = Attributes::default();
1041 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
1042 attributes.add_attr(
1043 AttributeValue::AsPath {
1044 path: AsPath::from_sequence([64500, 64501]),
1045 is_as4: false,
1046 }
1047 .into(),
1048 );
1049
1050 MrtRecord {
1051 common_header: CommonHeader {
1052 timestamp: 1_700_000_000,
1053 microsecond_timestamp: None,
1054 entry_type: EntryType::TABLE_DUMP,
1055 entry_subtype: 2,
1056 length: 0,
1057 },
1058 message: MrtMessage::TableDumpMessage(TableDumpMessage {
1059 view_number: 0,
1060 sequence_number: 1,
1061 prefix: NetworkPrefix::from_str("2001:db8::/32").unwrap(),
1062 status: 1,
1063 originated_time: 1_699_999_998,
1064 peer_ip: IpAddr::from_str("2001:db8::20").unwrap(),
1065 peer_asn: Asn::new_16bit(64496),
1066 attributes,
1067 }),
1068 }
1069 }
1070
1071 fn table_dump_v2_records_bytes() -> Vec<u8> {
1072 let peer = Peer::new(
1073 "192.0.2.10".parse().unwrap(),
1074 "192.0.2.11".parse().unwrap(),
1075 Asn::new_32bit(64496),
1076 );
1077 let mut peer_table = PeerIndexTable::default();
1078 let peer_index = peer_table.add_peer(peer).unwrap();
1079
1080 let mut attributes = Attributes::default();
1081 attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
1082 attributes.add_attr(
1083 AttributeValue::AsPath {
1084 path: AsPath::from_sequence([64500, 64501]),
1085 is_as4: false,
1086 }
1087 .into(),
1088 );
1089 attributes
1090 .add_attr(AttributeValue::NextHop(IpAddr::from_str("192.0.2.254").unwrap()).into());
1091
1092 let pit_record = MrtRecord {
1093 common_header: CommonHeader {
1094 timestamp: 1_700_000_000,
1095 microsecond_timestamp: None,
1096 entry_type: EntryType::TABLE_DUMP_V2,
1097 entry_subtype: TableDumpV2Type::PeerIndexTable as u16,
1098 length: 0,
1099 },
1100 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(peer_table)),
1101 };
1102 let rib_record = MrtRecord {
1103 common_header: CommonHeader {
1104 timestamp: 1_700_000_001,
1105 microsecond_timestamp: None,
1106 entry_type: EntryType::TABLE_DUMP_V2,
1107 entry_subtype: TableDumpV2Type::RibIpv4Unicast as u16,
1108 length: 0,
1109 },
1110 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(RibAfiEntries {
1111 rib_type: TableDumpV2Type::RibIpv4Unicast,
1112 sequence_number: 1,
1113 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1114 rib_entries: vec![RibEntry {
1115 peer_index,
1116 originated_time: 1_699_999_999,
1117 path_id: None,
1118 attributes,
1119 }],
1120 })),
1121 };
1122
1123 let mut bytes = pit_record.encode().unwrap().to_vec();
1124 bytes.extend_from_slice(&rib_record.encode().unwrap());
1125 bytes
1126 }
1127
1128 fn table_dump_v2_truncated_attribute_payload() -> (Vec<u8>, Bytes, PeerIndexTable) {
1129 let peer = Peer::new(
1130 "192.0.2.10".parse().unwrap(),
1131 "192.0.2.11".parse().unwrap(),
1132 Asn::new_32bit(64496),
1133 );
1134 let mut peer_table = PeerIndexTable::default();
1135 let peer_index = peer_table.add_peer(peer).unwrap();
1136
1137 let pit_record = MrtRecord {
1138 common_header: CommonHeader {
1139 timestamp: 1_700_000_000,
1140 microsecond_timestamp: None,
1141 entry_type: EntryType::TABLE_DUMP_V2,
1142 entry_subtype: TableDumpV2Type::PeerIndexTable as u16,
1143 length: 0,
1144 },
1145 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(
1146 peer_table.clone(),
1147 )),
1148 };
1149
1150 let first_entry = RibEntry {
1151 peer_index,
1152 originated_time: 1_699_999_999,
1153 path_id: None,
1154 attributes: route_attributes([64500, 64501]),
1155 };
1156
1157 let mut rib_body = BytesMut::new();
1158 rib_body.put_u32(1);
1159 rib_body.extend(NetworkPrefix::from_str("203.0.113.0/24").unwrap().encode());
1160 rib_body.put_u16(2);
1161 rib_body.extend(first_entry.encode().unwrap());
1162 rib_body.put_u16(peer_index);
1163 rib_body.put_u32(1_699_999_998);
1164 rib_body.put_u16(32);
1165 rib_body.put_u8(0);
1166
1167 let rib_body = rib_body.freeze();
1168 let rib_header = CommonHeader {
1169 timestamp: 1_700_000_001,
1170 microsecond_timestamp: None,
1171 entry_type: EntryType::TABLE_DUMP_V2,
1172 entry_subtype: TableDumpV2Type::RibIpv4Unicast as u16,
1173 length: rib_body.len() as u32,
1174 };
1175
1176 let mut bytes = pit_record.encode().unwrap().to_vec();
1177 bytes.extend_from_slice(&rib_header.encode());
1178 bytes.extend_from_slice(&rib_body);
1179
1180 (bytes, rib_body, peer_table)
1181 }
1182
1183 fn assert_filtered_route_projection(bytes: Vec<u8>, filters: &[(&str, &str)]) {
1184 let elem_parser = filters.iter().fold(
1185 BgpkitParser::from_reader(Cursor::new(bytes.clone())),
1186 |parser, (filter_type, filter_value)| {
1187 parser.add_filter(filter_type, filter_value).unwrap()
1188 },
1189 );
1190 let route_parser = filters.iter().fold(
1191 BgpkitParser::from_reader(Cursor::new(bytes)),
1192 |parser, (filter_type, filter_value)| {
1193 parser.add_filter(filter_type, filter_value).unwrap()
1194 },
1195 );
1196
1197 let elem_projection = elem_parser
1198 .into_elem_iter()
1199 .map(route_projection)
1200 .collect::<Vec<_>>();
1201 let routes = route_parser.into_route_iter().collect::<Vec<_>>();
1202
1203 assert_eq!(routes, elem_projection, "filters: {filters:?}");
1204 }
1205
1206 fn assert_route_projection(bytes: Vec<u8>) -> Vec<BgpRouteElem> {
1207 let elem_projection = BgpkitParser::from_reader(Cursor::new(bytes.clone()))
1208 .into_elem_iter()
1209 .map(route_projection)
1210 .collect::<Vec<_>>();
1211 let routes = BgpkitParser::from_reader(Cursor::new(bytes))
1212 .into_route_iter()
1213 .collect::<Vec<_>>();
1214
1215 assert_eq!(routes, elem_projection);
1216 routes
1217 }
1218
1219 #[test]
1220 fn bgp4mp_routes_rejects_link_state_envelope_afi() {
1221 let mut data = BytesMut::new();
1222 data.put_u16(65000);
1223 data.put_u16(65001);
1224 data.put_u16(0);
1225 data.put_u16(Afi::LinkState as u16);
1226 data.put_slice(&BgpMessage::KeepAlive.encode(AsnLength::Bits16).unwrap());
1227
1228 let error =
1229 match parse_bgp4mp_routes(Bgp4MpType::Message as u16, data.freeze(), 1_700_000_000.0) {
1230 Err(error) => error,
1231 Ok(_) => panic!("unexpectedly parsed BGP4MP routes"),
1232 };
1233 assert!(matches!(
1234 error,
1235 ParserError::ParseError(message)
1236 if message == "Link-State AFI is invalid in a BGP4MP envelope"
1237 ));
1238 }
1239
1240 #[test]
1241 fn route_iterator_matches_elem_projection_for_update() {
1242 let bytes = update_record().encode().unwrap().to_vec();
1243 let routes = assert_route_projection(bytes);
1244 assert_eq!(routes.len(), 2);
1245 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1246 assert_eq!(routes[1].elem_type, ElemType::WITHDRAW);
1247 assert!(routes[1].as_path.is_none());
1248 }
1249
1250 #[test]
1251 fn route_iterator_shares_as_path_for_update_announcements() {
1252 let bytes = bgp4mp_record(
1253 Bgp4MpType::MessageAs4,
1254 BgpMessage::Update(BgpUpdateMessage {
1255 withdrawn_prefixes: vec![],
1256 attributes: route_attributes([64500, 64501]),
1257 announced_prefixes: vec![
1258 NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1259 NetworkPrefix::from_str("198.51.100.0/24").unwrap(),
1260 ],
1261 }),
1262 )
1263 .encode()
1264 .unwrap()
1265 .to_vec();
1266
1267 let routes = BgpkitParser::from_reader(Cursor::new(bytes))
1268 .into_route_iter()
1269 .collect::<Vec<_>>();
1270
1271 assert_eq!(routes.len(), 2);
1272 assert!(Arc::ptr_eq(
1273 routes[0].as_path.as_ref().unwrap(),
1274 routes[1].as_path.as_ref().unwrap()
1275 ));
1276 }
1277
1278 #[test]
1279 fn route_iterator_uses_microsecond_timestamps() {
1280 let timestamp = record_timestamp(&CommonHeader {
1281 timestamp: 1_700_000_000,
1282 microsecond_timestamp: Some(123_456),
1283 entry_type: EntryType::BGP4MP_ET,
1284 entry_subtype: Bgp4MpType::MessageAs4 as u16,
1285 length: 0,
1286 });
1287
1288 assert_eq!(timestamp, 1_700_000_000.123_456);
1289 }
1290
1291 #[test]
1292 fn route_iterator_matches_elem_projection_for_mp_update() {
1293 let mut attributes = route_attributes([64500, 64501]);
1294 attributes.add_attr(
1295 AttributeValue::MpReachNlri(Nlri::new_reachable(
1296 NetworkPrefix::from_str("2001:db8::/32").unwrap(),
1297 Some(IpAddr::from_str("2001:db8::1").unwrap()),
1298 ))
1299 .into(),
1300 );
1301 attributes.add_attr(
1302 AttributeValue::MpUnreachNlri(Nlri::new_unreachable(
1303 NetworkPrefix::from_str("2001:db8:1::/48").unwrap(),
1304 ))
1305 .into(),
1306 );
1307
1308 let bytes = bgp4mp_record(
1309 Bgp4MpType::MessageAs4,
1310 BgpMessage::Update(BgpUpdateMessage {
1311 withdrawn_prefixes: vec![],
1312 attributes,
1313 announced_prefixes: vec![],
1314 }),
1315 )
1316 .encode()
1317 .unwrap()
1318 .to_vec();
1319
1320 let routes = assert_route_projection(bytes);
1321 assert_eq!(routes.len(), 2);
1322 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1323 assert_eq!(
1324 routes[0].prefix,
1325 NetworkPrefix::from_str("2001:db8::/32").unwrap()
1326 );
1327 assert_eq!(routes[1].elem_type, ElemType::WITHDRAW);
1328 assert_eq!(
1329 routes[1].prefix,
1330 NetworkPrefix::from_str("2001:db8:1::/48").unwrap()
1331 );
1332 }
1333
1334 #[test]
1335 fn route_iterator_matches_elem_projection_for_non_update_bgp4mp_messages() {
1336 let records = [
1337 bgp4mp_record(Bgp4MpType::Message, open_message()),
1338 bgp4mp_record(
1339 Bgp4MpType::MessageAs4,
1340 BgpMessage::Notification(BgpNotificationMessage {
1341 error: BgpError::Unknown(1, 0),
1342 data: vec![],
1343 }),
1344 ),
1345 bgp4mp_record(Bgp4MpType::MessageAddpath, BgpMessage::KeepAlive),
1346 bgp4mp_record(Bgp4MpType::MessageAs4Addpath, BgpMessage::KeepAlive),
1347 ];
1348 let mut bytes = Vec::new();
1349 for record in records {
1350 bytes.extend_from_slice(&record.encode().unwrap());
1351 }
1352
1353 assert!(assert_route_projection(bytes).is_empty());
1354 }
1355
1356 #[test]
1357 fn route_iterator_matches_elem_projection_for_bgp4mp_16bit_update() {
1358 let bytes = bgp4mp_record(
1359 Bgp4MpType::Message,
1360 BgpMessage::Update(BgpUpdateMessage {
1361 withdrawn_prefixes: vec![],
1362 attributes: route_attributes([64500, 64501]),
1363 announced_prefixes: vec![NetworkPrefix::from_str("203.0.113.0/24").unwrap()],
1364 }),
1365 )
1366 .encode()
1367 .unwrap()
1368 .to_vec();
1369
1370 let routes = assert_route_projection(bytes);
1371 assert_eq!(routes.len(), 1);
1372 assert_eq!(routes[0].peer_asn, Asn::new_16bit(64496));
1373 }
1374
1375 #[test]
1376 fn route_iterator_filters_match_elem_projection_for_update() {
1377 let bytes = update_record().encode().unwrap().to_vec();
1378 let cases: &[&[(&str, &str)]] = &[
1379 &[("peer_ip", "192.0.2.1")],
1380 &[("peer_ip", "192.0.2.99")],
1381 &[("peer_asn", "64496")],
1382 &[("type", "a")],
1383 &[("type", "w")],
1384 &[("type", "!w")],
1385 &[("prefix", "203.0.113.0/24")],
1386 &[("prefix", "198.51.100.0/24")],
1387 &[("prefix_super", "203.0.113.128/25")],
1388 &[("origin_asn", "64501")],
1389 &[("origin_asns", "64496,64501")],
1390 &[("as_path", "64500 64501$")],
1391 &[("ip_version", "4")],
1392 &[("ts_start", "1700000000"), ("ts_end", "1700000000")],
1393 &[("peer_ip", "192.0.2.1"), ("type", "a")],
1394 ];
1395
1396 for filters in cases {
1397 assert_filtered_route_projection(bytes.clone(), filters);
1398 }
1399 }
1400
1401 #[test]
1402 fn selective_attribute_parser_merges_as4_path() {
1403 let mut attributes = Attributes::default();
1404 attributes.add_attr(
1405 AttributeValue::AsPath {
1406 path: AsPath::from_sequence([23456, 64497]),
1407 is_as4: false,
1408 }
1409 .into(),
1410 );
1411 attributes.add_attr(
1412 AttributeValue::AsPath {
1413 path: AsPath::from_sequence([65536, 64497]),
1414 is_as4: true,
1415 }
1416 .into(),
1417 );
1418
1419 let attrs = parse_route_attributes(
1420 attributes.encode(AsnLength::Bits16).unwrap(),
1421 &AsnLength::Bits16,
1422 false,
1423 RouteAttributeContext {
1424 afi: None,
1425 safi: None,
1426 prefixes: None,
1427 is_announcement: Some(true),
1428 has_standard_nlri: true,
1429 },
1430 )
1431 .unwrap();
1432
1433 assert_eq!(
1434 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1435 vec![65536, 64497]
1436 );
1437 }
1438
1439 #[test]
1440 fn selective_attribute_parser_handles_as_path_without_as4_path() {
1441 let attrs = parse_route_attributes(
1442 route_attributes([64500, 64501])
1443 .encode(AsnLength::Bits16)
1444 .unwrap(),
1445 &AsnLength::Bits16,
1446 false,
1447 RouteAttributeContext {
1448 afi: None,
1449 safi: None,
1450 prefixes: None,
1451 is_announcement: Some(true),
1452 has_standard_nlri: true,
1453 },
1454 )
1455 .unwrap();
1456
1457 assert_eq!(
1458 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1459 vec![64500, 64501]
1460 );
1461 }
1462
1463 #[test]
1464 fn selective_attribute_parser_handles_as4_path_without_as_path() {
1465 let mut attributes = Attributes::default();
1466 attributes.add_attr(
1467 AttributeValue::AsPath {
1468 path: AsPath::from_sequence([65536, 64497]),
1469 is_as4: true,
1470 }
1471 .into(),
1472 );
1473
1474 let attrs = parse_route_attributes(
1475 attributes.encode(AsnLength::Bits16).unwrap(),
1476 &AsnLength::Bits16,
1477 false,
1478 RouteAttributeContext {
1479 afi: None,
1480 safi: None,
1481 prefixes: None,
1482 is_announcement: Some(false),
1483 has_standard_nlri: false,
1484 },
1485 )
1486 .unwrap();
1487
1488 assert_eq!(
1489 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1490 vec![65536, 64497]
1491 );
1492 }
1493
1494 #[test]
1495 fn selective_attribute_parser_handles_no_as_path() {
1496 let attrs = parse_route_attributes(
1497 Bytes::new(),
1498 &AsnLength::Bits16,
1499 false,
1500 RouteAttributeContext {
1501 afi: None,
1502 safi: None,
1503 prefixes: None,
1504 is_announcement: Some(false),
1505 has_standard_nlri: false,
1506 },
1507 )
1508 .unwrap();
1509
1510 assert!(attrs.as_path.is_none());
1511 }
1512
1513 #[test]
1514 fn selective_attribute_parser_handles_extended_and_truncated_attributes() {
1515 let mut extended_as_path = BytesMut::new();
1516 extended_as_path.put_u8((AttrFlags::TRANSITIVE | AttrFlags::EXTENDED).bits());
1517 extended_as_path.put_u8(u8::from(AttrType::AS_PATH));
1518 extended_as_path.put_u16(4);
1519 extended_as_path.put_u8(2);
1520 extended_as_path.put_u8(1);
1521 extended_as_path.put_u16(64500);
1522
1523 let attrs = parse_route_attributes(
1524 extended_as_path.freeze(),
1525 &AsnLength::Bits16,
1526 false,
1527 RouteAttributeContext {
1528 afi: None,
1529 safi: None,
1530 prefixes: None,
1531 is_announcement: Some(false),
1532 has_standard_nlri: false,
1533 },
1534 )
1535 .unwrap();
1536 assert_eq!(
1537 attrs.as_path.unwrap().to_u32_vec_opt(false).unwrap(),
1538 vec![64500]
1539 );
1540
1541 let attrs = parse_route_attributes(
1542 Bytes::from_static(&[0x40, 2, 5, 0]),
1543 &AsnLength::Bits16,
1544 false,
1545 RouteAttributeContext {
1546 afi: None,
1547 safi: None,
1548 prefixes: None,
1549 is_announcement: Some(false),
1550 has_standard_nlri: false,
1551 },
1552 )
1553 .unwrap();
1554 assert!(attrs.as_path.is_none());
1555 }
1556
1557 #[test]
1558 fn selective_attribute_parser_discards_malformed_as_path() {
1559 let attrs = parse_route_attributes(
1560 Bytes::from_static(&[0x40, 2, 1, 0]),
1561 &AsnLength::Bits16,
1562 false,
1563 RouteAttributeContext {
1564 afi: None,
1565 safi: None,
1566 prefixes: None,
1567 is_announcement: Some(false),
1568 has_standard_nlri: false,
1569 },
1570 )
1571 .unwrap();
1572
1573 assert!(attrs.as_path.is_none());
1574 }
1575
1576 #[test]
1577 fn route_iterator_matches_elem_projection_for_table_dump() {
1578 let bytes = table_dump_record().encode().unwrap().to_vec();
1579 let routes = assert_route_projection(bytes);
1580 assert_eq!(routes.len(), 1);
1581 assert_eq!(routes[0].timestamp, 1_699_999_998.0);
1582 assert_eq!(routes[0].peer_asn, Asn::new_16bit(64496));
1583 }
1584
1585 #[test]
1586 fn route_iterator_matches_elem_projection_for_table_dump_ipv6() {
1587 let bytes = table_dump_ipv6_record().encode().unwrap().to_vec();
1588 let routes = assert_route_projection(bytes);
1589 assert_eq!(routes.len(), 1);
1590 assert_eq!(
1591 routes[0].prefix,
1592 NetworkPrefix::from_str("2001:db8::/32").unwrap()
1593 );
1594 assert_eq!(
1595 routes[0].peer_ip,
1596 IpAddr::from(Ipv6Addr::from_str("2001:db8::20").unwrap())
1597 );
1598 }
1599
1600 #[test]
1601 fn route_iterator_matches_elem_projection_for_table_dump_v2() {
1602 let bytes = table_dump_v2_records_bytes();
1603 let routes = assert_route_projection(bytes);
1604 assert_eq!(routes.len(), 1);
1605 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
1606 assert_eq!(
1607 routes[0].as_path.as_ref().unwrap().to_u32_vec_opt(false),
1608 Some(vec![64500, 64501])
1609 );
1610 }
1611
1612 #[test]
1613 fn route_iterator_matches_elem_projection_for_table_dump_v2_ipv6_addpath() {
1614 let peer = Peer::new(
1615 "192.0.2.11".parse().unwrap(),
1616 "2001:db8::10".parse().unwrap(),
1617 Asn::new_32bit(64496),
1618 );
1619 let mut peer_table = PeerIndexTable::default();
1620 let peer_index = peer_table.add_peer(peer).unwrap();
1621
1622 let pit_record = MrtRecord {
1623 common_header: CommonHeader {
1624 timestamp: 1_700_000_000,
1625 microsecond_timestamp: None,
1626 entry_type: EntryType::TABLE_DUMP_V2,
1627 entry_subtype: TableDumpV2Type::PeerIndexTable as u16,
1628 length: 0,
1629 },
1630 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::PeerIndexTable(peer_table)),
1631 };
1632 let rib_record = MrtRecord {
1633 common_header: CommonHeader {
1634 timestamp: 1_700_000_001,
1635 microsecond_timestamp: None,
1636 entry_type: EntryType::TABLE_DUMP_V2,
1637 entry_subtype: TableDumpV2Type::RibIpv6UnicastAddPath as u16,
1638 length: 0,
1639 },
1640 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(RibAfiEntries {
1641 rib_type: TableDumpV2Type::RibIpv6UnicastAddPath,
1642 sequence_number: 1,
1643 prefix: NetworkPrefix::from_str("2001:db8::/32").unwrap(),
1644 rib_entries: vec![RibEntry {
1645 peer_index,
1646 originated_time: 1_699_999_999,
1647 path_id: Some(1234),
1648 attributes: route_attributes([64500, 64501]),
1649 }],
1650 })),
1651 };
1652
1653 let mut bytes = pit_record.encode().unwrap().to_vec();
1654 bytes.extend_from_slice(&rib_record.encode().unwrap());
1655 let routes = assert_route_projection(bytes);
1656 assert_eq!(routes.len(), 1);
1657 assert_eq!(
1658 routes[0].prefix,
1659 NetworkPrefix::from_str("2001:db8::/32").unwrap()
1660 );
1661 }
1662
1663 #[test]
1664 fn route_iterator_matches_elem_projection_for_bgp4mp_ipv6_peer_update() {
1665 let record = MrtRecord {
1666 common_header: CommonHeader {
1667 timestamp: 1_700_000_000,
1668 microsecond_timestamp: None,
1669 entry_type: EntryType::BGP4MP,
1670 entry_subtype: Bgp4MpType::MessageAs4 as u16,
1671 length: 0,
1672 },
1673 message: MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(Bgp4MpMessage {
1674 msg_type: Bgp4MpType::MessageAs4,
1675 peer_asn: Asn::new_32bit(64496),
1676 local_asn: Asn::new_32bit(64497),
1677 interface_index: 0,
1678 peer_ip: IpAddr::from_str("2001:db8::1").unwrap(),
1679 local_ip: IpAddr::from_str("2001:db8::2").unwrap(),
1680 bgp_message: BgpMessage::Update(BgpUpdateMessage {
1681 withdrawn_prefixes: vec![],
1682 attributes: route_attributes([64500, 64501]),
1683 announced_prefixes: vec![NetworkPrefix::from_str("203.0.113.0/24").unwrap()],
1684 }),
1685 })),
1686 };
1687
1688 let routes = assert_route_projection(record.encode().unwrap().to_vec());
1689 assert_eq!(routes.len(), 1);
1690 assert_eq!(
1691 routes[0].peer_ip,
1692 IpAddr::from(Ipv6Addr::from_str("2001:db8::1").unwrap())
1693 );
1694 }
1695
1696 #[test]
1697 fn route_iterator_filters_match_elem_projection_for_table_dump_v2() {
1698 let bytes = table_dump_v2_records_bytes();
1699 let cases: &[&[(&str, &str)]] = &[
1700 &[("peer_ip", "192.0.2.10")],
1701 &[("peer_asn", "64496")],
1702 &[("type", "a")],
1703 &[("type", "w")],
1704 &[("prefix", "203.0.113.0/24")],
1705 &[("prefix_sub", "203.0.112.0/23")],
1706 &[("origin_asn", "64501")],
1707 &[("as_path", "64500 64501$")],
1708 &[("ts_start", "1699999999"), ("ts_end", "1699999999")],
1709 &[("peer_asn", "64496"), ("origin_asn", "64501")],
1710 ];
1711
1712 for filters in cases {
1713 assert_filtered_route_projection(bytes.clone(), filters);
1714 }
1715 }
1716
1717 #[test]
1718 fn route_parser_reports_bgp_message_shape_errors() {
1719 assert!(parse_bgp_message_routes(
1720 raw_bgp_message(18, 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 .is_err());
1728 assert!(parse_bgp_message_routes(
1729 raw_bgp_message(4097, BgpMessageType::OPEN, &[]),
1730 false,
1731 &AsnLength::Bits16,
1732 1_700_000_000.0,
1733 "192.0.2.1".parse().unwrap(),
1734 Asn::new_16bit(64496)
1735 )
1736 .is_err());
1737
1738 let routes = collect_route_record_iter(
1739 parse_bgp_message_routes(
1740 raw_bgp_message(30, BgpMessageType::KEEPALIVE, &[]),
1741 false,
1742 &AsnLength::Bits16,
1743 1_700_000_000.0,
1744 "192.0.2.1".parse().unwrap(),
1745 Asn::new_16bit(64496),
1746 )
1747 .unwrap(),
1748 )
1749 .unwrap();
1750 assert!(routes.is_empty());
1751
1752 let routes = collect_route_record_iter(
1753 parse_bgp_message_routes(
1754 raw_bgp_message(19, BgpMessageType::KEEPALIVE, &[0]),
1755 false,
1756 &AsnLength::Bits16,
1757 1_700_000_000.0,
1758 "192.0.2.1".parse().unwrap(),
1759 Asn::new_16bit(64496),
1760 )
1761 .unwrap(),
1762 )
1763 .unwrap();
1764 assert!(routes.is_empty());
1765
1766 let routes = collect_route_record_iter(
1767 parse_bgp_message_routes(
1768 raw_bgp_message_with_marker([0x00; 16], 19, BgpMessageType::KEEPALIVE, &[]),
1769 false,
1770 &AsnLength::Bits16,
1771 1_700_000_000.0,
1772 "192.0.2.1".parse().unwrap(),
1773 Asn::new_16bit(64496),
1774 )
1775 .unwrap(),
1776 )
1777 .unwrap();
1778 assert!(routes.is_empty());
1779 }
1780
1781 #[test]
1782 fn route_core_dump_write_respects_enabled_flag() {
1783 let dir = tempfile::tempdir().unwrap();
1784 let path = dir.path().join("mrt_core_dump");
1785
1786 write_mrt_core_dump_to_path(false, Some(vec![1, 2, 3]), &path);
1787 assert!(!path.exists());
1788
1789 write_mrt_core_dump_to_path(true, Some(vec![1, 2, 3]), &path);
1790 assert_eq!(std::fs::read(&path).unwrap(), vec![1, 2, 3]);
1791 }
1792
1793 #[test]
1794 fn route_parser_handles_table_dump_v2_error_edges() {
1795 let rib = RibAfiEntries {
1796 rib_type: TableDumpV2Type::RibIpv4Unicast,
1797 sequence_number: 1,
1798 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1799 rib_entries: vec![RibEntry {
1800 peer_index: 99,
1801 originated_time: 1_699_999_999,
1802 path_id: None,
1803 attributes: route_attributes([64500, 64501]),
1804 }],
1805 };
1806 let mut no_peer_table = None;
1807 assert!(parse_table_dump_v2_routes(
1808 TableDumpV2Type::RibIpv4Unicast as u16,
1809 rib.encode().unwrap(),
1810 &mut no_peer_table,
1811 )
1812 .is_err());
1813
1814 let mut empty_peer_table = Some(RoutePeerTable::default());
1815 let routes = collect_route_record_iter(
1816 parse_table_dump_v2_routes(
1817 TableDumpV2Type::RibIpv4Unicast as u16,
1818 rib.encode().unwrap(),
1819 &mut empty_peer_table,
1820 )
1821 .unwrap(),
1822 )
1823 .unwrap();
1824 assert!(routes.is_empty());
1825
1826 let mut truncated = BytesMut::new();
1827 truncated.put_u32(1);
1828 truncated.extend(NetworkPrefix::from_str("203.0.113.0/24").unwrap().encode());
1829 truncated.put_u16(1);
1830 let mut empty_peer_table = Some(RoutePeerTable::default());
1831 let routes = collect_route_record_iter(
1832 parse_table_dump_v2_routes(
1833 TableDumpV2Type::RibIpv4Unicast as u16,
1834 truncated.freeze(),
1835 &mut empty_peer_table,
1836 )
1837 .unwrap(),
1838 )
1839 .unwrap();
1840 assert!(routes.is_empty());
1841
1842 let peer = Peer::new(
1843 "192.0.2.10".parse().unwrap(),
1844 "192.0.2.11".parse().unwrap(),
1845 Asn::new_32bit(64496),
1846 );
1847 let mut peer_table = PeerIndexTable::default();
1848 let peer_index = peer_table.add_peer(peer).unwrap();
1849
1850 let first_entry = RibEntry {
1851 peer_index,
1852 originated_time: 1_699_999_999,
1853 path_id: Some(1234),
1854 attributes: route_attributes([64500, 64501]),
1855 };
1856 let mut add_path_truncated = BytesMut::new();
1857 add_path_truncated.put_u32(1);
1858 add_path_truncated.extend(NetworkPrefix::from_str("203.0.113.0/24").unwrap().encode());
1859 add_path_truncated.put_u16(2);
1860 add_path_truncated.extend(first_entry.encode().unwrap());
1861 add_path_truncated.put_u16(peer_index);
1862 add_path_truncated.put_u32(1_699_999_998);
1863 add_path_truncated.put_u32(5678);
1864
1865 let mut peer_table = Some(route_peer_table_from_peer_index(peer_table));
1866 let routes = collect_route_record_iter(
1867 parse_table_dump_v2_routes(
1868 TableDumpV2Type::RibIpv4UnicastAddPath as u16,
1869 add_path_truncated.freeze(),
1870 &mut peer_table,
1871 )
1872 .unwrap(),
1873 )
1874 .unwrap();
1875 assert_eq!(routes.len(), 1);
1876 assert_eq!(
1877 routes[0].prefix,
1878 NetworkPrefix::from_str("203.0.113.0/24").unwrap()
1879 );
1880 }
1881
1882 #[test]
1883 fn route_parser_preserves_table_dump_v2_routes_before_truncated_attribute_payload() {
1884 let (_bytes, rib_body, peer_table) = table_dump_v2_truncated_attribute_payload();
1885 let mut peer_table = Some(route_peer_table_from_peer_index(peer_table));
1886
1887 let routes = collect_route_record_iter(
1888 parse_table_dump_v2_routes(
1889 TableDumpV2Type::RibIpv4Unicast as u16,
1890 rib_body,
1891 &mut peer_table,
1892 )
1893 .unwrap(),
1894 )
1895 .unwrap();
1896
1897 assert_eq!(routes.len(), 1);
1898 assert_eq!(
1899 routes[0].prefix,
1900 NetworkPrefix::from_str("203.0.113.0/24").unwrap()
1901 );
1902 assert_eq!(
1903 routes[0].as_path.as_ref().unwrap().to_u32_vec_opt(false),
1904 Some(vec![64500, 64501])
1905 );
1906 }
1907
1908 #[test]
1909 fn route_iterators_preserve_table_dump_v2_routes_before_truncated_attribute_payload() {
1910 let (bytes, _rib_body, _peer_table) = table_dump_v2_truncated_attribute_payload();
1911
1912 let routes = BgpkitParser::from_reader(Cursor::new(bytes.clone()))
1913 .into_route_iter()
1914 .collect::<Vec<_>>();
1915 assert_eq!(routes.len(), 1);
1916 assert_eq!(
1917 routes[0].prefix,
1918 NetworkPrefix::from_str("203.0.113.0/24").unwrap()
1919 );
1920
1921 let fallible_routes = BgpkitParser::from_reader(Cursor::new(bytes))
1922 .into_fallible_route_iter()
1923 .collect::<Result<Vec<_>, _>>()
1924 .unwrap();
1925 assert_eq!(fallible_routes, routes);
1926 }
1927
1928 fn table_dump_v2_rib_without_peer_table_record() -> MrtRecord {
1929 MrtRecord {
1930 common_header: CommonHeader {
1931 timestamp: 1_700_000_001,
1932 microsecond_timestamp: None,
1933 entry_type: EntryType::TABLE_DUMP_V2,
1934 entry_subtype: TableDumpV2Type::RibIpv4Unicast as u16,
1935 length: 0,
1936 },
1937 message: MrtMessage::TableDumpV2Message(TableDumpV2Message::RibAfi(RibAfiEntries {
1938 rib_type: TableDumpV2Type::RibIpv4Unicast,
1939 sequence_number: 1,
1940 prefix: NetworkPrefix::from_str("203.0.113.0/24").unwrap(),
1941 rib_entries: vec![RibEntry {
1942 peer_index: 0,
1943 originated_time: 1_699_999_999,
1944 path_id: None,
1945 attributes: route_attributes([64500, 64501]),
1946 }],
1947 })),
1948 }
1949 }
1950
1951 #[test]
1952 fn route_iterator_skips_route_parse_errors() {
1953 let routes = BgpkitParser::from_reader(Cursor::new(
1954 table_dump_v2_rib_without_peer_table_record()
1955 .encode()
1956 .unwrap()
1957 .to_vec(),
1958 ))
1959 .into_route_iter()
1960 .collect::<Vec<_>>();
1961
1962 assert!(routes.is_empty());
1963 }
1964
1965 #[test]
1966 fn fallible_route_iterator_applies_filters_to_cached_routes() {
1967 let routes =
1968 BgpkitParser::from_reader(Cursor::new(update_record().encode().unwrap().to_vec()))
1969 .add_filter("type", "w")
1970 .unwrap()
1971 .into_fallible_route_iter()
1972 .collect::<Result<Vec<_>, _>>()
1973 .unwrap();
1974
1975 assert_eq!(routes.len(), 1);
1976 assert_eq!(routes[0].elem_type, ElemType::WITHDRAW);
1977 }
1978
1979 #[test]
1980 fn fallible_route_iterator_returns_route_parse_errors() {
1981 let bytes = table_dump_v2_rib_without_peer_table_record()
1982 .encode()
1983 .unwrap()
1984 .to_vec();
1985 let mut iter =
1986 BgpkitParser::from_reader(Cursor::new(bytes.clone())).into_fallible_route_iter();
1987
1988 let error = iter.next().unwrap().unwrap_err();
1989 assert_eq!(error.bytes.as_deref(), Some(bytes.as_slice()));
1990 }
1991
1992 #[test]
1993 fn fallible_route_iterator_yields_routes() {
1994 let bytes = update_record().encode().unwrap().to_vec();
1995 let routes = BgpkitParser::from_reader(Cursor::new(bytes))
1996 .into_fallible_route_iter()
1997 .collect::<Result<Vec<_>, _>>()
1998 .unwrap();
1999
2000 assert_eq!(routes.len(), 2);
2001 assert_eq!(routes[0].elem_type, ElemType::ANNOUNCE);
2002 assert_eq!(routes[1].elem_type, ElemType::WITHDRAW);
2003 }
2004
2005 #[test]
2006 fn fallible_route_iterator_returns_parse_errors() {
2007 let invalid_data = vec![
2008 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, ];
2014
2015 let mut iter =
2016 BgpkitParser::from_reader(Cursor::new(invalid_data.clone())).into_fallible_route_iter();
2017
2018 let error = iter.next().unwrap().unwrap_err();
2019 assert_eq!(error.bytes.as_deref(), Some(&invalid_data[..12]));
2020 }
2021}