diff --git a/src/lib.rs b/src/lib.rs index fbb12f2..4a7af1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ use crate::as2rel::As2relBgpkit; use crate::asinfo::AsInfoUtils; use crate::bogons::Bogons; use crate::countries::Countries; -use crate::mrt_collectors::MrtCollector; +use crate::mrt_collectors::{MrtCollector, MrtCollectorPeer}; use crate::rpki::RpkiTrie; use anyhow::Result; use chrono::NaiveDate; @@ -65,6 +65,7 @@ pub struct BgpkitCommons { countries: Option, rpki_trie: Option, mrt_collectors: Option>, + mrt_collector_peers: Option>, bogons: Option, asinfo: Option, as2rel: Option, @@ -86,6 +87,9 @@ impl BgpkitCommons { if self.mrt_collectors.is_some() { self.load_mrt_collectors()?; } + if self.mrt_collector_peers.is_some() { + self.load_mrt_collector_peers()?; + } if self.bogons.is_some() { self.load_bogons()?; } @@ -121,6 +125,12 @@ impl BgpkitCommons { Ok(()) } + /// Load MRT mrt_collectors data + pub fn load_mrt_collector_peers(&mut self) -> Result<()> { + self.mrt_collector_peers = Some(mrt_collectors::get_mrt_collector_peers()?); + Ok(()) + } + /// Load bogons data pub fn load_bogons(&mut self) -> Result<()> { self.bogons = Some(Bogons::new()?); diff --git a/src/mrt_collectors/mod.rs b/src/mrt_collectors/mod.rs index 275abd2..6d2865f 100644 --- a/src/mrt_collectors/mod.rs +++ b/src/mrt_collectors/mod.rs @@ -13,10 +13,12 @@ use serde::{Deserialize, Serialize, Serializer}; use std::cmp::Ordering; use std::fmt::{Display, Formatter}; +mod peers; mod riperis; mod routeviews; use crate::BgpkitCommons; +pub use peers::{get_mrt_collector_peers, MrtCollectorPeer}; pub use riperis::get_riperis_collectors; pub use routeviews::get_routeviews_collectors; @@ -119,4 +121,28 @@ impl BgpkitCommons { .as_ref() .map(|c| c.iter().filter(|x| x.country == country).cloned().collect()) } + + pub fn mrt_collector_peers_all(&self) -> Result> { + if self.mrt_collector_peers.is_none() { + return Err(anyhow!( + "mrt_collector_peers is not loaded, call commons.load_mrt_collector_peers() first" + )); + } + Ok(self.mrt_collector_peers.clone().unwrap()) + } + + pub fn mrt_collector_peers_full_feed(&self) -> Result> { + if self.mrt_collector_peers.is_none() { + return Err(anyhow!("mrt_collector_peers is not loaded")); + } + // Filter out mrt_collectors that have full feed + Ok(self + .mrt_collector_peers + .as_ref() + .unwrap() + .iter() + .filter(|x| x.is_full_feed()) + .cloned() + .collect()) + } } diff --git a/src/mrt_collectors/peers.rs b/src/mrt_collectors/peers.rs new file mode 100644 index 0000000..6c28563 --- /dev/null +++ b/src/mrt_collectors/peers.rs @@ -0,0 +1,68 @@ +use anyhow::Result; +use chrono::NaiveDate; +use serde::{Deserialize, Serialize}; +use std::net::IpAddr; + +const COLLECTOR_PEERS_URL: &str = "https://api.bgpkit.com/v3/peers/list"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MrtCollectorPeersData { + pub count: u32, + pub data: Vec, +} + +/// MRT collector meta information +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct MrtCollectorPeer { + /// latest available dated + pub date: NaiveDate, + /// collector peer IP + pub ip: IpAddr, + /// collector peer ASN + pub asn: u32, + /// collector name + pub collector: String, + /// number of IPv4 prefixes + pub num_v4_pfxs: u32, + /// number of IPv6 prefixes + pub num_v6_pfxs: u32, + /// number of connected ASNs + pub num_connected_asns: u32, +} + +impl MrtCollectorPeer { + pub fn is_full_feed_v4(&self) -> bool { + self.num_v4_pfxs >= 700_000 + } + + pub fn is_full_feed_v6(&self) -> bool { + self.num_v6_pfxs >= 100_000 + } + + pub fn is_full_feed(&self) -> bool { + self.is_full_feed_v4() || self.is_full_feed_v6() + } +} + +pub fn get_mrt_collector_peers() -> Result> { + let peers: MrtCollectorPeersData = oneio::read_json_struct(COLLECTOR_PEERS_URL)?; + + Ok(peers.data) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_peers() { + let mut peers = get_mrt_collector_peers().unwrap(); + assert!(!peers.is_empty()); + // sort peers by the number of connected ASNs + peers.sort_by(|a, b| b.num_connected_asns.cmp(&a.num_connected_asns)); + // print top 10 peers + for peer in peers.iter().take(10) { + println!("{:?}", peer); + } + } +} diff --git a/tests/mrt_collectors.rs b/tests/mrt_collectors.rs new file mode 100644 index 0000000..6ecb8e0 --- /dev/null +++ b/tests/mrt_collectors.rs @@ -0,0 +1,19 @@ +#[test] +fn test_get_collectors() { + let mut commons = bgpkit_commons::BgpkitCommons::new(); + commons.load_mrt_collectors().unwrap(); + + let collectors = commons.mrt_collectors_all().unwrap(); + assert!(!collectors.is_empty()); +} + +#[test] +fn test_get_collector_peers() { + let mut commons = bgpkit_commons::BgpkitCommons::new(); + commons.load_mrt_collector_peers().unwrap(); + let all_collector_peers = commons.mrt_collector_peers_all().unwrap(); + assert!(!all_collector_peers.is_empty()); + let full_feed_collector_peers = commons.mrt_collector_peers_full_feed().unwrap(); + assert!(!full_feed_collector_peers.is_empty()); + assert!(full_feed_collector_peers.len() < all_collector_peers.len()); +}