补充rtr-client,完善tls连接等
This commit is contained in:
311
src/rtr/cache/ordering.rs
vendored
Normal file
311
src/rtr/cache/ordering.rs
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
use crate::data_model::resources::ip_resources::IPAddress;
|
||||
use crate::rtr::payload::{Aspa, Payload, RouteOrigin, RouterKey, Ski};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct OrderingViolation {
|
||||
index: usize,
|
||||
left: String,
|
||||
right: String,
|
||||
}
|
||||
|
||||
impl OrderingViolation {
|
||||
fn new(index: usize, left: &Payload, right: &Payload) -> Self {
|
||||
Self {
|
||||
index,
|
||||
left: payload_brief(left),
|
||||
right: payload_brief(right),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_update(index: usize, left: (bool, &Payload), right: (bool, &Payload)) -> Self {
|
||||
Self {
|
||||
index,
|
||||
left: payload_update_brief(left.0, left.1),
|
||||
right: payload_update_brief(right.0, right.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OrderingViolation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"RTR payload ordering violation at positions {} and {}: {} should not appear before {}",
|
||||
self.index,
|
||||
self.index + 1,
|
||||
self.left,
|
||||
self.right
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) enum RouterKeyKey {
|
||||
Key { ski: Ski, spki: Vec<u8>, asn: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) enum ChangeKey {
|
||||
RouteOrigin(RouteOriginKey),
|
||||
RouterKey(RouterKeyKey),
|
||||
AspaCustomer(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
enum PayloadPduType {
|
||||
Ipv4Prefix = 4,
|
||||
Ipv6Prefix = 6,
|
||||
RouterKey = 9,
|
||||
Aspa = 11,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) enum RouteOriginKey {
|
||||
V4 { addr: u32, plen: u8, mlen: u8, asn: u32 },
|
||||
V6 { addr: u128, plen: u8, mlen: u8, asn: u32 },
|
||||
}
|
||||
|
||||
pub(crate) fn change_key(payload: &Payload) -> ChangeKey {
|
||||
match payload {
|
||||
Payload::RouteOrigin(ro) => ChangeKey::RouteOrigin(route_origin_key(ro)),
|
||||
Payload::RouterKey(rk) => ChangeKey::RouterKey(router_key_key(rk)),
|
||||
Payload::Aspa(aspa) => ChangeKey::AspaCustomer(aspa.customer_asn().into_u32()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compare_payload_update_for_rtr(
|
||||
a_payload: &Payload,
|
||||
a_announce: bool,
|
||||
b_payload: &Payload,
|
||||
b_announce: bool,
|
||||
) -> Ordering {
|
||||
let type_a = payload_pdu_type(a_payload);
|
||||
let type_b = payload_pdu_type(b_payload);
|
||||
|
||||
match type_a.cmp(&type_b) {
|
||||
Ordering::Equal => {}
|
||||
other => return other,
|
||||
}
|
||||
|
||||
match b_announce.cmp(&a_announce) {
|
||||
Ordering::Equal => {}
|
||||
other => return other,
|
||||
}
|
||||
|
||||
match (a_payload, b_payload) {
|
||||
(Payload::RouteOrigin(a), Payload::RouteOrigin(b)) => {
|
||||
compare_route_origin_for_rtr(a, b, a_announce)
|
||||
}
|
||||
(Payload::RouterKey(a), Payload::RouterKey(b)) => compare_router_key_for_rtr(a, b),
|
||||
(Payload::Aspa(a), Payload::Aspa(b)) => compare_aspa_for_rtr(a, b),
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sort_payloads_for_rtr(payloads: &mut [Payload], announce: bool) {
|
||||
payloads.sort_by(|a, b| compare_payload_for_rtr(a, b, announce));
|
||||
}
|
||||
|
||||
pub fn validate_payloads_for_rtr(
|
||||
payloads: &[Payload],
|
||||
announce: bool,
|
||||
) -> Result<(), OrderingViolation> {
|
||||
for (index, pair) in payloads.windows(2).enumerate() {
|
||||
if compare_payload_for_rtr(&pair[0], &pair[1], announce) == Ordering::Greater {
|
||||
return Err(OrderingViolation::new(index, &pair[0], &pair[1]));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_payload_updates_for_rtr(
|
||||
updates: &[(bool, Payload)],
|
||||
) -> Result<(), OrderingViolation> {
|
||||
for (index, pair) in updates.windows(2).enumerate() {
|
||||
if compare_payload_update_for_rtr(&pair[0].1, pair[0].0, &pair[1].1, pair[1].0)
|
||||
== Ordering::Greater
|
||||
{
|
||||
return Err(OrderingViolation::new_update(
|
||||
index,
|
||||
(pair[0].0, &pair[0].1),
|
||||
(pair[1].0, &pair[1].1),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn router_key_key(rk: &RouterKey) -> RouterKeyKey {
|
||||
RouterKeyKey::Key {
|
||||
ski: rk.ski(),
|
||||
spki: rk.spki().as_ref().to_vec(),
|
||||
asn: rk.asn().into_u32(),
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_payload_for_rtr(a: &Payload, b: &Payload, announce: bool) -> Ordering {
|
||||
let type_a = payload_pdu_type(a);
|
||||
let type_b = payload_pdu_type(b);
|
||||
|
||||
match type_a.cmp(&type_b) {
|
||||
Ordering::Equal => {}
|
||||
other => return other,
|
||||
}
|
||||
|
||||
match (a, b) {
|
||||
(Payload::RouteOrigin(a), Payload::RouteOrigin(b)) => {
|
||||
compare_route_origin_for_rtr(a, b, announce)
|
||||
}
|
||||
(Payload::RouterKey(a), Payload::RouterKey(b)) => compare_router_key_for_rtr(a, b),
|
||||
(Payload::Aspa(a), Payload::Aspa(b)) => compare_aspa_for_rtr(a, b),
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
fn payload_pdu_type(payload: &Payload) -> PayloadPduType {
|
||||
match payload {
|
||||
Payload::RouteOrigin(ro) => {
|
||||
if route_origin_is_ipv4(ro) {
|
||||
PayloadPduType::Ipv4Prefix
|
||||
} else {
|
||||
PayloadPduType::Ipv6Prefix
|
||||
}
|
||||
}
|
||||
Payload::RouterKey(_) => PayloadPduType::RouterKey,
|
||||
Payload::Aspa(_) => PayloadPduType::Aspa,
|
||||
}
|
||||
}
|
||||
|
||||
fn route_origin_is_ipv4(ro: &RouteOrigin) -> bool {
|
||||
ro.prefix().address.is_ipv4()
|
||||
}
|
||||
|
||||
fn route_origin_key(ro: &RouteOrigin) -> RouteOriginKey {
|
||||
let prefix = ro.prefix();
|
||||
let plen = prefix.prefix_length;
|
||||
let mlen = ro.max_length();
|
||||
let asn = ro.asn().into_u32();
|
||||
|
||||
match prefix.address {
|
||||
IPAddress::V4(addr) => RouteOriginKey::V4 {
|
||||
addr: u32::from(addr),
|
||||
plen,
|
||||
mlen,
|
||||
asn,
|
||||
},
|
||||
IPAddress::V6(addr) => RouteOriginKey::V6 {
|
||||
addr: u128::from(addr),
|
||||
plen,
|
||||
mlen,
|
||||
asn,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_route_origin_for_rtr(a: &RouteOrigin, b: &RouteOrigin, announce: bool) -> Ordering {
|
||||
match (route_origin_key(a), route_origin_key(b)) {
|
||||
(
|
||||
RouteOriginKey::V4 {
|
||||
addr: addr_a,
|
||||
plen: plen_a,
|
||||
mlen: mlen_a,
|
||||
asn: asn_a,
|
||||
},
|
||||
RouteOriginKey::V4 {
|
||||
addr: addr_b,
|
||||
plen: plen_b,
|
||||
mlen: mlen_b,
|
||||
asn: asn_b,
|
||||
},
|
||||
) => {
|
||||
if announce {
|
||||
addr_b
|
||||
.cmp(&addr_a)
|
||||
.then_with(|| mlen_b.cmp(&mlen_a))
|
||||
.then_with(|| plen_b.cmp(&plen_a))
|
||||
.then_with(|| asn_b.cmp(&asn_a))
|
||||
} else {
|
||||
addr_a
|
||||
.cmp(&addr_b)
|
||||
.then_with(|| mlen_a.cmp(&mlen_b))
|
||||
.then_with(|| plen_a.cmp(&plen_b))
|
||||
.then_with(|| asn_a.cmp(&asn_b))
|
||||
}
|
||||
}
|
||||
(
|
||||
RouteOriginKey::V6 {
|
||||
addr: addr_a,
|
||||
plen: plen_a,
|
||||
mlen: mlen_a,
|
||||
asn: asn_a,
|
||||
},
|
||||
RouteOriginKey::V6 {
|
||||
addr: addr_b,
|
||||
plen: plen_b,
|
||||
mlen: mlen_b,
|
||||
asn: asn_b,
|
||||
},
|
||||
) => {
|
||||
if announce {
|
||||
addr_b
|
||||
.cmp(&addr_a)
|
||||
.then_with(|| mlen_b.cmp(&mlen_a))
|
||||
.then_with(|| plen_b.cmp(&plen_a))
|
||||
.then_with(|| asn_b.cmp(&asn_a))
|
||||
} else {
|
||||
addr_a
|
||||
.cmp(&addr_b)
|
||||
.then_with(|| mlen_a.cmp(&mlen_b))
|
||||
.then_with(|| plen_a.cmp(&plen_b))
|
||||
.then_with(|| asn_a.cmp(&asn_b))
|
||||
}
|
||||
}
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_router_key_for_rtr(a: &RouterKey, b: &RouterKey) -> Ordering {
|
||||
a.ski()
|
||||
.cmp(&b.ski())
|
||||
.then_with(|| a.spki().len().cmp(&b.spki().len()))
|
||||
.then_with(|| a.spki().cmp(b.spki()))
|
||||
.then_with(|| a.asn().into_u32().cmp(&b.asn().into_u32()))
|
||||
}
|
||||
|
||||
fn compare_aspa_for_rtr(a: &Aspa, b: &Aspa) -> Ordering {
|
||||
a.customer_asn()
|
||||
.into_u32()
|
||||
.cmp(&b.customer_asn().into_u32())
|
||||
}
|
||||
|
||||
fn payload_brief(payload: &Payload) -> String {
|
||||
match payload {
|
||||
Payload::RouteOrigin(origin) => format!(
|
||||
"{} prefix {:?}/{} max={} asn={}",
|
||||
if route_origin_is_ipv4(origin) { "IPv4" } else { "IPv6" },
|
||||
origin.prefix().address,
|
||||
origin.prefix().prefix_length,
|
||||
origin.max_length(),
|
||||
origin.asn().into_u32()
|
||||
),
|
||||
Payload::RouterKey(key) => format!(
|
||||
"RouterKey ski={:02x?} asn={}",
|
||||
key.ski().as_ref(),
|
||||
key.asn().into_u32()
|
||||
),
|
||||
Payload::Aspa(aspa) => format!("ASPA customer_asn={}", aspa.customer_asn().into_u32()),
|
||||
}
|
||||
}
|
||||
|
||||
fn payload_update_brief(announce: bool, payload: &Payload) -> String {
|
||||
format!(
|
||||
"{} {}",
|
||||
if announce { "announce" } else { "withdraw" },
|
||||
payload_brief(payload)
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user