use std::{collections::HashSet, sync::RwLock};
use inetnum::{addr::Prefix, asn::Asn};
use log::warn;
use rotonda_store::{match_options::{IncludeHistory, MatchOptions, MatchType}, rib::{config::MemoryOnlyConfig, StarCastRib}};
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[derive(Serialize)]
pub struct RpkiInfo {
rov: RovStatus,
}
impl RpkiInfo {
pub fn rov_status(&self) -> RovStatus {
self.rov
}
}
impl From<u8> for RpkiInfo {
fn from(value: u8) -> Self {
let rov = match value {
1 => RovStatus::NotFound,
2 => RovStatus::Valid,
4 => RovStatus::Invalid,
_ => RovStatus::NotChecked
};
Self { rov }
}
}
impl From<RpkiInfo> for u8 {
fn from(value: RpkiInfo) -> Self {
match value.rov {
RovStatus::NotChecked => 0,
RovStatus::NotFound => 1,
RovStatus::Valid => 2,
RovStatus::Invalid => 4,
}
}
}
impl From<RovStatus> for RpkiInfo {
fn from(value: RovStatus) -> Self {
Self { rov: value }
}
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum RovStatus {
#[default]
NotChecked,
NotFound,
Valid,
Invalid,
}
impl std::fmt::Display for RovStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RovStatus::NotChecked => write!(f, "not checked"),
RovStatus::NotFound => write!(f, "NotFound"),
RovStatus::Valid => write!(f, "Valid"),
RovStatus::Invalid => write!(f, "Invalid"),
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct RovStatusUpdate {
pub prefix: Prefix,
pub previous_status: RovStatus,
pub current_status: RovStatus,
pub origin: Asn,
pub peer_asn: Asn,
}
impl RovStatusUpdate {
pub fn new(
prefix: Prefix,
previous_status: RovStatus,
current_status: RovStatus,
origin: Asn,
peer_asn: Asn,
) -> Self {
Self {
prefix, previous_status, current_status, origin, peer_asn,
}
}
}
type VrpStore = StarCastRib<MaxLenList, MemoryOnlyConfig>;
#[derive(Clone, Debug, Default)]
pub struct MaxLenList {
list: Vec<u8>,
}
impl MaxLenList {
pub fn push(&mut self, value: u8) {
self.list.push(value);
}
pub fn remove(&mut self, index: usize) {
self.list.remove(index);
}
pub fn iter(&self) -> std::slice::Iter<'_, u8> {
self.list.iter()
}
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
}
impl AsRef<[u8]> for MaxLenList {
fn as_ref(&self) -> &[u8] {
self.list.as_ref()
}
}
impl From<Vec<u8>> for MaxLenList {
fn from(value: Vec<u8>) -> Self {
Self { list: value }
}
}
impl std::fmt::Display for MaxLenList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.list.iter()).finish()
}
}
impl rotonda_store::prefix_record::Meta for MaxLenList {
type Orderable<'a> = ();
type TBI = ();
fn as_orderable(&self, _tbi: Self::TBI) -> Self::Orderable<'_> {
todo!()
}
}
pub struct RtrCache {
pub route_origins: RwLock<HashSet<rpki::rtr::payload::RouteOrigin>>,
pub router_keys: RwLock<HashSet<rpki::rtr::payload::RouterKey>>,
pub aspas: RwLock<HashSet<rpki::rtr::payload::Aspa>>,
pub vrps: VrpStore,
}
impl RtrCache {
pub fn check_rov(&self, prefix: &Prefix, origin: Asn) -> RovStatus {
#[allow(unused_assignments)]
let mut covered = false;
let mut valid = false;
let match_options = MatchOptions {
match_type: MatchType::LongestMatch,
include_withdrawn: false,
include_less_specifics: true,
include_more_specifics: false,
mui: None,
include_history: IncludeHistory::None,
};
let guard = &rotonda_store::epoch::pin();
let res = match self.vrps.match_prefix(
prefix,
&match_options,
guard
) {
Ok(res) => res,
Err(e) => {
warn!("could not lookup {}: {}", &prefix, e);
return RovStatus::NotChecked;
}
};
'outer: for r in &res.records {
covered = covered || !r.meta.is_empty();
for maxlen in r.meta.iter() {
#[allow(clippy::collapsible_if)]
if prefix.len() <= *maxlen {
if r.multi_uniq_id == u32::from(origin) {
valid = true;
break 'outer;
}
}
}
}
if !valid {
if let Some(less_specifics) = res.less_specifics {
'outer: for r in less_specifics.iter() {
for record in r.meta.iter() {
covered = covered || !record.meta.is_empty();
for maxlen in record.meta.iter() {
#[allow(clippy::collapsible_if)]
if prefix.len() <= *maxlen {
if record.multi_uniq_id == u32::from(origin) {
valid = true;
break 'outer;
}
}
}
}
}
}
}
match (covered, valid) {
(true, true) => RovStatus::Valid,
(true, false) => RovStatus::Invalid,
(false, true) => unreachable!(),
(false, false) => RovStatus::NotFound,
}
}
}
impl Default for RtrCache {
fn default() -> Self {
Self {
route_origins: Default::default(),
router_keys: Default::default(),
aspas: Default::default(),
vrps: VrpStore::try_default().unwrap(),
}
}
}