1430 lines
42 KiB
Rust
1430 lines
42 KiB
Rust
mod common;
|
|
|
|
use std::collections::VecDeque;
|
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
use std::sync::Arc;
|
|
|
|
use common::test_helper::{
|
|
as_route_origin, as_v4_route_origin, indent_block, payloads_to_string,
|
|
serial_result_detail_to_string, snapshot_hashes_to_string, test_report, v4_origin, v6_origin,
|
|
};
|
|
|
|
use rpki::data_model::resources::as_resources::Asn;
|
|
use rpki::rtr::cache::{
|
|
CacheAvailability, Delta, RtrCacheBuilder, SerialResult, SessionIds, Snapshot,
|
|
validate_payload_updates_for_rtr, validate_payloads_for_rtr,
|
|
};
|
|
use rpki::rtr::payload::{Aspa, Payload, RouterKey, Ski, Timing};
|
|
use rpki::rtr::store::RtrStore;
|
|
|
|
fn delta_to_string(delta: &Delta) -> String {
|
|
format!(
|
|
"serial: {}\nannounced:\n{}withdrawn:\n{}",
|
|
delta.serial(),
|
|
indent_block(&payloads_to_string(delta.announced()), 2),
|
|
indent_block(&payloads_to_string(delta.withdrawn()), 2),
|
|
)
|
|
}
|
|
|
|
fn deltas_window_to_string(deltas: &VecDeque<Arc<Delta>>) -> String {
|
|
if deltas.is_empty() {
|
|
return " <empty>\n".to_string();
|
|
}
|
|
|
|
let mut out = String::new();
|
|
for (idx, delta) in deltas.iter().enumerate() {
|
|
out.push_str(&format!("delta[{}]:\n", idx));
|
|
out.push_str(&indent_block(&delta_to_string(delta), 2));
|
|
}
|
|
out
|
|
}
|
|
|
|
fn get_deltas_since_input_to_string(
|
|
cache_session_id: u16,
|
|
cache_serial: u32,
|
|
client_serial: u32,
|
|
) -> String {
|
|
format!(
|
|
"cache.session_id: {}\ncache.serial: {}\nclient_serial: {}\n",
|
|
cache_session_id, cache_serial, client_serial
|
|
)
|
|
}
|
|
|
|
fn snapshot_hashes_and_sorted_view_to_string(snapshot: &Snapshot) -> String {
|
|
let payloads = snapshot.payloads_for_rtr();
|
|
format!(
|
|
"hashes:\n{}sorted payloads_for_rtr:\n{}",
|
|
indent_block(&snapshot_hashes_to_string(snapshot), 2),
|
|
indent_block(&payloads_to_string(&payloads), 2),
|
|
)
|
|
}
|
|
|
|
/// Snapshot ?hash ?
|
|
/// payload snapshot_hash / origins_hash ?
|
|
#[test]
|
|
fn snapshot_hash_is_stable_for_same_content_with_different_input_order() {
|
|
let a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
let c = v6_origin(
|
|
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
|
|
32,
|
|
48,
|
|
64498,
|
|
);
|
|
|
|
let s1_input = vec![
|
|
Payload::RouteOrigin(a.clone()),
|
|
Payload::RouteOrigin(b.clone()),
|
|
Payload::RouteOrigin(c.clone()),
|
|
];
|
|
let s2_input = vec![
|
|
Payload::RouteOrigin(c),
|
|
Payload::RouteOrigin(a),
|
|
Payload::RouteOrigin(b),
|
|
];
|
|
|
|
let s1 = Snapshot::from_payloads(s1_input.clone());
|
|
let s2 = Snapshot::from_payloads(s2_input.clone());
|
|
|
|
let input = format!(
|
|
"s1 payloads:\n{}\ns2 payloads:\n{}",
|
|
indent_block(&payloads_to_string(&s1_input), 2),
|
|
indent_block(&payloads_to_string(&s2_input), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"s1:\n{}\ns2:\n{}\n:\n same_content: {}\n same_origins: {}\n snapshot_hash : {}\n origins_hash : {}\n",
|
|
indent_block(&snapshot_hashes_and_sorted_view_to_string(&s1), 2),
|
|
indent_block(&snapshot_hashes_and_sorted_view_to_string(&s2), 2),
|
|
s1.same_content(&s2),
|
|
s1.same_origins(&s2),
|
|
s1.snapshot_hash() == s2.snapshot_hash(),
|
|
s1.origins_hash() == s2.origins_hash(),
|
|
);
|
|
|
|
test_report(
|
|
"snapshot_hash_is_stable_for_same_content_with_different_input_order",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert!(s1.same_content(&s2));
|
|
assert!(s1.same_origins(&s2));
|
|
assert_eq!(s1.snapshot_hash(), s2.snapshot_hash());
|
|
assert_eq!(s1.origins_hash(), s2.origins_hash());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn init_keeps_cache_running_when_file_loader_returns_no_data() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
|
|
let cache = rpki::rtr::cache::RtrCache::default()
|
|
.init(&store, 16, false, Timing::new(600, 600, 7200), || {
|
|
Ok(vec![])
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(!cache.is_data_available());
|
|
assert_eq!(cache.serial(), 0);
|
|
assert!(cache.snapshot().payloads_for_rtr().is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn init_restores_wraparound_delta_window_from_store() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
let session_ids = SessionIds::from_array([42, 43, 44]);
|
|
let snapshot = Snapshot::from_payloads(vec![
|
|
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
|
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
|
Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498)),
|
|
]);
|
|
|
|
let d_max = Delta::new(
|
|
u32::MAX,
|
|
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
|
vec![],
|
|
);
|
|
let d_zero = Delta::new(
|
|
0,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
198, 51, 100, 0, 24, 24, 64497,
|
|
))],
|
|
vec![],
|
|
);
|
|
let d_one = Delta::new(
|
|
1,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
203, 0, 113, 0, 24, 24, 64498,
|
|
))],
|
|
vec![],
|
|
);
|
|
|
|
store
|
|
.save_cache_state(
|
|
CacheAvailability::Ready,
|
|
&snapshot,
|
|
&session_ids,
|
|
u32::MAX,
|
|
Some(&d_max),
|
|
Some((u32::MAX, u32::MAX)),
|
|
false,
|
|
)
|
|
.unwrap();
|
|
store
|
|
.save_cache_state(
|
|
CacheAvailability::Ready,
|
|
&snapshot,
|
|
&session_ids,
|
|
0,
|
|
Some(&d_zero),
|
|
Some((u32::MAX, 0)),
|
|
false,
|
|
)
|
|
.unwrap();
|
|
store
|
|
.save_cache_state(
|
|
CacheAvailability::Ready,
|
|
&snapshot,
|
|
&session_ids,
|
|
1,
|
|
Some(&d_one),
|
|
Some((u32::MAX, 1)),
|
|
false,
|
|
)
|
|
.unwrap();
|
|
|
|
let cache = rpki::rtr::cache::RtrCache::default()
|
|
.init(&store, 16, false, Timing::new(600, 600, 7200), || {
|
|
Ok(Vec::new())
|
|
})
|
|
.unwrap();
|
|
|
|
match cache.get_deltas_since(u32::MAX.wrapping_sub(1)) {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 1);
|
|
assert_eq!(delta.announced().len(), 3);
|
|
}
|
|
_ => panic!("expected wraparound delta to be restored from store"),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn update_prunes_delta_window_when_cumulative_delta_size_reaches_snapshot_size() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
let valid_spki = vec![
|
|
0x30, 0x13, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
|
|
0x05, 0x00, 0x03, 0x02, 0x00, 0x00,
|
|
];
|
|
|
|
let initial_snapshot = Snapshot::from_payloads(vec![Payload::RouterKey(RouterKey::new(
|
|
Ski::default(),
|
|
Asn::from(64496u32),
|
|
valid_spki.clone(),
|
|
))]);
|
|
|
|
let mut cache = RtrCacheBuilder::new()
|
|
.availability(CacheAvailability::Ready)
|
|
.session_ids(SessionIds::from_array([42, 43, 44]))
|
|
.serial(1)
|
|
.snapshot(initial_snapshot)
|
|
.max_delta(16)
|
|
.prune_delta_by_snapshot_size(true)
|
|
.timing(Timing::new(600, 600, 7200))
|
|
.build();
|
|
|
|
cache
|
|
.update(
|
|
vec![Payload::RouterKey(RouterKey::new(
|
|
Ski::from_bytes([1u8; 20]),
|
|
Asn::from(64496u32),
|
|
valid_spki,
|
|
))],
|
|
&store,
|
|
)
|
|
.unwrap();
|
|
|
|
match cache.get_deltas_since(1) {
|
|
SerialResult::ResetRequired => {}
|
|
_ => panic!(
|
|
"expected delta window to be pruned when cumulative delta size exceeds snapshot size"
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Snapshot::diff() ?
|
|
/// old_snapshot ?new_snapshot announced?withdrawn?
|
|
#[test]
|
|
fn snapshot_diff_reports_announced_and_withdrawn_correctly() {
|
|
let old_a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let old_b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
let new_c = v6_origin(
|
|
Ipv6Addr::new(0x2001, 0xdb8, 0, 1, 0, 0, 0, 0),
|
|
48,
|
|
48,
|
|
64499,
|
|
);
|
|
|
|
let old_input = vec![
|
|
Payload::RouteOrigin(old_a.clone()),
|
|
Payload::RouteOrigin(old_b.clone()),
|
|
];
|
|
let new_input = vec![
|
|
Payload::RouteOrigin(old_b),
|
|
Payload::RouteOrigin(new_c.clone()),
|
|
];
|
|
|
|
let old_snapshot = Snapshot::from_payloads(old_input.clone());
|
|
let new_snapshot = Snapshot::from_payloads(new_input.clone());
|
|
|
|
let (announced, withdrawn) = old_snapshot.diff(&new_snapshot);
|
|
|
|
let input = format!(
|
|
"old_snapshot :\n{}\nnew_snapshot :\n{}",
|
|
indent_block(&payloads_to_string(&old_input), 2),
|
|
indent_block(&payloads_to_string(&new_input), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"announced:\n{}withdrawn:\n{}",
|
|
indent_block(&payloads_to_string(&announced), 2),
|
|
indent_block(&payloads_to_string(&withdrawn), 2),
|
|
);
|
|
|
|
test_report(
|
|
"snapshot_diff_reports_announced_and_withdrawn_correctly",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(announced.len(), 1);
|
|
assert_eq!(withdrawn.len(), 1);
|
|
|
|
match &announced[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &new_c),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
|
|
match &withdrawn[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &old_a),
|
|
_ => panic!("expected withdrawn RouteOrigin"),
|
|
}
|
|
}
|
|
|
|
/// Snapshot::payloads_for_rtr() ?
|
|
/// IPv4 Prefix IPv6 Prefix ?IPv4 announcement ?RTR ?
|
|
#[test]
|
|
fn snapshot_payloads_for_rtr_sorts_ipv4_before_ipv6_and_ipv4_announcements_descending() {
|
|
let v4_low = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let v4_high = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
let v6 = v6_origin(
|
|
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
|
|
32,
|
|
48,
|
|
64498,
|
|
);
|
|
|
|
let input_payloads = vec![
|
|
Payload::RouteOrigin(v6.clone()),
|
|
Payload::RouteOrigin(v4_low.clone()),
|
|
Payload::RouteOrigin(v4_high.clone()),
|
|
];
|
|
|
|
let snapshot = Snapshot::from_payloads(input_payloads.clone());
|
|
let output_payloads = snapshot.payloads_for_rtr();
|
|
|
|
let input = format!(
|
|
" payloads?Snapshot :\n{}",
|
|
indent_block(&payloads_to_string(&input_payloads), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"?payloads_for_rtr:\n{}",
|
|
indent_block(&payloads_to_string(&output_payloads), 2),
|
|
);
|
|
|
|
test_report(
|
|
"snapshot_payloads_for_rtr_sorts_ipv4_before_ipv6_and_ipv4_announcements_descending",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(output_payloads.len(), 3);
|
|
|
|
let first = as_v4_route_origin(&output_payloads[0]);
|
|
let second = as_v4_route_origin(&output_payloads[1]);
|
|
|
|
assert_eq!(
|
|
first.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(198, 51, 100, 0))
|
|
);
|
|
assert_eq!(
|
|
second.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(192, 0, 2, 0))
|
|
);
|
|
|
|
let third = as_route_origin(&output_payloads[2]);
|
|
assert!(third.prefix().address.is_ipv6());
|
|
assert_eq!(
|
|
third.prefix().address.to_ipv6(),
|
|
Some(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0))
|
|
);
|
|
}
|
|
|
|
/// Delta::new() ?announced ?withdrawn?
|
|
/// announced announcement ithdrawn withdrawal ?
|
|
#[test]
|
|
fn delta_new_sorts_announced_descending_and_withdrawn_ascending() {
|
|
let announced_low = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let announced_high = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
let withdrawn_high = v4_origin(203, 0, 113, 0, 24, 24, 64501);
|
|
let withdrawn_low = v4_origin(10, 0, 0, 0, 24, 24, 64500);
|
|
|
|
let input_announced = vec![
|
|
Payload::RouteOrigin(announced_low),
|
|
Payload::RouteOrigin(announced_high),
|
|
];
|
|
let input_withdrawn = vec![
|
|
Payload::RouteOrigin(withdrawn_high),
|
|
Payload::RouteOrigin(withdrawn_low),
|
|
];
|
|
|
|
let delta = Delta::new(101, input_announced.clone(), input_withdrawn.clone());
|
|
|
|
let input = format!(
|
|
"announced?\n{}withdrawn?\n{}",
|
|
indent_block(&payloads_to_string(&input_announced), 2),
|
|
indent_block(&payloads_to_string(&input_withdrawn), 2),
|
|
);
|
|
|
|
let output = indent_block(&delta_to_string(&delta), 2);
|
|
|
|
test_report(
|
|
"delta_new_sorts_announced_descending_and_withdrawn_ascending",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(delta.serial(), 101);
|
|
assert_eq!(delta.announced().len(), 2);
|
|
assert_eq!(delta.withdrawn().len(), 2);
|
|
|
|
let a0 = as_v4_route_origin(&delta.announced()[0]);
|
|
let a1 = as_v4_route_origin(&delta.announced()[1]);
|
|
assert_eq!(
|
|
a0.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(198, 51, 100, 0))
|
|
);
|
|
assert_eq!(
|
|
a1.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(192, 0, 2, 0))
|
|
);
|
|
|
|
let w0 = as_v4_route_origin(&delta.withdrawn()[0]);
|
|
let w1 = as_v4_route_origin(&delta.withdrawn()[1]);
|
|
assert_eq!(
|
|
w0.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(10, 0, 0, 0))
|
|
);
|
|
assert_eq!(
|
|
w1.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(203, 0, 113, 0))
|
|
);
|
|
}
|
|
|
|
/// serial ?serial
|
|
/// get_deltas_since() ?UpToDate?Delta ?ResetRequired?
|
|
#[test]
|
|
fn get_deltas_since_returns_up_to_date_when_client_serial_matches_current() {
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(100)
|
|
.timing(Timing::default())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input =
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_returns_up_to_date_when_client_serial_matches_current",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::UpToDate => {}
|
|
_ => panic!("expected UpToDate"),
|
|
}
|
|
}
|
|
|
|
/// serial delta window ?/// get_deltas_since() ?ResetRequired?#[test]
|
|
fn get_deltas_since_returns_reset_required_when_client_serial_is_too_old() {
|
|
let d1 = Arc::new(Delta::new(
|
|
101,
|
|
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
|
vec![],
|
|
));
|
|
let d2 = Arc::new(Delta::new(
|
|
102,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
198, 51, 100, 0, 24, 24, 64497,
|
|
))],
|
|
vec![],
|
|
));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(102)
|
|
.timing(Timing::default())
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(99);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 99),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_returns_reset_required_when_client_serial_is_too_old",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::ResetRequired => {}
|
|
_ => panic!("expected ResetRequired"),
|
|
}
|
|
}
|
|
|
|
/// serial delta window ?
|
|
/// get_deltas_since() ?delta ?
|
|
#[test]
|
|
fn get_deltas_since_returns_minimal_merged_delta() {
|
|
let d1 = Arc::new(Delta::new(
|
|
101,
|
|
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
|
vec![],
|
|
));
|
|
let d2 = Arc::new(Delta::new(
|
|
102,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
198, 51, 100, 0, 24, 24, 64497,
|
|
))],
|
|
vec![],
|
|
));
|
|
let d3 = Arc::new(Delta::new(
|
|
103,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
203, 0, 113, 0, 24, 24, 64498,
|
|
))],
|
|
vec![],
|
|
));
|
|
|
|
let final_snapshot = Snapshot::from_payloads(vec![
|
|
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
|
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
|
Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498)),
|
|
]);
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
deltas.push_back(d3);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(103)
|
|
.timing(Timing::default())
|
|
.snapshot(final_snapshot)
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(101);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 101),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_returns_minimal_merged_delta",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 103);
|
|
assert_eq!(delta.announced().len(), 2);
|
|
assert_eq!(delta.withdrawn().len(), 0);
|
|
|
|
let a0 = as_v4_route_origin(&delta.announced()[0]);
|
|
let a1 = as_v4_route_origin(&delta.announced()[1]);
|
|
|
|
assert_eq!(
|
|
a0.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(203, 0, 113, 0))
|
|
);
|
|
assert_eq!(
|
|
a1.prefix().address.to_ipv4(),
|
|
Some(Ipv4Addr::new(198, 51, 100, 0))
|
|
);
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
/// serial serial
|
|
/// ResetRequired?
|
|
#[test]
|
|
fn get_deltas_since_returns_reset_required_when_client_serial_is_in_future() {
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(100)
|
|
.timing(Timing::default())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(101);
|
|
|
|
let input =
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 101);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_returns_reset_required_when_client_serial_is_in_future",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::ResetRequired => {}
|
|
_ => panic!("expected ResetRequired"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_deltas_since_supports_incremental_updates_across_serial_wraparound() {
|
|
let a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
|
|
let d_max = Arc::new(Delta::new(
|
|
u32::MAX,
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
vec![],
|
|
));
|
|
let d_zero = Arc::new(Delta::new(0, vec![Payload::RouteOrigin(b.clone())], vec![]));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d_max);
|
|
deltas.push_back(d_zero);
|
|
|
|
let final_snapshot = Snapshot::from_payloads(vec![
|
|
Payload::RouteOrigin(a.clone()),
|
|
Payload::RouteOrigin(b.clone()),
|
|
]);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(0)
|
|
.timing(Timing::default())
|
|
.snapshot(final_snapshot)
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(u32::MAX.wrapping_sub(1));
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(
|
|
cache.session_id_for_version(1),
|
|
cache.serial(),
|
|
u32::MAX.wrapping_sub(1)
|
|
),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_supports_incremental_updates_across_serial_wraparound",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 0);
|
|
assert_eq!(delta.announced().len(), 2);
|
|
assert_eq!(delta.withdrawn().len(), 0);
|
|
|
|
match &delta.announced()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &b),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
|
|
match &delta.announced()[1] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &a),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_deltas_since_returns_reset_required_when_client_serial_is_too_old_across_wraparound() {
|
|
let d_max = Arc::new(Delta::new(
|
|
u32::MAX,
|
|
vec![Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496))],
|
|
vec![],
|
|
));
|
|
let d_zero = Arc::new(Delta::new(
|
|
0,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
198, 51, 100, 0, 24, 24, 64497,
|
|
))],
|
|
vec![],
|
|
));
|
|
let d_one = Arc::new(Delta::new(
|
|
1,
|
|
vec![Payload::RouteOrigin(v4_origin(
|
|
203, 0, 113, 0, 24, 24, 64498,
|
|
))],
|
|
vec![],
|
|
));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d_max);
|
|
deltas.push_back(d_zero);
|
|
deltas.push_back(d_one);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(1)
|
|
.timing(Timing::default())
|
|
.snapshot(Snapshot::from_payloads(vec![
|
|
Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496)),
|
|
Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497)),
|
|
Payload::RouteOrigin(v4_origin(203, 0, 113, 0, 24, 24, 64498)),
|
|
]))
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let client_serial = u32::MAX.wrapping_sub(2);
|
|
let result = cache.get_deltas_since(client_serial);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(
|
|
cache.session_id_for_version(1),
|
|
cache.serial(),
|
|
client_serial
|
|
),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_returns_reset_required_when_client_serial_is_too_old_across_wraparound",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::ResetRequired => {}
|
|
_ => panic!("expected ResetRequired"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_deltas_since_returns_reset_required_when_client_serial_is_in_future_across_wraparound() {
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(u32::MAX)
|
|
.timing(Timing::default())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(0);
|
|
|
|
let input =
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 0);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_returns_reset_required_when_client_serial_is_in_future_across_wraparound",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::ResetRequired => {}
|
|
_ => panic!("expected ResetRequired"),
|
|
}
|
|
}
|
|
|
|
/// update() ?payload ?
|
|
/// serial delta?
|
|
#[tokio::test]
|
|
async fn update_no_change_keeps_serial_and_produces_no_delta() {
|
|
let old_a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let old_b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
|
|
let old_input = vec![
|
|
Payload::RouteOrigin(old_a.clone()),
|
|
Payload::RouteOrigin(old_b.clone()),
|
|
];
|
|
let snapshot = Snapshot::from_payloads(old_input.clone());
|
|
|
|
let mut cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(100)
|
|
.timing(Timing::default())
|
|
.snapshot(snapshot.clone())
|
|
.build();
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
|
|
let new_payloads = vec![Payload::RouteOrigin(old_b), Payload::RouteOrigin(old_a)];
|
|
|
|
cache.update(new_payloads.clone(), &store).unwrap();
|
|
|
|
let current_snapshot = cache.snapshot();
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"old_snapshot :\n{}new_payloads :\n{}",
|
|
indent_block(&payloads_to_string(&old_input), 2),
|
|
indent_block(&payloads_to_string(&new_payloads), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
|
cache.serial(),
|
|
indent_block(
|
|
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
|
2
|
|
),
|
|
indent_block(&serial_result_detail_to_string(&result), 2),
|
|
);
|
|
|
|
test_report(
|
|
"update_no_change_keeps_serial_and_produces_no_delta",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(cache.serial(), 100);
|
|
assert!(cache.snapshot().same_content(&snapshot));
|
|
|
|
match result {
|
|
SerialResult::UpToDate => {}
|
|
_ => panic!("expected UpToDate"),
|
|
}
|
|
}
|
|
|
|
/// update() payload ?
|
|
/// ?serial?announced ?
|
|
#[tokio::test]
|
|
async fn update_add_only_increments_serial_and_generates_announced_delta() {
|
|
let old_a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let new_b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
|
|
let old_input = vec![Payload::RouteOrigin(old_a.clone())];
|
|
let old_snapshot = Snapshot::from_payloads(old_input.clone());
|
|
|
|
let mut cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(100)
|
|
.timing(Timing::default())
|
|
.snapshot(old_snapshot.clone())
|
|
.build();
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
|
|
let new_payloads = vec![
|
|
Payload::RouteOrigin(old_a.clone()),
|
|
Payload::RouteOrigin(new_b.clone()),
|
|
];
|
|
|
|
cache.update(new_payloads.clone(), &store).unwrap();
|
|
|
|
let current_snapshot = cache.snapshot();
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"old_snapshot :\n{}new_payloads :\n{}",
|
|
indent_block(&payloads_to_string(&old_input), 2),
|
|
indent_block(&payloads_to_string(&new_payloads), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
|
cache.serial(),
|
|
indent_block(
|
|
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
|
2
|
|
),
|
|
indent_block(&serial_result_detail_to_string(&result), 2),
|
|
);
|
|
|
|
test_report(
|
|
"update_add_only_increments_serial_and_generates_announced_delta",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(cache.serial(), 101);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 101);
|
|
assert_eq!(delta.announced().len(), 1);
|
|
assert_eq!(delta.withdrawn().len(), 0);
|
|
|
|
match &delta.announced()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &new_b),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
/// update() payload ?
|
|
/// ?serial?withdrawn ?
|
|
#[tokio::test]
|
|
async fn update_remove_only_increments_serial_and_generates_withdrawn_delta() {
|
|
let old_a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let old_b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
|
|
let old_input = vec![
|
|
Payload::RouteOrigin(old_a.clone()),
|
|
Payload::RouteOrigin(old_b.clone()),
|
|
];
|
|
let old_snapshot = Snapshot::from_payloads(old_input.clone());
|
|
|
|
let mut cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(100)
|
|
.timing(Timing::default())
|
|
.snapshot(old_snapshot.clone())
|
|
.build();
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
|
|
let new_payloads = vec![Payload::RouteOrigin(old_b.clone())];
|
|
|
|
cache.update(new_payloads.clone(), &store).unwrap();
|
|
|
|
let current_snapshot = cache.snapshot();
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"old_snapshot :\n{}new_payloads :\n{}",
|
|
indent_block(&payloads_to_string(&old_input), 2),
|
|
indent_block(&payloads_to_string(&new_payloads), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
|
cache.serial(),
|
|
indent_block(
|
|
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
|
2
|
|
),
|
|
indent_block(&serial_result_detail_to_string(&result), 2),
|
|
);
|
|
|
|
test_report(
|
|
"update_remove_only_increments_serial_and_generates_withdrawn_delta",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(cache.serial(), 101);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 101);
|
|
assert_eq!(delta.announced().len(), 0);
|
|
assert_eq!(delta.withdrawn().len(), 1);
|
|
|
|
match &delta.withdrawn()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &old_a),
|
|
_ => panic!("expected withdrawn RouteOrigin"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
/// update() payload
|
|
/// ?serial announced ?withdrawn?
|
|
#[tokio::test]
|
|
async fn update_add_and_remove_increments_serial_and_generates_both_sides() {
|
|
let old_a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let old_b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
let new_c = v6_origin(
|
|
Ipv6Addr::new(0x2001, 0xdb8, 0, 1, 0, 0, 0, 0),
|
|
48,
|
|
48,
|
|
64499,
|
|
);
|
|
|
|
let old_input = vec![
|
|
Payload::RouteOrigin(old_a.clone()),
|
|
Payload::RouteOrigin(old_b.clone()),
|
|
];
|
|
let old_snapshot = Snapshot::from_payloads(old_input.clone());
|
|
|
|
let mut cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(100)
|
|
.timing(Timing::default())
|
|
.snapshot(old_snapshot.clone())
|
|
.build();
|
|
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let store = RtrStore::open(dir.path()).unwrap();
|
|
|
|
let new_payloads = vec![
|
|
Payload::RouteOrigin(old_b.clone()),
|
|
Payload::RouteOrigin(new_c.clone()),
|
|
];
|
|
|
|
cache.update(new_payloads.clone(), &store).unwrap();
|
|
|
|
let current_snapshot = cache.snapshot();
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"old_snapshot :\n{}new_payloads :\n{}",
|
|
indent_block(&payloads_to_string(&old_input), 2),
|
|
indent_block(&payloads_to_string(&new_payloads), 2),
|
|
);
|
|
|
|
let output = format!(
|
|
"cache.serial_after_update: {}\ncurrent_snapshot:\n{}get_deltas_since(100):\n{}",
|
|
cache.serial(),
|
|
indent_block(
|
|
&snapshot_hashes_and_sorted_view_to_string(¤t_snapshot),
|
|
2
|
|
),
|
|
indent_block(&serial_result_detail_to_string(&result), 2),
|
|
);
|
|
|
|
test_report(
|
|
"update_add_and_remove_increments_serial_and_generates_both_sides",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
assert_eq!(cache.serial(), 101);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 101);
|
|
assert_eq!(delta.announced().len(), 1);
|
|
assert_eq!(delta.withdrawn().len(), 1);
|
|
|
|
match &delta.announced()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &new_c),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
|
|
match &delta.withdrawn()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &old_a),
|
|
_ => panic!("expected withdrawn RouteOrigin"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
/// ?prefix announce withdraw
|
|
/// ?UpToDate?
|
|
#[test]
|
|
fn get_deltas_since_cancels_announce_then_withdraw_for_same_prefix() {
|
|
let a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
|
|
let d1 = Arc::new(Delta::new(
|
|
101,
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
vec![],
|
|
));
|
|
let d2 = Arc::new(Delta::new(
|
|
102,
|
|
vec![],
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
|
|
// A ?
|
|
let final_snapshot = Snapshot::empty();
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(102)
|
|
.timing(Timing::default())
|
|
.snapshot(final_snapshot)
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_cancels_announce_then_withdraw_for_same_prefix",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::UpToDate => {}
|
|
_ => panic!("expected UpToDate"),
|
|
}
|
|
}
|
|
|
|
/// ?prefix withdraw announce ?
|
|
/// ?UpToDate?
|
|
#[test]
|
|
fn get_deltas_since_cancels_withdraw_then_announce_for_same_prefix() {
|
|
let a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
|
|
let d1 = Arc::new(Delta::new(
|
|
101,
|
|
vec![],
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
));
|
|
let d2 = Arc::new(Delta::new(
|
|
102,
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
vec![],
|
|
));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
|
|
// ?A?
|
|
let final_snapshot = Snapshot::from_payloads(vec![Payload::RouteOrigin(a.clone())]);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(102)
|
|
.timing(Timing::default())
|
|
.snapshot(final_snapshot)
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_cancels_withdraw_then_announce_for_same_prefix",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::UpToDate => {}
|
|
_ => panic!("expected UpToDate"),
|
|
}
|
|
}
|
|
|
|
/// ?A ?B
|
|
/// ?delta?withdraw A + announce B?
|
|
#[test]
|
|
fn get_deltas_since_merges_replacement_into_withdraw_and_announce() {
|
|
let a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let b = v4_origin(192, 0, 2, 0, 24, 25, 64496);
|
|
|
|
let d1 = Arc::new(Delta::new(
|
|
101,
|
|
vec![],
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
));
|
|
let d2 = Arc::new(Delta::new(
|
|
102,
|
|
vec![Payload::RouteOrigin(b.clone())],
|
|
vec![],
|
|
));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
|
|
let final_snapshot = Snapshot::from_payloads(vec![Payload::RouteOrigin(b.clone())]);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(102)
|
|
.timing(Timing::default())
|
|
.snapshot(final_snapshot)
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_merges_replacement_into_withdraw_and_announce",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 102);
|
|
assert_eq!(delta.announced().len(), 1);
|
|
assert_eq!(delta.withdrawn().len(), 1);
|
|
|
|
match &delta.withdrawn()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &a),
|
|
_ => panic!("expected withdrawn RouteOrigin"),
|
|
}
|
|
|
|
match &delta.announced()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &b),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
/// delta ?
|
|
/// ?
|
|
#[test]
|
|
fn get_deltas_since_merges_multiple_deltas_to_final_minimal_view() {
|
|
let a = v4_origin(192, 0, 2, 0, 24, 24, 64496);
|
|
let b = v4_origin(198, 51, 100, 0, 24, 24, 64497);
|
|
let c = v4_origin(203, 0, 113, 0, 24, 24, 64498);
|
|
|
|
// 100 -> 101 : +A
|
|
let d1 = Arc::new(Delta::new(
|
|
101,
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
vec![],
|
|
));
|
|
// 101 -> 102 : -A +B
|
|
let d2 = Arc::new(Delta::new(
|
|
102,
|
|
vec![Payload::RouteOrigin(b.clone())],
|
|
vec![Payload::RouteOrigin(a.clone())],
|
|
));
|
|
// 102 -> 103 : -B +C
|
|
let d3 = Arc::new(Delta::new(
|
|
103,
|
|
vec![Payload::RouteOrigin(c.clone())],
|
|
vec![Payload::RouteOrigin(b.clone())],
|
|
));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
deltas.push_back(d3);
|
|
|
|
let final_snapshot = Snapshot::from_payloads(vec![Payload::RouteOrigin(c.clone())]);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(103)
|
|
.timing(Timing::default())
|
|
.snapshot(final_snapshot)
|
|
.deltas(deltas.clone())
|
|
.build();
|
|
|
|
// ?serial=100 A/B ?+C
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
let input = format!(
|
|
"{}delta_window:\n{}",
|
|
get_deltas_since_input_to_string(cache.session_id_for_version(1), cache.serial(), 100),
|
|
indent_block(&deltas_window_to_string(&deltas), 2),
|
|
);
|
|
let output = serial_result_detail_to_string(&result);
|
|
|
|
test_report(
|
|
"get_deltas_since_merges_multiple_deltas_to_final_minimal_view",
|
|
"test purpose",
|
|
&input,
|
|
&output,
|
|
);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.serial(), 103);
|
|
assert_eq!(delta.announced().len(), 1);
|
|
assert_eq!(delta.withdrawn().len(), 0);
|
|
|
|
match &delta.announced()[0] {
|
|
Payload::RouteOrigin(ro) => assert_eq!(ro, &c),
|
|
_ => panic!("expected announced RouteOrigin"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn snapshot_from_payloads_unions_aspas_by_customer() {
|
|
let first = Payload::Aspa(Aspa::new(Asn::from(64496u32), vec![Asn::from(64497u32)]));
|
|
let second = Payload::Aspa(Aspa::new(
|
|
Asn::from(64496u32),
|
|
vec![Asn::from(64498u32), Asn::from(64497u32)],
|
|
));
|
|
|
|
let snapshot = Snapshot::from_payloads(vec![first, second]);
|
|
let aspas = snapshot.aspas().iter().collect::<Vec<_>>();
|
|
|
|
assert_eq!(aspas.len(), 1);
|
|
assert_eq!(aspas[0].customer_asn(), Asn::from(64496u32));
|
|
assert_eq!(
|
|
aspas[0].provider_asns(),
|
|
&[Asn::from(64497u32), Asn::from(64498u32)]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn snapshot_diff_replaces_aspa_with_single_announcement() {
|
|
let old_snapshot = Snapshot::from_payloads(vec![Payload::Aspa(Aspa::new(
|
|
Asn::from(64496u32),
|
|
vec![Asn::from(64497u32)],
|
|
))]);
|
|
let new_snapshot = Snapshot::from_payloads(vec![Payload::Aspa(Aspa::new(
|
|
Asn::from(64496u32),
|
|
vec![Asn::from(64498u32)],
|
|
))]);
|
|
|
|
let (announced, withdrawn) = old_snapshot.diff(&new_snapshot);
|
|
|
|
assert_eq!(announced.len(), 1);
|
|
assert!(withdrawn.is_empty());
|
|
|
|
match &announced[0] {
|
|
Payload::Aspa(aspa) => {
|
|
assert_eq!(aspa.customer_asn(), Asn::from(64496u32));
|
|
assert_eq!(aspa.provider_asns(), &[Asn::from(64498u32)]);
|
|
}
|
|
_ => panic!("expected announced ASPA"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_deltas_since_merges_aspa_replacement_into_single_announcement() {
|
|
let old = Aspa::new(Asn::from(64496u32), vec![Asn::from(64497u32)]);
|
|
let new = Aspa::new(Asn::from(64496u32), vec![Asn::from(64498u32)]);
|
|
|
|
let d1 = Arc::new(Delta::new(101, vec![], vec![Payload::Aspa(old.clone())]));
|
|
let d2 = Arc::new(Delta::new(102, vec![Payload::Aspa(new.clone())], vec![]));
|
|
|
|
let mut deltas = VecDeque::new();
|
|
deltas.push_back(d1);
|
|
deltas.push_back(d2);
|
|
|
|
let cache = RtrCacheBuilder::new()
|
|
.session_ids(SessionIds::from_array([42, 42, 42]))
|
|
.serial(102)
|
|
.timing(Timing::default())
|
|
.snapshot(Snapshot::from_payloads(vec![Payload::Aspa(new.clone())]))
|
|
.deltas(deltas)
|
|
.build();
|
|
|
|
let result = cache.get_deltas_since(100);
|
|
|
|
match result {
|
|
SerialResult::Delta(delta) => {
|
|
assert_eq!(delta.announced().len(), 1);
|
|
assert!(delta.withdrawn().is_empty());
|
|
|
|
match &delta.announced()[0] {
|
|
Payload::Aspa(aspa) => assert_eq!(aspa, &new),
|
|
_ => panic!("expected announced ASPA"),
|
|
}
|
|
}
|
|
_ => panic!("expected Delta"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn validate_payloads_for_rtr_rejects_unsorted_snapshot_payloads() {
|
|
let low = Payload::RouteOrigin(v4_origin(192, 0, 2, 0, 24, 24, 64496));
|
|
let high = Payload::RouteOrigin(v4_origin(198, 51, 100, 0, 24, 24, 64497));
|
|
|
|
let err = validate_payloads_for_rtr(&[low, high], true).unwrap_err();
|
|
assert!(err.to_string().contains("RTR payload ordering violation"));
|
|
}
|
|
|
|
#[test]
|
|
fn validate_payload_updates_for_rtr_rejects_unsorted_aspa_updates() {
|
|
let withdraw = (
|
|
false,
|
|
Payload::Aspa(Aspa::new(Asn::from(64497u32), vec![Asn::from(64500u32)])),
|
|
);
|
|
let announce = (
|
|
true,
|
|
Payload::Aspa(Aspa::new(Asn::from(64496u32), vec![Asn::from(64499u32)])),
|
|
);
|
|
|
|
let err = validate_payload_updates_for_rtr(&[withdraw, announce]).unwrap_err();
|
|
assert!(err.to_string().contains("withdraw ASPA"));
|
|
assert!(err.to_string().contains("announce ASPA"));
|
|
}
|