///|
/// An RFC 8416 prefix filter for validated ROA payloads.
pub struct SlurmPrefixFilter {
prefix : Ipv4Prefix?
ipv6_prefix : Ipv6Prefix?
asn : UInt?
comment : String
} derive(Eq, Debug)
///|
pub fn SlurmPrefixFilter::by_prefix(
prefix : Ipv4Prefix,
comment? : String = "",
) -> SlurmPrefixFilter {
{ prefix: Some(prefix), ipv6_prefix: None, asn: None, comment, }
}
///|
pub fn SlurmPrefixFilter::by_asn(
asn : UInt,
comment? : String = "",
) -> SlurmPrefixFilter {
{ prefix: None, ipv6_prefix: None, asn: Some(asn), comment, }
}
///|
pub fn SlurmPrefixFilter::by_prefix_and_asn(
prefix : Ipv4Prefix,
asn : UInt,
comment? : String = "",
) -> SlurmPrefixFilter {
{ prefix: Some(prefix), ipv6_prefix: None, asn: Some(asn), comment, }
}
///|
pub fn SlurmPrefixFilter::by_ipv6_prefix(
prefix : Ipv6Prefix,
comment? : String = "",
) -> SlurmPrefixFilter {
{ prefix: None, ipv6_prefix: Some(prefix), asn: None, comment, }
}
///|
pub fn SlurmPrefixFilter::by_ipv6_prefix_and_asn(
prefix : Ipv6Prefix,
asn : UInt,
comment? : String = "",
) -> SlurmPrefixFilter {
{ prefix: None, ipv6_prefix: Some(prefix), asn: Some(asn), comment, }
}
///|
pub fn SlurmPrefixFilter::prefix(self : SlurmPrefixFilter) -> Ipv4Prefix? {
self.prefix
}
///|
pub fn SlurmPrefixFilter::ipv6_prefix(self : SlurmPrefixFilter) -> Ipv6Prefix? {
self.ipv6_prefix
}
///|
pub fn SlurmPrefixFilter::asn(self : SlurmPrefixFilter) -> UInt? {
self.asn
}
///|
pub fn SlurmPrefixFilter::comment(self : SlurmPrefixFilter) -> String {
self.comment
}
///|
fn SlurmPrefixFilter::matches(self : SlurmPrefixFilter, vrp : Vrp) -> Bool {
if self.ipv6_prefix is Some(_) {
return false
}
let prefix_matches = match self.prefix {
Some(prefix) => prefix.covers(vrp.prefix())
None => true
}
let asn_matches = match self.asn {
Some(asn) => asn == vrp.asn()
None => true
}
prefix_matches && asn_matches
}
///|
fn SlurmPrefixFilter::matches_ipv6(
self : SlurmPrefixFilter,
vrp : Ipv6Vrp,
) -> Bool {
if self.prefix is Some(_) {
return false
}
let prefix_matches = match self.ipv6_prefix {
Some(prefix) => prefix.covers(vrp.prefix())
None => true
}
let asn_matches = match self.asn {
Some(asn) => asn == vrp.asn()
None => true
}
prefix_matches && asn_matches
}
///|
/// Create a local SLURM assertion. An omitted maximum length defaults to the
/// asserted prefix length as required by RFC 8416.
pub fn slurm_assertion(
prefix : Ipv4Prefix,
asn : UInt,
max_length? : Int,
) -> Result[Vrp, String] {
Vrp::new(prefix, max_length.unwrap_or(prefix.length()), asn)
}
///|
/// Create an IPv6 local assertion; the default maximum length is the prefix
/// length, just as it is for IPv4 assertions.
pub fn ipv6_slurm_assertion(
prefix : Ipv6Prefix,
asn : UInt,
max_length? : Int,
) -> Result[Ipv6Vrp, String] {
Ipv6Vrp::new(prefix, max_length.unwrap_or(prefix.length()), asn)
}
///|
pub(all) struct SlurmApplyResult {
vrps : Array[Vrp]
filtered : Int
duplicate_assertions : Int
} derive(Eq, Debug)
///|
pub fn SlurmApplyResult::summary(self : SlurmApplyResult) -> String {
"\{self.vrps.length()} VRP(s), \{self.filtered} filtered, \{self.duplicate_assertions} duplicate assertion(s)"
}
///|
pub(all) struct Ipv6SlurmApplyResult {
vrps : Array[Ipv6Vrp]
filtered : Int
duplicate_assertions : Int
} derive(Eq, Debug)
///|
pub fn Ipv6SlurmApplyResult::summary(self : Ipv6SlurmApplyResult) -> String {
"\{self.vrps.length()} VRP(s), \{self.filtered} filtered, \{self.duplicate_assertions} duplicate assertion(s)"
}
///|
struct SlurmVrpKey {
address : UInt
prefix_length : Int
max_length : Int
asn : UInt
} derive(Eq, Hash)
///|
fn Vrp::slurm_key(self : Vrp) -> SlurmVrpKey {
{
address: self.prefix().address,
prefix_length: self.prefix().length(),
max_length: self.max_length(),
asn: self.asn(),
}
}
///|
struct Ipv6SlurmVrpKey {
prefix : Ipv6Prefix
max_length : Int
asn : UInt
} derive(Eq, Hash)
///|
fn Ipv6Vrp::slurm_key(self : Ipv6Vrp) -> Ipv6SlurmVrpKey {
{ prefix: self.prefix(), max_length: self.max_length(), asn: self.asn(), }
}
///|
/// Apply RFC 8416 IPv4 prefix filters and local assertions atomically.
///
/// Filters are applied only to the validated input. Assertions are appended
/// afterwards and exact duplicates are omitted.
pub fn apply_slurm(
validated : Array[Vrp],
filters : Array[SlurmPrefixFilter],
assertions : Array[Vrp],
) -> SlurmApplyResult {
let output : Array[Vrp] = []
let seen : Map[SlurmVrpKey, Unit] = Map(
[],
capacity=validated.length() + assertions.length(),
)
let mut filtered = 0
for vrp in validated {
let mut rejected = false
for filter in filters {
if filter.matches(vrp) {
rejected = true
break
}
}
if rejected {
filtered = filtered + 1
} else {
let key = vrp.slurm_key()
if !seen.contains(key) {
seen.set(key, ())
output.push(vrp)
}
}
}
let mut duplicate_assertions = 0
for assertion in assertions {
let key = assertion.slurm_key()
if seen.contains(key) {
duplicate_assertions = duplicate_assertions + 1
} else {
seen.set(key, ())
output.push(assertion)
}
}
{ vrps: output, filtered, duplicate_assertions, }
}
///|
/// Apply RFC 8416 IPv6 filters to validated output, then add local assertions.
/// ASN-only filters apply to both address families; IPv4 prefix filters do not
/// match IPv6 VRPs.
pub fn apply_ipv6_slurm(
validated : Array[Ipv6Vrp],
filters : Array[SlurmPrefixFilter],
assertions : Array[Ipv6Vrp],
) -> Ipv6SlurmApplyResult {
let output : Array[Ipv6Vrp] = []
let seen : Map[Ipv6SlurmVrpKey, Unit] = Map(
[],
capacity=validated.length() + assertions.length(),
)
let mut filtered = 0
for vrp in validated {
let mut rejected = false
for filter in filters {
if filter.matches_ipv6(vrp) {
rejected = true
break
}
}
if rejected {
filtered = filtered + 1
} else {
let key = vrp.slurm_key()
if !seen.contains(key) {
seen.set(key, ())
output.push(vrp)
}
}
}
let mut duplicate_assertions = 0
for assertion in assertions {
let key = assertion.slurm_key()
if seen.contains(key) {
duplicate_assertions = duplicate_assertions + 1
} else {
seen.set(key, ())
output.push(assertion)
}
}
{ vrps: output, filtered, duplicate_assertions, }
}