use crate::parser::iters::{handle_record_parse_error, record_matches_filters};
use crate::parser::mrt::mrt_record::raw_record_uses_zebra_compat;
use crate::{chunk_mrt_record, BgpkitParser, Elementor, Filter, MrtRecord, RawMrtRecord};
use std::io::Read;
pub struct RawRecordIterator<R> {
parser: BgpkitParser<R>,
count: u64,
}
impl<R> RawRecordIterator<R> {
pub(crate) fn new(parser: BgpkitParser<R>) -> Self {
RawRecordIterator { parser, count: 0 }
}
}
impl<R: Read> Iterator for RawRecordIterator<R> {
type Item = RawMrtRecord;
fn next(&mut self) -> Option<RawMrtRecord> {
if self.parser.text_dump_iter.is_some() {
return None;
}
self.count += 1;
loop {
match chunk_mrt_record(&mut self.parser.reader) {
Ok(raw_record) => return Some(raw_record),
Err(e) => {
if !handle_record_parse_error(&mut self.parser, e.error, e.bytes) {
return None;
}
}
}
}
}
}
pub struct FilteredRawRecordIterator<R> {
inner: RawRecordIterator<R>,
elementor: Elementor,
filters: Vec<Filter>,
}
impl<R> FilteredRawRecordIterator<R> {
pub(crate) fn new(parser: BgpkitParser<R>) -> Self {
let filters = parser.filters.clone();
FilteredRawRecordIterator {
inner: RawRecordIterator::new(parser),
elementor: Elementor::new(),
filters,
}
}
fn warn_zebra_once(&mut self, raw: &RawMrtRecord) {
if raw_record_uses_zebra_compat(raw) {
self.inner.parser.warn_zebra_compat_once();
}
}
}
impl<R: Read> Iterator for FilteredRawRecordIterator<R> {
type Item = (RawMrtRecord, MrtRecord);
fn next(&mut self) -> Option<Self::Item> {
loop {
let raw = self.inner.next()?;
self.warn_zebra_once(&raw);
let record = match raw.clone().parse() {
Ok(record) => record,
Err(error) => {
if !handle_record_parse_error(
&mut self.inner.parser,
error,
Some(raw.raw_bytes().to_vec()),
) {
return None;
}
continue;
}
};
if self.filters.is_empty()
|| record_matches_filters(&record, &self.filters, &mut self.elementor)
{
return Some((raw, record));
}
}
}
}
#[cfg(test)]
mod filtered_tests {
use super::*;
use crate::models::*;
use std::io::Cursor;
fn bgp4mp_record(timestamp: u32, bgp_message: BgpMessage) -> Vec<u8> {
let body_of = |update: &BgpUpdateMessage| update.encode(AsnLength::Bits32).unwrap();
let body = match &bgp_message {
BgpMessage::Update(update) => body_of(update),
BgpMessage::KeepAlive => Vec::new().into(),
_ => unreachable!("test builds updates and keepalives only"),
};
let mut bgp = vec![0xFF; 16];
bgp.extend_from_slice(&((19 + body.len()) as u16).to_be_bytes());
bgp.push(bgp_message.msg_type() as u8);
bgp.extend_from_slice(&body);
let mut mrt_body = Vec::new();
mrt_body.extend_from_slice(&64496u32.to_be_bytes());
mrt_body.extend_from_slice(&64497u32.to_be_bytes());
mrt_body.extend_from_slice(&0u16.to_be_bytes());
mrt_body.extend_from_slice(&1u16.to_be_bytes());
mrt_body.extend_from_slice(&[192, 0, 2, 1]);
mrt_body.extend_from_slice(&[192, 0, 2, 2]);
mrt_body.extend_from_slice(&bgp);
let mut wire = Vec::new();
wire.extend_from_slice(×tamp.to_be_bytes());
wire.extend_from_slice(&(EntryType::BGP4MP as u16).to_be_bytes());
wire.extend_from_slice(&(Bgp4MpType::MessageAs4 as u16).to_be_bytes());
wire.extend_from_slice(&(mrt_body.len() as u32).to_be_bytes());
wire.extend_from_slice(&mrt_body);
wire
}
fn keepalive(timestamp: u32) -> Vec<u8> {
bgp4mp_record(timestamp, BgpMessage::KeepAlive)
}
fn update(timestamp: u32) -> Vec<u8> {
let mut attributes = Attributes::default();
attributes.add_attr(AttributeValue::Origin(Origin::IGP).into());
attributes.add_attr(AttributeValue::AsPath(AsPath::from_sequence([65000])).into());
attributes.add_attr(AttributeValue::NextHop("192.0.2.254".parse().unwrap()).into());
bgp4mp_record(
timestamp,
BgpMessage::Update(BgpUpdateMessage {
withdrawn_prefixes: vec![],
attributes,
announced_prefixes: vec!["198.51.100.0/24".parse().unwrap()],
}),
)
}
#[test]
fn no_filters_yield_original_bytes_exactly() {
let mut input = keepalive(1);
let upd = update(2);
input.extend_from_slice(&upd);
let parser = BgpkitParser::from_reader(Cursor::new(input.clone()));
let yielded: Vec<Vec<u8>> = parser
.into_filtered_raw_record_iter()
.map(|(raw, _)| raw.raw_bytes().to_vec())
.collect();
assert_eq!(yielded.len(), 2);
assert_eq!(yielded[0], input[..input.len() - upd.len()].to_vec());
assert_eq!(yielded[1], upd);
}
#[test]
fn filters_drop_no_elem_records_and_preserve_bytes() {
let keep = keepalive(1);
let upd = update(2);
let mut input = keep.clone();
input.extend_from_slice(&upd);
let parser = BgpkitParser::from_reader(Cursor::new(input))
.add_filter("prefix", "198.51.100.0/24")
.unwrap();
let yielded: Vec<Vec<u8>> = parser
.into_filtered_raw_record_iter()
.map(|(raw, _)| raw.raw_bytes().to_vec())
.collect();
assert_eq!(yielded.len(), 1);
assert_eq!(yielded[0], upd);
}
}