use std::collections::BTreeMap;
use bgpkit_parser::models::{AttributeValue, Bgp4MpEnum, Bgp4MpMessage, BgpMessage, MrtMessage};
use bgpkit_parser::BgpkitParser;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
if args.is_empty() {
eprintln!("usage: safi_scan <mrt-url-or-path>...");
std::process::exit(2);
}
let mut safi_counts: BTreeMap<(u16, u8), u64> = BTreeMap::new();
let mut attr_counts: BTreeMap<String, u64> = BTreeMap::new();
let mut records = 0u64;
let mut updates = 0u64;
for input in &args {
let parser = match BgpkitParser::new(input) {
Ok(p) => p,
Err(e) => {
eprintln!("skip {input}: {e}");
continue;
}
};
for record in parser.into_record_iter() {
records += 1;
let MrtMessage::Bgp4Mp(Bgp4MpEnum::Message(m)) = record.message else {
continue;
};
let Bgp4MpMessage { bgp_message, .. } = m;
let BgpMessage::Update(u) = bgp_message else {
continue;
};
updates += 1;
for attr in &u.attributes {
*attr_counts
.entry(format!("{:?}", attr.attr_type()))
.or_default() += 1;
if let AttributeValue::MpReachNlri(nlri) | AttributeValue::MpUnreachNlri(nlri) =
attr
{
*safi_counts
.entry((u16::from(nlri.afi), u8::from(nlri.safi)))
.or_default() += 1;
}
}
}
}
println!(
"== files: {} records: {} updates: {}",
args.len(),
records,
updates
);
println!("== AFI/SAFI (count of MP_REACH/MP_UNREACH attributes seen):");
for ((afi, safi), n) in &safi_counts {
println!(" afi={afi} safi={safi}: {n}");
}
println!("== attribute tallies:");
for (t, n) in &attr_counts {
println!(" {t}: {n}");
}
}