mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-10-01 05:44:52 +02:00
Make specific end-point for announcement centric ROA vs BGP analysis. (closes: #241)
This commit is contained in:
+8
-10
@@ -13,7 +13,6 @@ use crate::commons::api::{
|
||||
AllCertAuthIssues, CaRepoDetails, CertAuthIssues, ChildCaInfo, CurrentRepoState,
|
||||
ParentCaContact, PublisherDetails, PublisherList, Token,
|
||||
};
|
||||
use crate::commons::bgp::{RoaSummary, RoaTable};
|
||||
use crate::commons::remote::rfc8183;
|
||||
use crate::commons::util::httpclient;
|
||||
use crate::constants::KRILL_CLI_API_ENV;
|
||||
@@ -212,17 +211,16 @@ impl KrillClient {
|
||||
Ok(ApiResponse::Empty)
|
||||
}
|
||||
|
||||
CaCommand::RouteAuthorizationsBgpDetails(handle) => {
|
||||
let uri = format!("api/v1/cas/{}/routes/bgp", handle);
|
||||
let roa_table = self.get_json(&uri).await?;
|
||||
Ok(ApiResponse::RouteAuthorizationsBgpDetails(roa_table))
|
||||
CaCommand::BgpAnalysisFull(handle) => {
|
||||
let uri = format!("api/v1/cas/{}/routes/analysis/full", handle);
|
||||
let report = self.get_json(&uri).await?;
|
||||
Ok(ApiResponse::BgpAnalysisFull(report))
|
||||
}
|
||||
|
||||
CaCommand::RouteAuthorizationsBgpSummary(handle) => {
|
||||
let uri = format!("api/v1/cas/{}/routes/bgp", handle);
|
||||
let roa_table: RoaTable = self.get_json(&uri).await?;
|
||||
let summary: RoaSummary = roa_table.into();
|
||||
Ok(ApiResponse::RouteAuthorizationsBgpSummary(summary))
|
||||
CaCommand::BgpAnalysisAnnouncements(handle) => {
|
||||
let uri = format!("api/v1/cas/{}/routes/analysis/announcements", handle);
|
||||
let report = self.get_json(&uri).await?;
|
||||
Ok(ApiResponse::BgpAnalysisAnnouncements(report))
|
||||
}
|
||||
|
||||
CaCommand::Show(handle) => {
|
||||
|
||||
+43
-18
@@ -607,19 +607,29 @@ impl Options {
|
||||
app.subcommand(sub)
|
||||
}
|
||||
|
||||
fn make_cas_routes_bgp_full_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
let mut sub = SubCommand::with_name("full").about("Show full report.");
|
||||
|
||||
sub = Self::add_general_args(sub);
|
||||
sub = Self::add_my_ca_arg(sub);
|
||||
app.subcommand(sub)
|
||||
}
|
||||
|
||||
fn make_cas_routes_bgp_announcements_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
let mut sub =
|
||||
SubCommand::with_name("announcements").about("Show announcement centric report.");
|
||||
|
||||
sub = Self::add_general_args(sub);
|
||||
sub = Self::add_my_ca_arg(sub);
|
||||
app.subcommand(sub)
|
||||
}
|
||||
|
||||
fn make_cas_routes_bgp_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
let mut sub = SubCommand::with_name("bgp")
|
||||
.about("Show current authorizations in relation to known announcements.");
|
||||
|
||||
sub = Self::add_general_args(sub);
|
||||
sub = Self::add_my_ca_arg(sub);
|
||||
|
||||
sub = sub.arg(
|
||||
Arg::with_name("full")
|
||||
.long("full")
|
||||
.help("Show detailed view instead of summary")
|
||||
.required(false),
|
||||
);
|
||||
sub = Self::make_cas_routes_bgp_full_sc(sub);
|
||||
sub = Self::make_cas_routes_bgp_announcements_sc(sub);
|
||||
|
||||
app.subcommand(sub)
|
||||
}
|
||||
@@ -1305,17 +1315,32 @@ impl Options {
|
||||
Ok(Options::make(general_args, command))
|
||||
}
|
||||
|
||||
fn parse_matches_cas_routes_bgp(matches: &ArgMatches) -> Result<Options, Error> {
|
||||
fn parse_matches_cas_routes_bgp_full(matches: &ArgMatches) -> Result<Options, Error> {
|
||||
let general_args = GeneralArgs::from_matches(matches)?;
|
||||
let my_ca = Self::parse_my_ca(matches)?;
|
||||
Ok(Options::make(
|
||||
general_args,
|
||||
Command::CertAuth(CaCommand::BgpAnalysisFull(my_ca)),
|
||||
))
|
||||
}
|
||||
|
||||
let command = if matches.is_present("full") {
|
||||
Command::CertAuth(CaCommand::RouteAuthorizationsBgpDetails(my_ca))
|
||||
fn parse_matches_cas_routes_bgp_announcements(matches: &ArgMatches) -> Result<Options, Error> {
|
||||
let general_args = GeneralArgs::from_matches(matches)?;
|
||||
let my_ca = Self::parse_my_ca(matches)?;
|
||||
Ok(Options::make(
|
||||
general_args,
|
||||
Command::CertAuth(CaCommand::BgpAnalysisAnnouncements(my_ca)),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_matches_cas_routes_bgp(matches: &ArgMatches) -> Result<Options, Error> {
|
||||
if let Some(m) = matches.subcommand_matches("full") {
|
||||
Self::parse_matches_cas_routes_bgp_full(m)
|
||||
} else if let Some(m) = matches.subcommand_matches("announcements") {
|
||||
Self::parse_matches_cas_routes_bgp_announcements(m)
|
||||
} else {
|
||||
Command::CertAuth(CaCommand::RouteAuthorizationsBgpSummary(my_ca))
|
||||
};
|
||||
|
||||
Ok(Options::make(general_args, command))
|
||||
Err(Error::UnrecognisedSubCommand)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_matches_cas_routes(matches: &ArgMatches) -> Result<Options, Error> {
|
||||
@@ -1659,10 +1684,10 @@ pub enum CaCommand {
|
||||
RouteAuthorizationsUpdate(Handle, RoaDefinitionUpdates),
|
||||
|
||||
#[display(fmt = "Show detailed ROA vs BGP analysis for ca: '{}'", _0)]
|
||||
RouteAuthorizationsBgpDetails(Handle),
|
||||
BgpAnalysisFull(Handle),
|
||||
|
||||
#[display(fmt = "Show summary of ROA vs BGP analysis for ca: '{}'", _0)]
|
||||
RouteAuthorizationsBgpSummary(Handle),
|
||||
BgpAnalysisAnnouncements(Handle),
|
||||
|
||||
// Show details for this CA
|
||||
#[display(fmt = "Show details for ca: '{}'", _0)]
|
||||
|
||||
+7
-9
@@ -11,7 +11,7 @@ use crate::commons::api::{
|
||||
ParentCaContact, PublisherDetails, PublisherList, RepositoryContact, RoaDefinition, ServerInfo,
|
||||
StoredEffect,
|
||||
};
|
||||
use crate::commons::bgp::{RoaSummary, RoaTable};
|
||||
use crate::commons::bgp::{AnnouncementReport, BgpAnalysisReport};
|
||||
use crate::commons::eventsourcing::WithStorableDetails;
|
||||
use crate::commons::remote::api::ClientInfo;
|
||||
use crate::commons::remote::rfc8183;
|
||||
@@ -31,8 +31,8 @@ pub enum ApiResponse {
|
||||
CertAuthAction(CaCommandDetails),
|
||||
CertAuths(CertAuthList),
|
||||
RouteAuthorizations(Vec<RoaDefinition>),
|
||||
RouteAuthorizationsBgpDetails(RoaTable),
|
||||
RouteAuthorizationsBgpSummary(RoaSummary),
|
||||
BgpAnalysisFull(BgpAnalysisReport),
|
||||
BgpAnalysisAnnouncements(AnnouncementReport),
|
||||
|
||||
ParentCaContact(ParentCaContact),
|
||||
|
||||
@@ -72,10 +72,8 @@ impl ApiResponse {
|
||||
ApiResponse::CertAuthIssues(issues) => Ok(Some(issues.report(fmt)?)),
|
||||
ApiResponse::AllCertAuthIssues(issues) => Ok(Some(issues.report(fmt)?)),
|
||||
ApiResponse::RouteAuthorizations(auths) => Ok(Some(auths.report(fmt)?)),
|
||||
ApiResponse::RouteAuthorizationsBgpDetails(table) => Ok(Some(table.report(fmt)?)),
|
||||
ApiResponse::RouteAuthorizationsBgpSummary(summary) => {
|
||||
Ok(Some(summary.report(fmt)?))
|
||||
}
|
||||
ApiResponse::BgpAnalysisFull(table) => Ok(Some(table.report(fmt)?)),
|
||||
ApiResponse::BgpAnalysisAnnouncements(summary) => Ok(Some(summary.report(fmt)?)),
|
||||
ApiResponse::ParentCaContact(contact) => Ok(Some(contact.report(fmt)?)),
|
||||
ApiResponse::ChildInfo(info) => Ok(Some(info.report(fmt)?)),
|
||||
ApiResponse::PublisherList(list) => Ok(Some(list.report(fmt)?)),
|
||||
@@ -414,13 +412,13 @@ impl Report for Vec<RoaDefinition> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Report for RoaTable {
|
||||
impl Report for BgpAnalysisReport {
|
||||
fn text(&self) -> Result<String, ReportError> {
|
||||
Ok(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Report for RoaSummary {
|
||||
impl Report for AnnouncementReport {
|
||||
fn text(&self) -> Result<String, ReportError> {
|
||||
Ok(self.to_string())
|
||||
}
|
||||
|
||||
+26
-18
@@ -7,11 +7,13 @@ use rpki::x509::Time;
|
||||
use crate::commons::api::{ResourceSet, RoaDefinition};
|
||||
use crate::commons::bgp::{
|
||||
make_roa_tree, make_validated_announcement_tree, Announcement, AnnouncementValidity,
|
||||
Announcements, IpRange, RisDumpError, RisDumpLoader, RoaTable, RoaTableEntry,
|
||||
Announcements, BgpAnalysisEntry, BgpAnalysisReport, IpRange, RisDumpError, RisDumpLoader,
|
||||
ValidatedAnnouncement,
|
||||
};
|
||||
use crate::constants::BGP_RIS_REFRESH_MINUTES;
|
||||
|
||||
//------------ BgpAnalyser -------------------------------------------------
|
||||
|
||||
/// This type helps analyse ROAs vs BGP and vice versa.
|
||||
pub struct BgpAnalyser {
|
||||
dumploader: Option<RisDumpLoader>,
|
||||
@@ -57,14 +59,14 @@ impl BgpAnalyser {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyse(&self, roas: &[RoaDefinition], scope: &ResourceSet) -> RoaTable {
|
||||
pub fn analyse(&self, roas: &[RoaDefinition], scope: &ResourceSet) -> BgpAnalysisReport {
|
||||
let seen = self.seen.read().unwrap();
|
||||
let mut entries = vec![];
|
||||
|
||||
if seen.last_updated().is_none() {
|
||||
// nothing to analyse, just push all ROAs as 'no announcement info'
|
||||
for roa in roas {
|
||||
entries.push(RoaTableEntry::roa_no_announcement_info(roa.clone()));
|
||||
entries.push(BgpAnalysisEntry::roa_no_announcement_info(roa.clone()));
|
||||
}
|
||||
} else {
|
||||
let roa_tree = make_roa_tree(roas);
|
||||
@@ -91,7 +93,7 @@ impl BgpAnalyser {
|
||||
for roa in roas {
|
||||
let covered = validated_tree.matching_or_more_specific(&roa.prefix());
|
||||
if covered.is_empty() {
|
||||
entries.push(RoaTableEntry::roa_stale(roa.clone()))
|
||||
entries.push(BgpAnalysisEntry::roa_stale(roa.clone()))
|
||||
} else {
|
||||
let allows: Vec<Announcement> = covered
|
||||
.iter()
|
||||
@@ -113,9 +115,9 @@ impl BgpAnalyser {
|
||||
.collect();
|
||||
|
||||
if allows.is_empty() {
|
||||
entries.push(RoaTableEntry::roa_disallowing(roa.clone(), disallows));
|
||||
entries.push(BgpAnalysisEntry::roa_disallowing(roa.clone(), disallows));
|
||||
} else {
|
||||
entries.push(RoaTableEntry::roa_authorizing(
|
||||
entries.push(BgpAnalysisEntry::roa_authorizing(
|
||||
roa.clone(),
|
||||
allows,
|
||||
disallows,
|
||||
@@ -126,28 +128,33 @@ impl BgpAnalyser {
|
||||
|
||||
// Loop over all validated announcements and report
|
||||
for v in validated.into_iter() {
|
||||
let (announcement, validity, _, invalidating_roas) = v.unpack();
|
||||
let (announcement, validity, allowed_by, invalidating_roas) = v.unpack();
|
||||
match validity {
|
||||
AnnouncementValidity::Valid => {} // will show up under ROAs
|
||||
AnnouncementValidity::Valid => {
|
||||
entries.push(BgpAnalysisEntry::announcement_valid(
|
||||
announcement,
|
||||
allowed_by.unwrap(), // always set for valid announcements
|
||||
))
|
||||
}
|
||||
AnnouncementValidity::InvalidLength => {
|
||||
entries.push(RoaTableEntry::announcement_invalid_length(
|
||||
entries.push(BgpAnalysisEntry::announcement_invalid_length(
|
||||
announcement,
|
||||
invalidating_roas,
|
||||
));
|
||||
}
|
||||
AnnouncementValidity::InvalidAsn => {
|
||||
entries.push(RoaTableEntry::announcement_invalid_asn(
|
||||
entries.push(BgpAnalysisEntry::announcement_invalid_asn(
|
||||
announcement,
|
||||
invalidating_roas,
|
||||
));
|
||||
}
|
||||
AnnouncementValidity::NotFound => {
|
||||
entries.push(RoaTableEntry::announcement_not_found(announcement));
|
||||
entries.push(BgpAnalysisEntry::announcement_not_found(announcement));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RoaTable::new(entries)
|
||||
BgpAnalysisReport::new(entries)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -182,7 +189,7 @@ impl From<RisDumpError> for BgpAnalyserError {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::commons::bgp::RoaTableEntryState;
|
||||
use crate::commons::bgp::BgpAnalysisState;
|
||||
use crate::test::*;
|
||||
|
||||
use super::*;
|
||||
@@ -226,14 +233,15 @@ mod tests {
|
||||
ann_not_found,
|
||||
ann_irrelevant,
|
||||
]);
|
||||
let table = analyser.analyse(&[roa_authorizing, roa_stale, roa_disallowing], &resources);
|
||||
|
||||
let expected_table: RoaTable = serde_json::from_str(include_str!(
|
||||
"../../../test-resources/bgp/expected_roa_table.json"
|
||||
let report = analyser.analyse(&[roa_authorizing, roa_stale, roa_disallowing], &resources);
|
||||
|
||||
let expected: BgpAnalysisReport = serde_json::from_str(include_str!(
|
||||
"../../../test-resources/bgp/expected_bgp_analyis_report.json"
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(table, expected_table);
|
||||
assert_eq!(report, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -251,7 +259,7 @@ mod tests {
|
||||
|
||||
let roas_no_info: Vec<&RoaDefinition> = table_entries
|
||||
.iter()
|
||||
.filter(|e| e.state() == RoaTableEntryState::RoaNoAnnouncementInfo)
|
||||
.filter(|e| e.state() == BgpAnalysisState::RoaNoAnnouncementInfo)
|
||||
.map(|e| e.definition())
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ impl Announcement {
|
||||
ValidatedAnnouncement {
|
||||
announcement: self.clone(),
|
||||
validity: AnnouncementValidity::NotFound,
|
||||
validating: None,
|
||||
invalidating: vec![],
|
||||
authorizing: None,
|
||||
disallowing: vec![],
|
||||
}
|
||||
} else {
|
||||
let mut invalidating = vec![];
|
||||
@@ -81,8 +81,8 @@ impl Announcement {
|
||||
return ValidatedAnnouncement {
|
||||
announcement: self.clone(),
|
||||
validity: AnnouncementValidity::Valid,
|
||||
validating: Some(roa.clone()),
|
||||
invalidating: vec![],
|
||||
authorizing: Some(roa.clone()),
|
||||
disallowing: vec![],
|
||||
};
|
||||
} else {
|
||||
same_asn_found = true;
|
||||
@@ -100,8 +100,8 @@ impl Announcement {
|
||||
ValidatedAnnouncement {
|
||||
announcement: self.clone(),
|
||||
validity,
|
||||
validating: None,
|
||||
invalidating,
|
||||
authorizing: None,
|
||||
disallowing: invalidating,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,8 +227,8 @@ impl Default for Announcements {
|
||||
pub struct ValidatedAnnouncement {
|
||||
announcement: Announcement,
|
||||
validity: AnnouncementValidity,
|
||||
validating: Option<RoaDefinition>,
|
||||
invalidating: Vec<RoaDefinition>,
|
||||
authorizing: Option<RoaDefinition>,
|
||||
disallowing: Vec<RoaDefinition>,
|
||||
}
|
||||
|
||||
impl ValidatedAnnouncement {
|
||||
@@ -251,8 +251,8 @@ impl ValidatedAnnouncement {
|
||||
(
|
||||
self.announcement,
|
||||
self.validity,
|
||||
self.validating,
|
||||
self.invalidating,
|
||||
self.authorizing,
|
||||
self.disallowing,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,5 @@ pub use self::iptree::*;
|
||||
mod risdumps;
|
||||
pub use self::risdumps::*;
|
||||
|
||||
mod roa_table;
|
||||
pub use self::roa_table::*;
|
||||
mod report;
|
||||
pub use self::report::*;
|
||||
|
||||
@@ -5,172 +5,38 @@ use std::fmt;
|
||||
use crate::commons::api::RoaDefinition;
|
||||
use crate::commons::bgp::Announcement;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialOrd, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RoaTableEntryState {
|
||||
RoaAuthorizing,
|
||||
RoaDisallowing,
|
||||
RoaStale,
|
||||
AnnouncementInvalidLength,
|
||||
AnnouncementInvalidAsn,
|
||||
AnnouncementNotFound,
|
||||
RoaNoAnnouncementInfo,
|
||||
}
|
||||
//------------ BgpAnalysisReport -------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct RoaTableEntry {
|
||||
#[serde(flatten)]
|
||||
definition: RoaDefinition,
|
||||
state: RoaTableEntryState,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
|
||||
disallowed_by: Vec<RoaDefinition>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
|
||||
authorizes: Vec<Announcement>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
|
||||
disallows: Vec<Announcement>,
|
||||
}
|
||||
pub struct BgpAnalysisReport(Vec<BgpAnalysisEntry>);
|
||||
|
||||
impl RoaTableEntry {
|
||||
pub fn state(&self) -> RoaTableEntryState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn definition(&self) -> &RoaDefinition {
|
||||
&self.definition
|
||||
}
|
||||
|
||||
pub fn roa_authorizing(
|
||||
definition: RoaDefinition,
|
||||
mut authorizes: Vec<Announcement>,
|
||||
mut disallows: Vec<Announcement>,
|
||||
) -> Self {
|
||||
authorizes.sort();
|
||||
disallows.sort();
|
||||
RoaTableEntry {
|
||||
definition,
|
||||
state: RoaTableEntryState::RoaAuthorizing,
|
||||
disallowed_by: vec![],
|
||||
authorizes,
|
||||
disallows,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roa_disallowing(definition: RoaDefinition, mut disallows: Vec<Announcement>) -> Self {
|
||||
disallows.sort();
|
||||
RoaTableEntry {
|
||||
definition,
|
||||
state: RoaTableEntryState::RoaDisallowing,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roa_stale(definition: RoaDefinition) -> Self {
|
||||
RoaTableEntry {
|
||||
definition,
|
||||
state: RoaTableEntryState::RoaStale,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roa_no_announcement_info(definition: RoaDefinition) -> Self {
|
||||
RoaTableEntry {
|
||||
definition,
|
||||
state: RoaTableEntryState::RoaNoAnnouncementInfo,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_invalid_asn(
|
||||
announcement: Announcement,
|
||||
mut disallowed_by: Vec<RoaDefinition>,
|
||||
) -> Self {
|
||||
disallowed_by.sort();
|
||||
RoaTableEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: RoaTableEntryState::AnnouncementInvalidAsn,
|
||||
disallowed_by,
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_invalid_length(
|
||||
announcement: Announcement,
|
||||
mut disallowed_by: Vec<RoaDefinition>,
|
||||
) -> Self {
|
||||
disallowed_by.sort();
|
||||
RoaTableEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: RoaTableEntryState::AnnouncementInvalidLength,
|
||||
disallowed_by,
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_not_found(announcement: Announcement) -> Self {
|
||||
RoaTableEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: RoaTableEntryState::AnnouncementNotFound,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for RoaTableEntry {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let mut ordering = self.state.cmp(&other.state);
|
||||
if ordering == Ordering::Equal {
|
||||
ordering = self.definition.cmp(&other.definition);
|
||||
}
|
||||
ordering
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RoaTableEntry {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct RoaTable(Vec<RoaTableEntry>);
|
||||
|
||||
impl RoaTable {
|
||||
pub fn new(mut roas: Vec<RoaTableEntry>) -> Self {
|
||||
impl BgpAnalysisReport {
|
||||
pub fn new(mut roas: Vec<BgpAnalysisEntry>) -> Self {
|
||||
roas.sort();
|
||||
RoaTable(roas)
|
||||
BgpAnalysisReport(roas)
|
||||
}
|
||||
|
||||
pub fn entries(&self) -> &Vec<RoaTableEntry> {
|
||||
pub fn entries(&self) -> &Vec<BgpAnalysisEntry> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn matching_defs(&self, state: RoaTableEntryState) -> Vec<&RoaDefinition> {
|
||||
pub fn matching_defs(&self, state: BgpAnalysisState) -> Vec<&RoaDefinition> {
|
||||
self.matching_entries(state)
|
||||
.into_iter()
|
||||
.map(|e| &e.definition)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn matching_entries(&self, state: RoaTableEntryState) -> Vec<&RoaTableEntry> {
|
||||
pub fn matching_entries(&self, state: BgpAnalysisState) -> Vec<&BgpAnalysisEntry> {
|
||||
self.0.iter().filter(|e| e.state == state).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RoaTable {
|
||||
impl fmt::Display for BgpAnalysisReport {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let entries = self.entries();
|
||||
|
||||
let mut entry_map: HashMap<RoaTableEntryState, Vec<&RoaTableEntry>> = HashMap::new();
|
||||
let mut entry_map: HashMap<BgpAnalysisState, Vec<&BgpAnalysisEntry>> = HashMap::new();
|
||||
for entry in entries.into_iter() {
|
||||
let state = entry.state();
|
||||
if !entry_map.contains_key(&state) {
|
||||
@@ -179,12 +45,12 @@ impl fmt::Display for RoaTable {
|
||||
entry_map.get_mut(&state).unwrap().push(entry);
|
||||
}
|
||||
|
||||
if entry_map.contains_key(&RoaTableEntryState::RoaNoAnnouncementInfo) {
|
||||
if entry_map.contains_key(&BgpAnalysisState::RoaNoAnnouncementInfo) {
|
||||
write!(f, "no BGP announcements known")
|
||||
} else {
|
||||
if let Some(valids) = entry_map.get(&RoaTableEntryState::RoaAuthorizing) {
|
||||
if let Some(authorizing) = entry_map.get(&BgpAnalysisState::RoaAuthorizing) {
|
||||
writeln!(f, "Authorizations causing VALID announcements:")?;
|
||||
for roa in valids {
|
||||
for roa in authorizing {
|
||||
writeln!(f)?;
|
||||
writeln!(f, "\tDefinition: {}", roa.definition)?;
|
||||
writeln!(f)?;
|
||||
@@ -204,9 +70,9 @@ impl fmt::Display for RoaTable {
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
if let Some(invalids) = entry_map.get(&RoaTableEntryState::RoaDisallowing) {
|
||||
if let Some(disallowing) = entry_map.get(&BgpAnalysisState::RoaDisallowing) {
|
||||
writeln!(f, "Authorizations causing INVALID announcements only:")?;
|
||||
for roa in invalids {
|
||||
for roa in disallowing {
|
||||
writeln!(f)?;
|
||||
writeln!(f, "\tDefinition: {}", roa.definition)?;
|
||||
writeln!(f)?;
|
||||
@@ -218,7 +84,7 @@ impl fmt::Display for RoaTable {
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
if let Some(stales) = entry_map.get(&RoaTableEntryState::RoaStale) {
|
||||
if let Some(stales) = entry_map.get(&BgpAnalysisState::RoaStale) {
|
||||
writeln!(
|
||||
f,
|
||||
"Authorizations for which no announcements are found (possibly stale):"
|
||||
@@ -230,7 +96,16 @@ impl fmt::Display for RoaTable {
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
if let Some(invalid_asn) = entry_map.get(&RoaTableEntryState::AnnouncementInvalidAsn) {
|
||||
if let Some(valids) = entry_map.get(&BgpAnalysisState::AnnouncementValid) {
|
||||
writeln!(f, "Announcements which are valid:")?;
|
||||
writeln!(f)?;
|
||||
for ann in valids {
|
||||
writeln!(f, "\tAnnouncement: {}", ann.definition)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
if let Some(invalid_asn) = entry_map.get(&BgpAnalysisState::AnnouncementInvalidAsn) {
|
||||
writeln!(f, "Announcements from an unauthorized ASN:")?;
|
||||
for ann in invalid_asn {
|
||||
writeln!(f)?;
|
||||
@@ -245,7 +120,7 @@ impl fmt::Display for RoaTable {
|
||||
}
|
||||
|
||||
if let Some(invalid_length) =
|
||||
entry_map.get(&RoaTableEntryState::AnnouncementInvalidLength)
|
||||
entry_map.get(&BgpAnalysisState::AnnouncementInvalidLength)
|
||||
{
|
||||
writeln!(f, "Announcements from an authorized ASN, which are too specific (not allowed by max length):")?;
|
||||
for ann in invalid_length {
|
||||
@@ -260,8 +135,9 @@ impl fmt::Display for RoaTable {
|
||||
writeln!(f)?;
|
||||
}
|
||||
|
||||
if let Some(not_found) = entry_map.get(&RoaTableEntryState::AnnouncementNotFound) {
|
||||
if let Some(not_found) = entry_map.get(&BgpAnalysisState::AnnouncementNotFound) {
|
||||
writeln!(f, "Announcements which are 'not found' (not covered by any of your authorizations):")?;
|
||||
writeln!(f)?;
|
||||
for ann in not_found {
|
||||
writeln!(f, "\tAnnouncement: {}", ann.definition)?;
|
||||
}
|
||||
@@ -273,28 +149,209 @@ impl fmt::Display for RoaTable {
|
||||
}
|
||||
}
|
||||
|
||||
//------------ RoaSummary --------------------------------------------------
|
||||
//------------ BgpAnalysisEntry --------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct RoaSummary(Vec<RoaSummmaryEntry>);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct RoaSummmaryEntry {
|
||||
pub struct BgpAnalysisEntry {
|
||||
#[serde(flatten)]
|
||||
definition: RoaDefinition,
|
||||
state: RoaSummaryState,
|
||||
state: BgpAnalysisState,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
allowed_by: Option<RoaDefinition>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
|
||||
disallowed_by: Vec<RoaDefinition>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
|
||||
authorizes: Vec<Announcement>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
|
||||
disallows: Vec<Announcement>,
|
||||
}
|
||||
|
||||
impl fmt::Display for RoaSummmaryEntry {
|
||||
impl BgpAnalysisEntry {
|
||||
pub fn definition(&self) -> &RoaDefinition {
|
||||
&self.definition
|
||||
}
|
||||
|
||||
pub fn state(&self) -> BgpAnalysisState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn allowed_by(&self) -> Option<&RoaDefinition> {
|
||||
self.allowed_by.as_ref()
|
||||
}
|
||||
|
||||
pub fn disallowed_by(&self) -> &Vec<RoaDefinition> {
|
||||
&self.disallowed_by
|
||||
}
|
||||
|
||||
pub fn authorizes(&self) -> &Vec<Announcement> {
|
||||
&self.authorizes
|
||||
}
|
||||
|
||||
pub fn disallows(&self) -> &Vec<Announcement> {
|
||||
&self.disallows
|
||||
}
|
||||
|
||||
pub fn roa_authorizing(
|
||||
definition: RoaDefinition,
|
||||
mut authorizes: Vec<Announcement>,
|
||||
mut disallows: Vec<Announcement>,
|
||||
) -> Self {
|
||||
authorizes.sort();
|
||||
disallows.sort();
|
||||
BgpAnalysisEntry {
|
||||
definition,
|
||||
state: BgpAnalysisState::RoaAuthorizing,
|
||||
allowed_by: None,
|
||||
disallowed_by: vec![],
|
||||
authorizes,
|
||||
disallows,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roa_disallowing(definition: RoaDefinition, mut disallows: Vec<Announcement>) -> Self {
|
||||
disallows.sort();
|
||||
BgpAnalysisEntry {
|
||||
definition,
|
||||
state: BgpAnalysisState::RoaDisallowing,
|
||||
allowed_by: None,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roa_stale(definition: RoaDefinition) -> Self {
|
||||
BgpAnalysisEntry {
|
||||
definition,
|
||||
state: BgpAnalysisState::RoaStale,
|
||||
allowed_by: None,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roa_no_announcement_info(definition: RoaDefinition) -> Self {
|
||||
BgpAnalysisEntry {
|
||||
definition,
|
||||
state: BgpAnalysisState::RoaNoAnnouncementInfo,
|
||||
allowed_by: None,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_valid(announcement: Announcement, allowed_by: RoaDefinition) -> Self {
|
||||
BgpAnalysisEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: BgpAnalysisState::AnnouncementValid,
|
||||
allowed_by: Some(allowed_by),
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_invalid_asn(
|
||||
announcement: Announcement,
|
||||
mut disallowed_by: Vec<RoaDefinition>,
|
||||
) -> Self {
|
||||
disallowed_by.sort();
|
||||
BgpAnalysisEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: BgpAnalysisState::AnnouncementInvalidAsn,
|
||||
allowed_by: None,
|
||||
disallowed_by,
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_invalid_length(
|
||||
announcement: Announcement,
|
||||
mut disallowed_by: Vec<RoaDefinition>,
|
||||
) -> Self {
|
||||
disallowed_by.sort();
|
||||
BgpAnalysisEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: BgpAnalysisState::AnnouncementInvalidLength,
|
||||
allowed_by: None,
|
||||
disallowed_by,
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announcement_not_found(announcement: Announcement) -> Self {
|
||||
BgpAnalysisEntry {
|
||||
definition: RoaDefinition::from(announcement),
|
||||
state: BgpAnalysisState::AnnouncementNotFound,
|
||||
allowed_by: None,
|
||||
disallowed_by: vec![],
|
||||
authorizes: vec![],
|
||||
disallows: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BgpAnalysisEntry {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let mut ordering = self.state.cmp(&other.state);
|
||||
if ordering == Ordering::Equal {
|
||||
ordering = self.definition.cmp(&other.definition);
|
||||
}
|
||||
ordering
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for BgpAnalysisEntry {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
//------------ BgpAnalysisState --------------------------------------------
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialOrd, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BgpAnalysisState {
|
||||
RoaAuthorizing,
|
||||
RoaDisallowing,
|
||||
RoaStale,
|
||||
AnnouncementValid,
|
||||
AnnouncementInvalidLength,
|
||||
AnnouncementInvalidAsn,
|
||||
AnnouncementNotFound,
|
||||
RoaNoAnnouncementInfo,
|
||||
}
|
||||
|
||||
//------------ AnnouncementReport ------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct AnnouncementReport(Vec<AnnouncementReportEntry>);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct AnnouncementReportEntry {
|
||||
definition: RoaDefinition,
|
||||
state: AnnouncementReportState,
|
||||
}
|
||||
|
||||
impl fmt::Display for AnnouncementReportEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let state_str = match self.state {
|
||||
RoaSummaryState::Valid => "announcement 'valid'",
|
||||
RoaSummaryState::InvalidAsn => "announcement 'invalid': unauthorized asn",
|
||||
RoaSummaryState::InvalidLength => "announcement 'invalid': more specific than allowed",
|
||||
RoaSummaryState::NotFound => "announcement 'not found': not covered by your ROAs",
|
||||
RoaSummaryState::Stale => {
|
||||
AnnouncementReportState::Valid => "announcement 'valid'",
|
||||
AnnouncementReportState::InvalidAsn => "announcement 'invalid': unauthorized asn",
|
||||
AnnouncementReportState::InvalidLength => {
|
||||
"announcement 'invalid': more specific than allowed"
|
||||
}
|
||||
AnnouncementReportState::NotFound => {
|
||||
"announcement 'not found': not covered by your ROAs"
|
||||
}
|
||||
AnnouncementReportState::Stale => {
|
||||
"ROA does not cover any known announcement (stale or backup?)"
|
||||
}
|
||||
RoaSummaryState::NoInfo => "ROA exists, but no bgp info currently available",
|
||||
AnnouncementReportState::NoInfo => "ROA exists, but no bgp info currently available",
|
||||
};
|
||||
write!(f, "{}\t{}", self.definition, state_str)
|
||||
}
|
||||
@@ -302,7 +359,7 @@ impl fmt::Display for RoaSummmaryEntry {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RoaSummaryState {
|
||||
pub enum AnnouncementReportState {
|
||||
Valid,
|
||||
InvalidAsn,
|
||||
InvalidLength,
|
||||
@@ -311,57 +368,53 @@ pub enum RoaSummaryState {
|
||||
NoInfo,
|
||||
}
|
||||
|
||||
impl From<RoaTable> for RoaSummary {
|
||||
fn from(table: RoaTable) -> Self {
|
||||
let mut entries: Vec<RoaSummmaryEntry> = vec![];
|
||||
for valid in table
|
||||
.matching_entries(RoaTableEntryState::RoaAuthorizing)
|
||||
.into_iter()
|
||||
.flat_map(|e| &e.authorizes)
|
||||
{
|
||||
entries.push(RoaSummmaryEntry {
|
||||
definition: valid.clone().into(),
|
||||
state: RoaSummaryState::Valid,
|
||||
impl From<BgpAnalysisReport> for AnnouncementReport {
|
||||
fn from(table: BgpAnalysisReport) -> Self {
|
||||
let mut entries: Vec<AnnouncementReportEntry> = vec![];
|
||||
for def in table.matching_defs(BgpAnalysisState::AnnouncementValid) {
|
||||
entries.push(AnnouncementReportEntry {
|
||||
definition: def.clone(),
|
||||
state: AnnouncementReportState::Valid,
|
||||
})
|
||||
}
|
||||
|
||||
for def in table.matching_defs(RoaTableEntryState::AnnouncementInvalidAsn) {
|
||||
entries.push(RoaSummmaryEntry {
|
||||
for def in table.matching_defs(BgpAnalysisState::AnnouncementInvalidAsn) {
|
||||
entries.push(AnnouncementReportEntry {
|
||||
definition: def.clone(),
|
||||
state: RoaSummaryState::InvalidAsn,
|
||||
state: AnnouncementReportState::InvalidAsn,
|
||||
})
|
||||
}
|
||||
|
||||
for def in table.matching_defs(RoaTableEntryState::AnnouncementInvalidLength) {
|
||||
entries.push(RoaSummmaryEntry {
|
||||
for def in table.matching_defs(BgpAnalysisState::AnnouncementInvalidLength) {
|
||||
entries.push(AnnouncementReportEntry {
|
||||
definition: def.clone(),
|
||||
state: RoaSummaryState::InvalidLength,
|
||||
state: AnnouncementReportState::InvalidLength,
|
||||
})
|
||||
}
|
||||
|
||||
for def in table.matching_defs(RoaTableEntryState::AnnouncementNotFound) {
|
||||
entries.push(RoaSummmaryEntry {
|
||||
for def in table.matching_defs(BgpAnalysisState::AnnouncementNotFound) {
|
||||
entries.push(AnnouncementReportEntry {
|
||||
definition: def.clone(),
|
||||
state: RoaSummaryState::NotFound,
|
||||
state: AnnouncementReportState::NotFound,
|
||||
})
|
||||
}
|
||||
for def in table.matching_defs(RoaTableEntryState::RoaStale) {
|
||||
entries.push(RoaSummmaryEntry {
|
||||
for def in table.matching_defs(BgpAnalysisState::RoaStale) {
|
||||
entries.push(AnnouncementReportEntry {
|
||||
definition: def.clone(),
|
||||
state: RoaSummaryState::Stale,
|
||||
state: AnnouncementReportState::Stale,
|
||||
})
|
||||
}
|
||||
for def in table.matching_defs(RoaTableEntryState::RoaNoAnnouncementInfo) {
|
||||
entries.push(RoaSummmaryEntry {
|
||||
for def in table.matching_defs(BgpAnalysisState::RoaNoAnnouncementInfo) {
|
||||
entries.push(AnnouncementReportEntry {
|
||||
definition: def.clone(),
|
||||
state: RoaSummaryState::NoInfo,
|
||||
state: AnnouncementReportState::NoInfo,
|
||||
})
|
||||
}
|
||||
RoaSummary(entries)
|
||||
AnnouncementReport(entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RoaSummary {
|
||||
impl fmt::Display for AnnouncementReport {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for e in self.0.iter() {
|
||||
writeln!(f, "{}", e)?;
|
||||
@@ -374,22 +427,25 @@ impl fmt::Display for RoaSummary {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::commons::bgp::{RoaSummary, RoaTable};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn print_roa_table() {
|
||||
let json = include_str!("../../../test-resources/bgp/expected_roa_table.json");
|
||||
let table: RoaTable = serde_json::from_str(json).unwrap();
|
||||
fn print_bgp_report() {
|
||||
let json = include_str!("../../../test-resources/bgp/expected_bgp_analyis_report.json");
|
||||
let report: BgpAnalysisReport = serde_json::from_str(json).unwrap();
|
||||
|
||||
let expected_text = include_str!("../../../test-resources/bgp/expected_full_details.txt");
|
||||
assert_eq!(table.to_string(), expected_text);
|
||||
let expected = include_str!("../../../test-resources/bgp/expected_bgp_analysis_report.txt");
|
||||
|
||||
print!("{}", report);
|
||||
|
||||
assert_eq!(report.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_roa_table_summary() {
|
||||
let json = include_str!("../../../test-resources/bgp/expected_roa_table.json");
|
||||
let table: RoaTable = serde_json::from_str(json).unwrap();
|
||||
let summary: RoaSummary = table.into();
|
||||
let json = include_str!("../../../test-resources/bgp/expected_bgp_analyis_report.json");
|
||||
let report: BgpAnalysisReport = serde_json::from_str(json).unwrap();
|
||||
let summary: AnnouncementReport = report.into();
|
||||
|
||||
let expected_text = include_str!("../../../test-resources/bgp/expected_summary.txt");
|
||||
assert_eq!(summary.to_string(), expected_text);
|
||||
@@ -490,8 +490,8 @@ async fn api_ca_routes(req: Request, path: &mut RequestPath, ca: Handle) -> Rout
|
||||
Method::POST => ca_routes_update(req, ca).await,
|
||||
_ => render_unknown_method(),
|
||||
},
|
||||
Some("bgp") => match *req.method() {
|
||||
Method::GET => ca_routes_bgp_analysis(req, ca).await,
|
||||
Some("analysis") => match *req.method() {
|
||||
Method::GET => ca_routes_analysis(req, path, ca).await,
|
||||
_ => render_unknown_method(),
|
||||
},
|
||||
_ => render_unknown_method(),
|
||||
@@ -998,8 +998,18 @@ async fn ca_routes_show(req: Request, handle: Handle) -> RoutingResult {
|
||||
}
|
||||
|
||||
/// Show the state of ROAs vs BGP for this CA
|
||||
async fn ca_routes_bgp_analysis(req: Request, handle: Handle) -> RoutingResult {
|
||||
render_json_res(req.state().read().await.ca_routes_bgp_analysis(&handle))
|
||||
async fn ca_routes_analysis(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult {
|
||||
match path.next() {
|
||||
Some("full") => render_json_res(req.state().read().await.ca_routes_bgp_analysis(&handle)),
|
||||
Some("announcements") => render_json_res(
|
||||
req.state()
|
||||
.read()
|
||||
.await
|
||||
.ca_routes_bgp_analysis_announcements(&handle),
|
||||
),
|
||||
Some("roas") => unimplemented!(),
|
||||
_ => render_unknown_method(),
|
||||
}
|
||||
}
|
||||
|
||||
//------------ Admin: Force republish ----------------------------------------
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::commons::api::{
|
||||
RepositoryContact, RepositoryUpdate, RoaDefinition, RoaDefinitionUpdates, ServerInfo,
|
||||
TaCertDetails, UpdateChildRequest,
|
||||
};
|
||||
use crate::commons::bgp::{BgpAnalyser, RoaTable};
|
||||
use crate::commons::bgp::{AnnouncementReport, BgpAnalyser, BgpAnalysisReport};
|
||||
use crate::commons::error::Error;
|
||||
use crate::commons::eventsourcing::CommandKey;
|
||||
use crate::commons::remote::rfc8183;
|
||||
@@ -680,7 +680,7 @@ impl KrillServer {
|
||||
Ok(ca.roa_definitions())
|
||||
}
|
||||
|
||||
pub fn ca_routes_bgp_analysis(&self, handle: &Handle) -> KrillResult<RoaTable> {
|
||||
pub fn ca_routes_bgp_analysis(&self, handle: &Handle) -> KrillResult<BgpAnalysisReport> {
|
||||
let ca = self.caserver.get_ca(handle)?;
|
||||
let definitions = ca.roa_definitions();
|
||||
let resources = ca.all_resources();
|
||||
@@ -688,6 +688,14 @@ impl KrillServer {
|
||||
.bgp_analyser
|
||||
.analyse(definitions.as_slice(), &resources))
|
||||
}
|
||||
|
||||
pub fn ca_routes_bgp_analysis_announcements(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
) -> KrillResult<AnnouncementReport> {
|
||||
let full = self.ca_routes_bgp_analysis(handle)?;
|
||||
Ok(full.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// # Handle publication requests
|
||||
|
||||
+20
@@ -35,6 +35,26 @@
|
||||
"prefix": "10.0.4.0/24",
|
||||
"state": "roa_stale"
|
||||
},
|
||||
{
|
||||
"asn": 64496,
|
||||
"prefix": "10.0.0.0/22",
|
||||
"state": "announcement_valid",
|
||||
"allowed_by": {
|
||||
"asn": 64496,
|
||||
"prefix": "10.0.0.0/22",
|
||||
"max_length": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"asn": 64496,
|
||||
"prefix": "10.0.2.0/23",
|
||||
"state": "announcement_valid",
|
||||
"allowed_by": {
|
||||
"asn": 64496,
|
||||
"prefix": "10.0.0.0/22",
|
||||
"max_length": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"asn": 64496,
|
||||
"prefix": "10.0.0.0/24",
|
||||
+6
@@ -15,6 +15,11 @@ Authorizations for which no announcements are found (possibly stale):
|
||||
Definition: 10.0.3.0/24 => 64497
|
||||
Definition: 10.0.4.0/24 => 0
|
||||
|
||||
Announcements which are valid:
|
||||
|
||||
Announcement: 10.0.0.0/22 => 64496
|
||||
Announcement: 10.0.2.0/23 => 64496
|
||||
|
||||
Announcements from an unauthorized ASN:
|
||||
|
||||
Announcement: 10.0.0.0/22 => 64497
|
||||
@@ -30,5 +35,6 @@ Announcements from an authorized ASN, which are too specific (not allowed by max
|
||||
10.0.0.0/22-23 => 64496
|
||||
|
||||
Announcements which are 'not found' (not covered by any of your authorizations):
|
||||
|
||||
Announcement: 10.0.0.0/21 => 64497
|
||||
|
||||
Reference in New Issue
Block a user