rpki/src/slurm/serde.rs
2026-04-10 14:29:40 +08:00

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
))
})
}