297 lines
7.4 KiB
Rust
297 lines
7.4 KiB
Rust
use std::fmt::Debug;
|
|
use std::io;
|
|
use std::time::Duration;
|
|
use serde::{Deserialize, Serialize};
|
|
use crate::data_model::resources::as_resources::Asn;
|
|
use crate::data_model::resources::ip_resources::IPAddressPrefix;
|
|
use x509_parser::prelude::FromDer;
|
|
use x509_parser::x509::SubjectPublicKeyInfo;
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
|
enum PayloadPduType {
|
|
Ipv4Prefix = 4,
|
|
Ipv6Prefix = 6,
|
|
RouterKey = 9,
|
|
Aspa = 11,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
|
pub struct Ski([u8; 20]);
|
|
|
|
impl AsRef<[u8]> for Ski {
|
|
fn as_ref(&self) -> &[u8] {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl Ski {
|
|
pub fn from_bytes(bytes: [u8; 20]) -> Self {
|
|
Self(bytes)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
|
pub struct RouteOrigin {
|
|
prefix: IPAddressPrefix,
|
|
max_length: u8,
|
|
asn: Asn,
|
|
}
|
|
|
|
impl RouteOrigin {
|
|
pub fn new(prefix: IPAddressPrefix, max_length: u8, asn: Asn) -> Self {
|
|
Self {
|
|
prefix,
|
|
max_length,
|
|
asn,
|
|
}
|
|
}
|
|
|
|
pub fn prefix(&self) -> &IPAddressPrefix {
|
|
&self.prefix
|
|
}
|
|
|
|
pub fn max_length(&self) -> u8 {
|
|
self.max_length
|
|
}
|
|
|
|
pub fn asn(&self) -> Asn {
|
|
self.asn
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
|
pub struct RouterKey {
|
|
subject_key_identifier: Ski,
|
|
asn: Asn,
|
|
subject_public_key_info: Vec<u8>,
|
|
}
|
|
|
|
impl RouterKey {
|
|
pub fn new(subject_key_identifier: Ski, asn: Asn, subject_public_key_info: Vec<u8>) -> Self {
|
|
Self {
|
|
subject_key_identifier,
|
|
asn,
|
|
subject_public_key_info,
|
|
}
|
|
}
|
|
|
|
pub fn ski(&self) -> Ski {
|
|
self.subject_key_identifier
|
|
}
|
|
|
|
pub fn asn(&self) -> Asn {
|
|
self.asn
|
|
}
|
|
|
|
pub fn spki(&self) -> &[u8] {
|
|
&self.subject_public_key_info
|
|
}
|
|
|
|
pub fn validate(&self) -> Result<(), io::Error> {
|
|
if self.asn.into_u32() == 0 {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
"RouterKey ASN must not be AS0",
|
|
));
|
|
}
|
|
|
|
if self.subject_public_key_info.is_empty() {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
"RouterKey SPKI must not be empty",
|
|
));
|
|
}
|
|
|
|
let (rem, _) = SubjectPublicKeyInfo::from_der(&self.subject_public_key_info)
|
|
.map_err(|err| {
|
|
io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!("RouterKey SPKI is not valid DER: {err}"),
|
|
)
|
|
})?;
|
|
|
|
if !rem.is_empty() {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!(
|
|
"RouterKey SPKI DER has trailing bytes: {}",
|
|
rem.len()
|
|
),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
|
pub struct Aspa {
|
|
customer_asn: Asn,
|
|
provider_asns: Vec<Asn>,
|
|
}
|
|
|
|
impl Aspa {
|
|
pub fn new(customer_asn: Asn, mut provider_asns: Vec<Asn>) -> Self {
|
|
provider_asns.sort();
|
|
provider_asns.dedup();
|
|
|
|
Self {
|
|
customer_asn,
|
|
provider_asns,
|
|
}
|
|
}
|
|
|
|
pub fn customer_asn(&self) -> Asn {
|
|
self.customer_asn
|
|
}
|
|
|
|
pub fn provider_asns(&self) -> &[Asn] {
|
|
&self.provider_asns
|
|
}
|
|
|
|
pub fn validate_announcement(&self) -> Result<(), io::Error> {
|
|
if self.customer_asn.into_u32() == 0 {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
"ASPA customer ASN must not be AS0",
|
|
));
|
|
}
|
|
|
|
if self.provider_asns.is_empty() {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
"ASPA announcement must contain at least one provider ASN",
|
|
));
|
|
}
|
|
|
|
if self.provider_asns.iter().any(|asn| asn.into_u32() == 0) {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
"ASPA provider list must not contain AS0",
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
|
pub enum Payload {
|
|
/// A route origin.
|
|
RouteOrigin(RouteOrigin),
|
|
|
|
/// A BGPsec router key.
|
|
RouterKey(RouterKey),
|
|
|
|
/// An ASPA unit.
|
|
Aspa(Aspa),
|
|
}
|
|
|
|
|
|
// Timing
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct Timing {
|
|
/// The number of seconds until a client should refresh its data.
|
|
pub refresh: u32,
|
|
|
|
/// The number of seconds a client whould wait before retrying to connect.
|
|
pub retry: u32,
|
|
|
|
/// The number of secionds before data expires if not refreshed.
|
|
pub expire: u32
|
|
}
|
|
|
|
impl Timing {
|
|
pub const MIN_REFRESH: u32 = 1;
|
|
pub const MAX_REFRESH: u32 = 86_400;
|
|
pub const MIN_RETRY: u32 = 1;
|
|
pub const MAX_RETRY: u32 = 7_200;
|
|
pub const MIN_EXPIRE: u32 = 600;
|
|
pub const MAX_EXPIRE: u32 = 172_800;
|
|
|
|
pub const fn new(refresh: u32, retry: u32, expire: u32) -> Self {
|
|
Self { refresh, retry, expire }
|
|
}
|
|
|
|
pub fn validate(self) -> Result<(), io::Error> {
|
|
if !(Self::MIN_REFRESH..=Self::MAX_REFRESH).contains(&self.refresh) {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!(
|
|
"refresh interval {} out of range {}..={}",
|
|
self.refresh, Self::MIN_REFRESH, Self::MAX_REFRESH
|
|
),
|
|
));
|
|
}
|
|
|
|
if !(Self::MIN_RETRY..=Self::MAX_RETRY).contains(&self.retry) {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!(
|
|
"retry interval {} out of range {}..={}",
|
|
self.retry, Self::MIN_RETRY, Self::MAX_RETRY
|
|
),
|
|
));
|
|
}
|
|
|
|
if !(Self::MIN_EXPIRE..=Self::MAX_EXPIRE).contains(&self.expire) {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!(
|
|
"expire interval {} out of range {}..={}",
|
|
self.expire, Self::MIN_EXPIRE, Self::MAX_EXPIRE
|
|
),
|
|
));
|
|
}
|
|
|
|
if self.expire <= self.refresh {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!(
|
|
"expire interval {} must be greater than refresh interval {}",
|
|
self.expire, self.refresh
|
|
),
|
|
));
|
|
}
|
|
|
|
if self.expire <= self.retry {
|
|
return Err(io::Error::new(
|
|
io::ErrorKind::InvalidData,
|
|
format!(
|
|
"expire interval {} must be greater than retry interval {}",
|
|
self.expire, self.retry
|
|
),
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn refresh(self) -> Duration {
|
|
Duration::from_secs(u64::from(self.refresh))
|
|
}
|
|
|
|
pub fn retry(self) -> Duration {
|
|
Duration::from_secs(u64::from(self.retry))
|
|
}
|
|
|
|
pub fn expire(self) -> Duration {
|
|
Duration::from_secs(u64::from(self.expire))
|
|
}
|
|
|
|
}
|
|
|
|
impl Default for Timing {
|
|
fn default() -> Self {
|
|
Self {
|
|
refresh: 3600,
|
|
retry: 600,
|
|
expire: 7200,
|
|
}
|
|
}
|
|
}
|