bgpkit-parser 0.21.0

MRT/BGP/BMP data processing library
Documentation
use bgpkit_parser::models::{Bgp4MpEnum, BgpMessage, BgpState, MrtMessage};
use bgpkit_parser::BgpkitParser;
use log::{Level, LevelFilter, Log, Metadata, Record};
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Mutex;

const FIXTURE: &str = "tests/fixtures/ripe/rrc01/2000.11/updates.20001104.0124.gz";
const ZEBRA_COMPAT_WARNING: &str = "recovered shortened Zebra BGP4MP records";

static WARNINGS: Mutex<Vec<String>> = Mutex::new(Vec::new());
static TEST_LOGGER: TestLogger = TestLogger;

struct TestLogger;

impl Log for TestLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Warn
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            WARNINGS.lock().unwrap().push(record.args().to_string());
        }
    }

    fn flush(&self) {}
}

#[test]
fn parses_shortened_zebra_bgp4mp_records_with_one_warning_per_parser() {
    log::set_logger(&TEST_LOGGER).unwrap();
    log::set_max_level(LevelFilter::Warn);

    let source = format!("{}/{FIXTURE}", env!("CARGO_MANIFEST_DIR"));
    let zero = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
    let mut records = 0usize;
    let mut recovered_state_changes = 0usize;
    let mut recovered_open_messages = 0usize;

    for result in BgpkitParser::new(&source)
        .unwrap()
        .into_fallible_record_iter()
    {
        records += 1;
        match result.unwrap().message {
            MrtMessage::Bgp4Mp(Bgp4MpEnum::StateChange(state_change))
                if state_change.peer_asn.to_u32() == 0
                    && state_change.local_asn.to_u32() == 0
                    && state_change.interface_index == 0
                    && state_change.peer_ip == zero
                    && state_change.local_addr == zero =>
            {
                assert!(matches!(
                    (state_change.old_state, state_change.new_state),
                    (BgpState::Active, BgpState::OpenSent) | (BgpState::OpenSent, BgpState::Idle)
                ));
                recovered_state_changes += 1;
            }
            MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(message))
                if message.peer_asn.to_u32() == 0
                    && message.local_asn.to_u32() == 0
                    && message.interface_index == 0
                    && message.peer_ip == zero
                    && message.local_ip == zero
                    && matches!(message.bgp_message, BgpMessage::Open(_)) =>
            {
                let BgpMessage::Open(open) = message.bgp_message else {
                    unreachable!()
                };
                assert_eq!(open.asn.to_u32(), 12_390);
                assert_eq!(open.hold_time, 180);
                assert_eq!(open.bgp_identifier, Ipv4Addr::new(212, 50, 161, 199));
                recovered_open_messages += 1;
            }
            _ => {}
        }
    }

    assert_eq!(records, 1_286);
    assert_eq!(recovered_state_changes, 42);
    assert_eq!(recovered_open_messages, 21);

    assert_eq!(
        WARNINGS
            .lock()
            .unwrap()
            .iter()
            .filter(|warning| warning.contains(ZEBRA_COMPAT_WARNING))
            .count(),
        1
    );

    let second_parser_records = BgpkitParser::new(&source)
        .unwrap()
        .into_fallible_record_iter()
        .map(Result::unwrap)
        .count();
    assert_eq!(second_parser_records, 1_286);
    assert_eq!(
        WARNINGS
            .lock()
            .unwrap()
            .iter()
            .filter(|warning| warning.contains(ZEBRA_COMPAT_WARNING))
            .count(),
        2
    );
}