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 { let version = serde_json::from_slice::(input)?.slurm_version; match version { SlurmVersion::V1_U32 => { let raw = serde_json::from_slice::(input)?; Self::from_raw_v1(raw) } SlurmVersion::V2_U32 => { let raw = serde_json::from_slice::(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 { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes)?; Self::from_slice(&bytes) } fn from_raw_v1(raw: RawSlurmFileV1) -> Result { 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::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, #[serde(rename = "bgpsecFilters")] bgpsec_filters: Vec, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] struct RawValidationOutputFiltersV2 { #[serde(rename = "prefixFilters")] prefix_filters: Vec, #[serde(rename = "bgpsecFilters")] bgpsec_filters: Vec, #[serde(rename = "aspaFilters")] aspa_filters: Vec, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] struct RawLocallyAddedAssertionsV1 { #[serde(rename = "prefixAssertions")] prefix_assertions: Vec, #[serde(rename = "bgpsecAssertions")] bgpsec_assertions: Vec, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] struct RawLocallyAddedAssertionsV2 { #[serde(rename = "prefixAssertions")] prefix_assertions: Vec, #[serde(rename = "bgpsecAssertions")] bgpsec_assertions: Vec, #[serde(rename = "aspaAssertions")] aspa_assertions: Vec, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] struct RawPrefixFilter { prefix: Option, asn: Option, comment: Option, } impl<'de> Deserialize<'de> for PrefixFilter { fn deserialize(deserializer: D) -> Result 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, #[serde(rename = "SKI")] ski: Option, comment: Option, } impl<'de> Deserialize<'de> for BgpsecFilter { fn deserialize(deserializer: D) -> Result 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, } impl<'de> Deserialize<'de> for AspaFilter { fn deserialize(deserializer: D) -> Result 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, comment: Option, } impl<'de> Deserialize<'de> for PrefixAssertion { fn deserialize(deserializer: D) -> Result 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, } impl<'de> Deserialize<'de> for BgpsecAssertion { fn deserialize(deserializer: D) -> Result 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, comment: Option, } impl<'de> Deserialize<'de> for AspaAssertion { fn deserialize(deserializer: D) -> Result 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 { 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, SlurmError> { STANDARD_NO_PAD.decode(input).map_err(|err| { SlurmError::Invalid(format!( "invalid routerPublicKey base64 '{}': {}", input, err )) }) }