Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 52 additions & 31 deletions examples/route_level_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,26 @@ fn main() {
let parser = BgpkitParser::new(url).unwrap();

let mut route_count = 0;
for route in parser.into_route_iter().take(1000) {
if route_count < 3 {
// Show first few routes
log::info!(
"Route {}: {} via AS{} (peer: {})",
route_count + 1,
route.prefix,
route.peer_asn,
route.peer_ip
);
if let Some(ref path) = route.as_path {
log::info!(" AS Path: {}", path);
'routes: for batch in parser.into_route_iter() {
for route in batch.routes() {
let route = route.unwrap();
if route_count < 3 {
log::info!(
"Route {}: {} via AS{} (peer: {})",
route_count + 1,
route.prefix,
route.peer_asn,
route.peer_ip
);
if let Some(path) = route.as_path {
log::info!(" AS Path: {}", path);
}
}
route_count += 1;
if route_count >= 1000 {
break 'routes;
}
}
route_count += 1;
}
let route_time = start.elapsed();
log::info!(
Expand Down Expand Up @@ -89,15 +94,23 @@ fn main() {
let filter = bgpkit_parser::Filter::new("peer_asn", "49788").unwrap();

let mut filtered_count = 0;
for route in parser.into_route_iter().take(1000) {
if route.match_filter(&filter) {
filtered_count += 1;
if filtered_count <= 3 {
log::info!(
"Matched filter (peer_asn=49788): {} from AS{}",
route.prefix,
route.peer_asn
);
let mut seen = 0;
'routes: for batch in parser.into_route_iter() {
for route in batch.all_routes() {
let route = route.unwrap();
if route.match_filter(&filter) {
filtered_count += 1;
if filtered_count <= 3 {
log::info!(
"Matched filter (peer_asn=49788): {} from AS{}",
route.prefix,
route.peer_asn
);
}
}
seen += 1;
if seen >= 1000 {
break 'routes;
}
}
}
Expand All @@ -114,15 +127,23 @@ fn main() {
let as_path_filter = bgpkit_parser::Filter::new("as_path", "1299").unwrap();

let mut as_path_matches = 0;
for route in parser.into_route_iter().take(1000) {
if route.match_filter(&as_path_filter) {
as_path_matches += 1;
if as_path_matches <= 3 {
log::info!(
"AS Path contains 1299: {} - path: {:?}",
route.prefix,
route.as_path.as_ref().map(|p| p.to_string())
);
let mut seen = 0;
'routes: for batch in parser.into_route_iter() {
for route in batch.all_routes() {
let route = route.unwrap();
if route.match_filter(&as_path_filter) {
as_path_matches += 1;
if as_path_matches <= 3 {
log::info!(
"AS Path contains 1299: {} - path: {:?}",
route.prefix,
route.as_path.map(|p| p.to_string())
);
}
}
seen += 1;
if seen >= 1000 {
break 'routes;
}
}
}
Expand Down
60 changes: 48 additions & 12 deletions src/models/bgp/elem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,19 @@ pub struct BgpElem {
pub deprecated: Option<Vec<AttrRaw>>,
}

/// Lightweight per-prefix route element.
/// Lightweight borrowed per-prefix route element.
///
/// This struct is intended for fast scans that only need route identity,
/// peer metadata, timestamp, and AS path. Use [`BgpElem`] when you need the
/// full set of BGP attributes. Because route elements do not carry
/// communities, community filters do not match [`BgpRouteElem`] values.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BgpRouteElem {
///
/// With the `serde` feature enabled, this borrowed type only supports
/// `Serialize`; use [`BgpRouteElemOwned`] when you need to `Deserialize`
/// route elements.
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct BgpRouteElem<'a> {
/// The timestamp of the item in floating-point format.
pub timestamp: f64,
/// The element type of an item.
Expand All @@ -177,24 +181,55 @@ pub struct BgpRouteElem {
pub peer_asn: Asn,
/// The network prefix of the item.
pub prefix: NetworkPrefix,
/// The optional path representation of the item.
/// The optional borrowed path representation of the item.
///
/// Route-level parsing shares the same AS path across all announced
/// prefixes from a single message.
/// Route-level parsing keeps the AS path on the containing route batch
/// and borrows it for each announced prefix.
pub as_path: Option<&'a AsPath>,
}

/// Owned lightweight per-prefix route element.
///
/// Convert [`BgpRouteElem`] into this type when route rows must outlive the
/// route batch they came from.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BgpRouteElemOwned {
pub timestamp: f64,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub elem_type: ElemType,
pub peer_ip: IpAddr,
pub peer_asn: Asn,
pub prefix: NetworkPrefix,
pub as_path: Option<Arc<AsPath>>,
}

impl BgpRouteElem<'_> {
pub fn into_owned(self) -> BgpRouteElemOwned {
BgpRouteElemOwned {
timestamp: self.timestamp,
elem_type: self.elem_type,
peer_ip: self.peer_ip,
peer_asn: self.peer_asn,
prefix: self.prefix,
as_path: self.as_path.map(|path| Arc::new(path.clone())),
}
}
}

impl Eq for BgpElem {}

impl Eq for BgpRouteElem {}
impl Eq for BgpRouteElem<'_> {}

impl Eq for BgpRouteElemOwned {}

impl PartialOrd<Self> for BgpElem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl PartialOrd<Self> for BgpRouteElem {
impl PartialOrd<Self> for BgpRouteElem<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
Expand All @@ -209,7 +244,7 @@ impl Ord for BgpElem {
}
}

impl Ord for BgpRouteElem {
impl Ord for BgpRouteElem<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.timestamp
.partial_cmp(&other.timestamp)
Expand Down Expand Up @@ -324,7 +359,7 @@ impl Display for BgpElem {
}
}

impl Display for BgpRouteElem {
impl Display for BgpRouteElem<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let t = match self.elem_type {
ElemType::ANNOUNCE => "A",
Expand Down Expand Up @@ -534,13 +569,14 @@ mod tests {

#[test]
fn test_route_elem_display() {
let as_path = AsPath::from_sequence([64496, 64497]);
let elem = BgpRouteElem {
timestamp: 1.1,
elem_type: ElemType::ANNOUNCE,
peer_ip: IpAddr::from_str("192.168.1.1").unwrap(),
peer_asn: 64496.into(),
prefix: NetworkPrefix::from_str("8.8.8.0/24").unwrap(),
as_path: Some(Arc::new(AsPath::from_sequence([64496, 64497]))),
as_path: Some(&as_path),
};

assert_eq!(
Expand Down
11 changes: 5 additions & 6 deletions src/parser/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ impl RouteFilterView for BgpElem {
}
}

impl RouteFilterView for BgpRouteElem {
impl RouteFilterView for BgpRouteElem<'_> {
fn timestamp(&self) -> f64 {
self.timestamp
}
Expand All @@ -698,7 +698,7 @@ impl RouteFilterView for BgpRouteElem {
}

fn as_path(&self) -> Option<&AsPath> {
self.as_path.as_deref()
self.as_path
}

fn matches_origin_asn(&self, asn: Asn) -> bool {
Expand All @@ -715,7 +715,7 @@ impl Filterable for BgpElem {
}
}

impl Filterable for BgpRouteElem {
impl Filterable for BgpRouteElem<'_> {
fn match_filter(&self, filter: &Filter) -> bool {
match_route_view_filter(self, filter)
}
Expand All @@ -727,7 +727,6 @@ mod tests {
use crate::BgpkitParser;
use anyhow::Result;
use std::str::FromStr;
use std::sync::Arc;

fn filter_test_elem() -> BgpElem {
BgpElem {
Expand Down Expand Up @@ -756,14 +755,14 @@ mod tests {
}
}

fn route_projection(elem: &BgpElem) -> BgpRouteElem {
fn route_projection(elem: &BgpElem) -> BgpRouteElem<'_> {
BgpRouteElem {
timestamp: elem.timestamp,
elem_type: elem.elem_type,
peer_ip: elem.peer_ip,
peer_asn: elem.peer_asn,
prefix: elem.prefix,
as_path: elem.as_path.clone().map(Arc::new),
as_path: elem.as_path.as_ref(),
}
}

Expand Down
14 changes: 7 additions & 7 deletions src/parser/iters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod update;
pub use default::{ElemIterator, RecordIterator};
pub use fallible::{FallibleElemIterator, FallibleRecordIterator};
pub use raw::RawRecordIterator;
pub use route::{FallibleRouteIterator, RouteIterator};
pub use route::{FallibleRouteIterator, RouteBatch, RouteBatchIter, RouteIterator};
pub use update::{
Bgp4MpUpdate, FallibleUpdateIterator, MrtUpdate, TableDumpV2Entry, UpdateIterator,
};
Expand Down Expand Up @@ -115,13 +115,13 @@ impl<R> BgpkitParser<R> {
UpdateIterator::new(self)
}

/// Creates an iterator over lightweight route elements from MRT data.
/// Creates an iterator over lightweight route batches from MRT data.
///
/// This iterator yields [`BgpRouteElem`](crate::models::BgpRouteElem)
/// values and only parses route identity, peer metadata, timestamp, and
/// AS path. Use [`into_elem_iter`](Self::into_elem_iter) when you need
/// the full [`BgpElem`] attribute set. Filters that only depend on route
/// fields are supported; `community` filters do not match route elements.
/// This iterator yields [`RouteBatch`] values. Each batch owns shared
/// route state such as AS path and raw prefix bytes; call
/// [`RouteBatch::routes`] to iterate filtered [`BgpRouteElem`](crate::models::BgpRouteElem)
/// rows. Use [`into_elem_iter`](Self::into_elem_iter) when you need the
/// full [`BgpElem`] attribute set.
pub fn into_route_iter(self) -> RouteIterator<R> {
RouteIterator::new(self)
}
Expand Down
Loading
Loading