315 lines
9.3 KiB
Rust
315 lines
9.3 KiB
Rust
use std::io;
|
|
|
|
use base64::Engine;
|
|
use base64::engine::general_purpose::STANDARD_NO_PAD;
|
|
use serde::Deserialize;
|
|
|
|
use crate::data_model::resources::as_resources::Asn;
|
|
use crate::rtr::payload::Ski;
|
|
use crate::slurm::file::{SlurmError, SlurmFile, SlurmVersion};
|
|
use crate::slurm::policy::{
|
|
AspaAssertion, AspaFilter, BgpsecAssertion, BgpsecFilter, LocallyAddedAssertions,
|
|
PrefixAssertion, PrefixFilter, ValidationOutputFilters, parse_ip_prefix,
|
|
};
|
|
|
|
impl SlurmFile {
|
|
pub fn from_slice(input: &[u8]) -> Result<Self, SlurmError> {
|
|
let version = serde_json::from_slice::<SlurmVersionMarker>(input)?.slurm_version;
|
|
match version {
|
|
SlurmVersion::V1_U32 => {
|
|
let raw = serde_json::from_slice::<RawSlurmFileV1>(input)?;
|
|
Self::from_raw_v1(raw)
|
|
}
|
|
SlurmVersion::V2_U32 => {
|
|
let raw = serde_json::from_slice::<RawSlurmFileV2>(input)?;
|
|
Self::from_raw_v2(raw)
|
|
}
|
|
other => Err(SlurmError::Invalid(format!(
|
|
"unsupported slurmVersion {}, expected 1 or 2",
|
|
other
|
|
))),
|
|
}
|
|
}
|
|
|
|
pub fn from_reader(mut reader: impl io::Read) -> Result<Self, SlurmError> {
|
|
let mut bytes = Vec::new();
|
|
reader.read_to_end(&mut bytes)?;
|
|
Self::from_slice(&bytes)
|
|
}
|
|
|
|
fn from_raw_v1(raw: RawSlurmFileV1) -> Result<Self, SlurmError> {
|
|
Self::new(
|
|
SlurmVersion::V1,
|
|
ValidationOutputFilters {
|
|
prefix_filters: raw.validation_output_filters.prefix_filters,
|
|
bgpsec_filters: raw.validation_output_filters.bgpsec_filters,
|
|
aspa_filters: Vec::new(),
|
|
},
|
|
LocallyAddedAssertions {
|
|
prefix_assertions: raw.locally_added_assertions.prefix_assertions,
|
|
bgpsec_assertions: raw.locally_added_assertions.bgpsec_assertions,
|
|
aspa_assertions: Vec::new(),
|
|
},
|
|
)
|
|
}
|
|
|
|
fn from_raw_v2(raw: RawSlurmFileV2) -> Result<Self, SlurmError> {
|
|
Self::new(
|
|
SlurmVersion::V2,
|
|
ValidationOutputFilters {
|
|
prefix_filters: raw.validation_output_filters.prefix_filters,
|
|
bgpsec_filters: raw.validation_output_filters.bgpsec_filters,
|
|
aspa_filters: raw.validation_output_filters.aspa_filters,
|
|
},
|
|
LocallyAddedAssertions {
|
|
prefix_assertions: raw.locally_added_assertions.prefix_assertions,
|
|
bgpsec_assertions: raw.locally_added_assertions.bgpsec_assertions,
|
|
aspa_assertions: raw.locally_added_assertions.aspa_assertions,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct SlurmVersionMarker {
|
|
#[serde(rename = "slurmVersion")]
|
|
slurm_version: u32,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawSlurmFileV1 {
|
|
#[serde(rename = "slurmVersion")]
|
|
_slurm_version: u32,
|
|
#[serde(rename = "validationOutputFilters")]
|
|
validation_output_filters: RawValidationOutputFiltersV1,
|
|
#[serde(rename = "locallyAddedAssertions")]
|
|
locally_added_assertions: RawLocallyAddedAssertionsV1,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawSlurmFileV2 {
|
|
#[serde(rename = "slurmVersion")]
|
|
_slurm_version: u32,
|
|
#[serde(rename = "validationOutputFilters")]
|
|
validation_output_filters: RawValidationOutputFiltersV2,
|
|
#[serde(rename = "locallyAddedAssertions")]
|
|
locally_added_assertions: RawLocallyAddedAssertionsV2,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawValidationOutputFiltersV1 {
|
|
#[serde(rename = "prefixFilters")]
|
|
prefix_filters: Vec<PrefixFilter>,
|
|
#[serde(rename = "bgpsecFilters")]
|
|
bgpsec_filters: Vec<BgpsecFilter>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawValidationOutputFiltersV2 {
|
|
#[serde(rename = "prefixFilters")]
|
|
prefix_filters: Vec<PrefixFilter>,
|
|
#[serde(rename = "bgpsecFilters")]
|
|
bgpsec_filters: Vec<BgpsecFilter>,
|
|
#[serde(rename = "aspaFilters")]
|
|
aspa_filters: Vec<AspaFilter>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawLocallyAddedAssertionsV1 {
|
|
#[serde(rename = "prefixAssertions")]
|
|
prefix_assertions: Vec<PrefixAssertion>,
|
|
#[serde(rename = "bgpsecAssertions")]
|
|
bgpsec_assertions: Vec<BgpsecAssertion>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawLocallyAddedAssertionsV2 {
|
|
#[serde(rename = "prefixAssertions")]
|
|
prefix_assertions: Vec<PrefixAssertion>,
|
|
#[serde(rename = "bgpsecAssertions")]
|
|
bgpsec_assertions: Vec<BgpsecAssertion>,
|
|
#[serde(rename = "aspaAssertions")]
|
|
aspa_assertions: Vec<AspaAssertion>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawPrefixFilter {
|
|
prefix: Option<String>,
|
|
asn: Option<u32>,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for PrefixFilter {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let raw = RawPrefixFilter::deserialize(deserializer)?;
|
|
Ok(Self {
|
|
prefix: raw
|
|
.prefix
|
|
.map(|prefix| parse_ip_prefix(&prefix))
|
|
.transpose()
|
|
.map_err(serde::de::Error::custom)?,
|
|
asn: raw.asn.map(Asn::from),
|
|
comment: raw.comment,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawBgpsecFilter {
|
|
asn: Option<u32>,
|
|
#[serde(rename = "SKI")]
|
|
ski: Option<String>,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for BgpsecFilter {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let raw = RawBgpsecFilter::deserialize(deserializer)?;
|
|
Ok(Self {
|
|
asn: raw.asn.map(Asn::from),
|
|
ski: raw
|
|
.ski
|
|
.map(|ski| decode_ski(&ski))
|
|
.transpose()
|
|
.map_err(serde::de::Error::custom)?,
|
|
comment: raw.comment,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawAspaFilter {
|
|
#[serde(rename = "customerAsn")]
|
|
customer_asn: u32,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for AspaFilter {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let raw = RawAspaFilter::deserialize(deserializer)?;
|
|
Ok(Self {
|
|
customer_asn: Asn::from(raw.customer_asn),
|
|
comment: raw.comment,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawPrefixAssertion {
|
|
prefix: String,
|
|
asn: u32,
|
|
#[serde(rename = "maxPrefixLength")]
|
|
max_prefix_length: Option<u8>,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for PrefixAssertion {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let raw = RawPrefixAssertion::deserialize(deserializer)?;
|
|
Ok(Self {
|
|
prefix: parse_ip_prefix(&raw.prefix).map_err(serde::de::Error::custom)?,
|
|
asn: Asn::from(raw.asn),
|
|
max_prefix_length: raw.max_prefix_length,
|
|
comment: raw.comment,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawBgpsecAssertion {
|
|
asn: u32,
|
|
#[serde(rename = "SKI")]
|
|
ski: String,
|
|
#[serde(rename = "routerPublicKey")]
|
|
router_public_key: String,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for BgpsecAssertion {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let raw = RawBgpsecAssertion::deserialize(deserializer)?;
|
|
Ok(Self {
|
|
asn: Asn::from(raw.asn),
|
|
ski: decode_ski(&raw.ski).map_err(serde::de::Error::custom)?,
|
|
router_public_key: decode_router_public_key(&raw.router_public_key)
|
|
.map_err(serde::de::Error::custom)?,
|
|
comment: raw.comment,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct RawAspaAssertion {
|
|
#[serde(rename = "customerAsn")]
|
|
customer_asn: u32,
|
|
#[serde(rename = "providerAsns")]
|
|
provider_asns: Vec<u32>,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for AspaAssertion {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let raw = RawAspaAssertion::deserialize(deserializer)?;
|
|
Ok(Self {
|
|
customer_asn: Asn::from(raw.customer_asn),
|
|
provider_asns: raw.provider_asns.into_iter().map(Asn::from).collect(),
|
|
comment: raw.comment,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn decode_ski(input: &str) -> Result<Ski, SlurmError> {
|
|
let bytes = STANDARD_NO_PAD
|
|
.decode(input)
|
|
.map_err(|err| SlurmError::Invalid(format!("invalid SKI base64 '{}': {}", input, err)))?;
|
|
if bytes.len() != 20 {
|
|
return Err(SlurmError::Invalid(format!(
|
|
"SKI must be exactly 20 bytes, got {}",
|
|
bytes.len()
|
|
)));
|
|
}
|
|
|
|
let mut ski = [0u8; 20];
|
|
ski.copy_from_slice(&bytes);
|
|
Ok(Ski::from_bytes(ski))
|
|
}
|
|
|
|
fn decode_router_public_key(input: &str) -> Result<Vec<u8>, SlurmError> {
|
|
STANDARD_NO_PAD.decode(input).map_err(|err| {
|
|
SlurmError::Invalid(format!(
|
|
"invalid routerPublicKey base64 '{}': {}",
|
|
input, err
|
|
))
|
|
})
|
|
}
|