bgpkit_commons/lib.rs
1//! # Overview
2//!
3//! BGPKIT-Commons is a library for common BGP-related data and functions with a lazy-loading architecture.
4//! Each module can be independently enabled via feature flags, allowing for minimal builds.
5//!
6//! ## Available Modules
7//!
8//! ### [`asinfo`] - Autonomous System Information (requires `asinfo` feature)
9//! **Load Method**: `load_asinfo(as2org, population, hegemony, peeringdb)` or `load_asinfo_cached()`
10//! **Access Methods**: `asinfo_get(asn)`, `asinfo_all()`
11//! **Data Sources**: RIPE NCC, CAIDA as2org, APNIC population, IIJ IHR hegemony, PeeringDB
12//! **Functionality**: AS name resolution, country mapping, organization data, population statistics, hegemony scores
13//!
14//! ### [`as2rel`] - AS Relationship Data (requires `as2rel` feature)
15//! **Load Method**: `load_as2rel()`
16//! **Access Methods**: `as2rel_lookup(asn1, asn2)`
17//! **Data Sources**: BGPKIT AS relationship inference
18//! **Functionality**: Provider-customer, peer-to-peer, and sibling relationships between ASes
19//!
20//! ### [`bogons`] - Bogon Detection (requires `bogons` feature)
21//! **Load Method**: `load_bogons()`
22//! **Access Methods**: `bogons_match(input)`, `bogons_match_prefix(prefix)`, `bogons_match_asn(asn)`, `get_bogon_prefixes()`, `get_bogon_asns()`
23//! **Data Sources**: IANA special registries (IPv4, IPv6, ASN)
24//! **Functionality**: Detect invalid/reserved IP prefixes and ASNs that shouldn't appear in routing
25//!
26//! ### [`countries`] - Country Information (requires `countries` feature)
27//! **Load Method**: `load_countries()`
28//! **Access Methods**: `country_by_code(code)`, country lookup by name
29//! **Data Sources**: GeoNames geographical database
30//! **Functionality**: ISO country code to name mapping and geographical information
31//!
32//! ### [`mrt_collectors`] - MRT Collector Metadata (requires `mrt_collectors` feature)
33//! **Load Methods**: `load_mrt_collectors()`, `load_mrt_collector_peers()`
34//! **Access Methods**: `mrt_collectors_all()`, `mrt_collector_peers()`, `mrt_collector_peers_full_feed()`
35//! **Data Sources**: RouteViews and RIPE RIS official APIs
36//! **Functionality**: BGP collector information, peer details, full-feed vs partial-feed classification
37//!
38//! ### [`rpki`] - RPKI Validation (requires `rpki` feature)
39//! **Load Method**: `load_rpki(optional_date)`
40//! **Access Methods**: `rpki_validate(prefix, asn)`
41//! **Data Sources**: RIPE NCC historical data, Cloudflare real-time data
42//! **Functionality**: Route Origin Authorization (ROA) validation, supports multiple ROAs per prefix
43//!
44//! ## Quick Start
45//!
46//! Add `bgpkit-commons` to your `Cargo.toml`:
47//! ```toml
48//! [dependencies]
49//! bgpkit-commons = "0.8"
50//! ```
51//!
52//! ### Basic Usage Pattern
53//!
54//! All modules follow the same lazy-loading pattern:
55//! 1. Create a mutable `BgpkitCommons` instance
56//! 2. Load the data you need by calling `load_xxx()` methods
57//! 3. Access the data using the corresponding `xxx_yyy()` methods
58//!
59//! ```rust
60//! use bgpkit_commons::BgpkitCommons;
61//!
62//! let mut commons = BgpkitCommons::new();
63//!
64//! // Load bogon data
65//! commons.load_bogons().unwrap();
66//!
67//! // Use the data
68//! if let Ok(is_bogon) = commons.bogons_match("23456") {
69//! println!("ASN 23456 is a bogon: {}", is_bogon);
70//! }
71//! ```
72//!
73//! ### Working with Multiple Modules
74//!
75//! ```rust
76//! use bgpkit_commons::BgpkitCommons;
77//!
78//! let mut commons = BgpkitCommons::new();
79//!
80//! // Load multiple data sources
81//! commons.load_asinfo(false, false, false, false).unwrap();
82//! commons.load_countries().unwrap();
83//!
84//! // Use the data together
85//! if let Ok(Some(asinfo)) = commons.asinfo_get(13335) {
86//! println!("AS13335: {} ({})", asinfo.name, asinfo.country);
87//! }
88//! ```
89//!
90//! ## Feature Flags
91//!
92//! ### Module Features
93//! - `asinfo` - AS information with organization and population data
94//! - `as2rel` - AS relationship data
95//! - `bogons` - Bogon prefix and ASN detection
96//! - `countries` - Country information lookup
97//! - `mrt_collectors` - MRT collector metadata
98//! - `rpki` - RPKI validation functionality
99//!
100//! ### Convenience Features
101//! - `all` (default) - Enables all modules for backwards compatibility
102//!
103//! ### Minimal Build Example
104//! ```toml
105//! [dependencies]
106//! bgpkit-commons = { version = "0.8", default-features = false, features = ["bogons", "countries"] }
107//! ```
108//!
109//! ## Error Handling
110//!
111//! All access methods return `Result<T>` and will return an error if the corresponding module
112//! hasn't been loaded yet or if there are data validation issues. Error messages include guidance
113//! on which `load_xxx()` method to call. Always call the appropriate `load_xxx()` method before accessing data.
114//!
115//! ## Data Persistence and Reloading
116//!
117//! All loaded data is kept in memory for fast access. Use the `reload()` method to refresh
118//! all currently loaded data sources:
119//!
120//! ```rust
121//! # use bgpkit_commons::BgpkitCommons;
122//! let mut commons = BgpkitCommons::new();
123//! commons.load_bogons().unwrap();
124//!
125//! // Later, reload all loaded data
126//! commons.reload().unwrap();
127//! ```
128
129#![doc(
130 html_logo_url = "https://raw.githubusercontent.com/bgpkit/assets/main/logos/icon-transparent.png",
131 html_favicon_url = "https://raw.githubusercontent.com/bgpkit/assets/main/logos/favicon.ico"
132)]
133
134#[cfg(feature = "as2rel")]
135pub mod as2rel;
136#[cfg(feature = "asinfo")]
137pub mod asinfo;
138#[cfg(feature = "bogons")]
139pub mod bogons;
140#[cfg(feature = "countries")]
141pub mod countries;
142#[cfg(feature = "mrt_collectors")]
143pub mod mrt_collectors;
144#[cfg(feature = "rpki")]
145pub mod rpki;
146
147pub mod errors;
148
149// Re-export error types for convenience
150pub use errors::{BgpkitCommonsError, Result};
151
152/// Trait for modules that support lazy loading and reloading of data
153pub trait LazyLoadable {
154 /// Reload the module's data from its external sources
155 fn reload(&mut self) -> Result<()>;
156
157 /// Check if the module's data is currently loaded
158 fn is_loaded(&self) -> bool;
159
160 /// Get a description of the module's current loading status
161 fn loading_status(&self) -> &'static str;
162}
163
164#[derive(Default)]
165pub struct BgpkitCommons {
166 #[cfg(feature = "countries")]
167 countries: Option<crate::countries::Countries>,
168 #[cfg(feature = "rpki")]
169 rpki_trie: Option<crate::rpki::RpkiTrie>,
170 #[cfg(feature = "mrt_collectors")]
171 mrt_collectors: Option<Vec<crate::mrt_collectors::MrtCollector>>,
172 #[cfg(feature = "mrt_collectors")]
173 mrt_collector_peers: Option<Vec<crate::mrt_collectors::MrtCollectorPeer>>,
174 #[cfg(feature = "bogons")]
175 bogons: Option<crate::bogons::Bogons>,
176 #[cfg(feature = "asinfo")]
177 asinfo: Option<crate::asinfo::AsInfoUtils>,
178 #[cfg(feature = "as2rel")]
179 as2rel: Option<crate::as2rel::As2relBgpkit>,
180}
181
182impl BgpkitCommons {
183 pub fn new() -> Self {
184 Self::default()
185 }
186
187 /// Reload all data sources that are already loaded
188 pub fn reload(&mut self) -> Result<()> {
189 #[cfg(feature = "countries")]
190 if self.countries.is_some() {
191 self.load_countries()?;
192 }
193 #[cfg(feature = "rpki")]
194 if let Some(rpki) = self.rpki_trie.as_mut() {
195 rpki.reload()?;
196 }
197 #[cfg(feature = "mrt_collectors")]
198 if self.mrt_collectors.is_some() {
199 self.load_mrt_collectors()?;
200 }
201 #[cfg(feature = "mrt_collectors")]
202 if self.mrt_collector_peers.is_some() {
203 self.load_mrt_collector_peers()?;
204 }
205 #[cfg(feature = "bogons")]
206 if self.bogons.is_some() {
207 self.load_bogons()?;
208 }
209 #[cfg(feature = "asinfo")]
210 if let Some(asinfo) = self.asinfo.as_mut() {
211 asinfo.reload()?;
212 }
213 #[cfg(feature = "as2rel")]
214 if self.as2rel.is_some() {
215 self.load_as2rel()?;
216 }
217
218 Ok(())
219 }
220
221 /// Get loading status for all available modules
222 pub fn loading_status(&self) -> Vec<(&'static str, &'static str)> {
223 #[allow(unused_mut)] // mut needed when any features are enabled
224 let mut status = Vec::new();
225
226 #[cfg(feature = "countries")]
227 if let Some(ref countries) = self.countries {
228 status.push(("countries", countries.loading_status()));
229 } else {
230 status.push(("countries", "Countries data not loaded"));
231 }
232
233 #[cfg(feature = "bogons")]
234 if let Some(ref bogons) = self.bogons {
235 status.push(("bogons", bogons.loading_status()));
236 } else {
237 status.push(("bogons", "Bogons data not loaded"));
238 }
239
240 #[cfg(feature = "rpki")]
241 if let Some(ref rpki) = self.rpki_trie {
242 status.push(("rpki", rpki.loading_status()));
243 } else {
244 status.push(("rpki", "RPKI data not loaded"));
245 }
246
247 #[cfg(feature = "asinfo")]
248 if let Some(ref asinfo) = self.asinfo {
249 status.push(("asinfo", asinfo.loading_status()));
250 } else {
251 status.push(("asinfo", "ASInfo data not loaded"));
252 }
253
254 #[cfg(feature = "as2rel")]
255 if let Some(ref as2rel) = self.as2rel {
256 status.push(("as2rel", as2rel.loading_status()));
257 } else {
258 status.push(("as2rel", "AS2Rel data not loaded"));
259 }
260
261 #[cfg(feature = "mrt_collectors")]
262 {
263 if self.mrt_collectors.is_some() {
264 status.push(("mrt_collectors", "MRT collectors data loaded"));
265 } else {
266 status.push(("mrt_collectors", "MRT collectors data not loaded"));
267 }
268
269 if self.mrt_collector_peers.is_some() {
270 status.push(("mrt_collector_peers", "MRT collector peers data loaded"));
271 } else {
272 status.push(("mrt_collector_peers", "MRT collector peers data not loaded"));
273 }
274 }
275
276 status
277 }
278
279 /// Load countries data
280 #[cfg(feature = "countries")]
281 pub fn load_countries(&mut self) -> Result<()> {
282 self.countries = Some(crate::countries::Countries::new()?);
283 Ok(())
284 }
285
286 /// Load RPKI data
287 #[cfg(feature = "rpki")]
288 pub fn load_rpki(&mut self, date_opt: Option<chrono::NaiveDate>) -> Result<()> {
289 if let Some(date) = date_opt {
290 self.rpki_trie = Some(rpki::RpkiTrie::from_ripe_historical(date)?);
291 } else {
292 self.rpki_trie = Some(rpki::RpkiTrie::from_cloudflare()?);
293 }
294 Ok(())
295 }
296
297 /// Load MRT mrt_collectors data
298 #[cfg(feature = "mrt_collectors")]
299 pub fn load_mrt_collectors(&mut self) -> Result<()> {
300 self.mrt_collectors = Some(crate::mrt_collectors::get_all_collectors()?);
301 Ok(())
302 }
303
304 /// Load MRT mrt_collectors data
305 #[cfg(feature = "mrt_collectors")]
306 pub fn load_mrt_collector_peers(&mut self) -> Result<()> {
307 self.mrt_collector_peers = Some(crate::mrt_collectors::get_mrt_collector_peers()?);
308 Ok(())
309 }
310
311 /// Load bogons data
312 #[cfg(feature = "bogons")]
313 pub fn load_bogons(&mut self) -> Result<()> {
314 self.bogons = Some(crate::bogons::Bogons::new()?);
315 Ok(())
316 }
317
318 /// Load AS name and country data
319 #[cfg(feature = "asinfo")]
320 pub fn load_asinfo(
321 &mut self,
322 load_as2org: bool,
323 load_population: bool,
324 load_hegemony: bool,
325 load_peeringdb: bool,
326 ) -> Result<()> {
327 self.asinfo = Some(crate::asinfo::AsInfoUtils::new(
328 load_as2org,
329 load_population,
330 load_hegemony,
331 load_peeringdb,
332 )?);
333 Ok(())
334 }
335
336 #[cfg(feature = "asinfo")]
337 pub fn load_asinfo_cached(&mut self) -> Result<()> {
338 self.asinfo = Some(crate::asinfo::AsInfoUtils::new_from_cached()?);
339 Ok(())
340 }
341
342 /// Load AS-level relationship data
343 #[cfg(feature = "as2rel")]
344 pub fn load_as2rel(&mut self) -> Result<()> {
345 self.as2rel = Some(crate::as2rel::As2relBgpkit::new()?);
346 Ok(())
347 }
348}