完善routerkey和aspa功能

This commit is contained in:
xiuting.xu
2026-03-26 10:04:09 +08:00
parent 03c0ab0ec7
commit 7432fbcac4
20 changed files with 1156 additions and 204 deletions

123
tests/test_loader.rs Normal file
View File

@@ -0,0 +1,123 @@
use std::net::IpAddr;
use std::str::FromStr;
use rpki::rtr::loader::{
parse_aspa_line, parse_router_key_line, parse_vrp_line, ParsedAspa, ParsedVrp,
};
#[test]
fn parse_ipv4_vrp_line() {
let got = parse_vrp_line("10.0.0.0/24,24,65001").unwrap();
assert_eq!(
got,
ParsedVrp {
prefix_addr: IpAddr::from_str("10.0.0.0").unwrap(),
prefix_len: 24,
max_len: 24,
asn: 65001,
}
);
}
#[test]
fn parse_ipv6_vrp_line() {
let got = parse_vrp_line("2001:db8::/32,48,65003").unwrap();
assert_eq!(
got,
ParsedVrp {
prefix_addr: IpAddr::from_str("2001:db8::").unwrap(),
prefix_len: 32,
max_len: 48,
asn: 65003,
}
);
}
#[test]
fn parse_rejects_invalid_max_len() {
let err = parse_vrp_line("10.0.0.0/24,16,65001").unwrap_err();
assert!(err.to_string().contains("max_len"));
}
#[test]
fn parse_rejects_invalid_ip() {
let err = parse_vrp_line("10.0.0.999/24,24,65001").unwrap_err();
assert!(err.to_string().contains("invalid IP"));
}
#[test]
fn parse_rejects_invalid_format() {
let err = parse_vrp_line("10.0.0.0/24,24").unwrap_err();
assert!(err.to_string().contains("expected format"));
}
#[test]
fn parse_aspa_line_ok() {
let got = parse_aspa_line("64496,64497 64498").unwrap();
assert_eq!(
got,
ParsedAspa {
customer_asn: 64496,
provider_asns: vec![64497, 64498],
}
);
}
#[test]
fn parse_aspa_rejects_empty_provider_list() {
let err = parse_aspa_line("64496,").unwrap_err();
assert!(err.to_string().contains("provider list"));
}
#[test]
fn parse_aspa_rejects_as0() {
let err = parse_aspa_line("0,64497").unwrap_err();
assert!(err.to_string().contains("AS0"));
let err = parse_aspa_line("64496,0").unwrap_err();
assert!(err.to_string().contains("AS0"));
}
#[test]
fn parse_router_key_line_ok() {
let got = parse_router_key_line(
"00112233445566778899aabbccddeeff00112233,64496,3013300d06092a864886f70d010101050003020000",
)
.unwrap();
assert_eq!(got.asn, 64496);
assert_eq!(
got.ski,
[
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33,
]
);
assert_eq!(
got.spki,
vec![
0x30, 0x13, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x01, 0x05, 0x00, 0x03, 0x02, 0x00, 0x00,
]
);
}
#[test]
fn parse_router_key_rejects_invalid_ski_length() {
let err = parse_router_key_line("0011,64496,deadbeef").unwrap_err();
assert!(err.to_string().contains("SKI"));
}
#[test]
fn parse_router_key_rejects_empty_spki() {
let err =
parse_router_key_line("00112233445566778899aabbccddeeff00112233,64496,").unwrap_err();
assert!(err.to_string().contains("SPKI"));
}
#[test]
fn parse_router_key_rejects_invalid_spki_der() {
let err =
parse_router_key_line("00112233445566778899aabbccddeeff00112233,64496,deadbeef")
.unwrap_err();
assert!(err.to_string().contains("valid DER"));
}

View File

@@ -113,6 +113,22 @@ async fn router_key_length_matches_wire_size() {
assert_eq!(header.length(), 8 + 20 + 4 + 32);
}
#[tokio::test]
async fn router_key_read_rejects_reserved_zero_octet() {
let (mut client, mut server) = duplex(1024);
let mut bytes = vec![1, RouterKey::PDU, 1, 1];
bytes.extend_from_slice(&(8u32 + 20 + 4 + 4).to_be_bytes());
bytes.extend_from_slice(&[0u8; 20]);
bytes.extend_from_slice(&64496u32.to_be_bytes());
bytes.extend_from_slice(&[1, 2, 3, 4]);
client.write_all(&bytes).await.unwrap();
let err = RouterKey::read(&mut server).await.unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert!(err.to_string().contains("zero octet"));
}
#[tokio::test]
async fn aspa_length_matches_wire_size() {
let pdu = Aspa::new(2, Flags::new(1), 64496, vec![64497, 64498]);
@@ -127,6 +143,37 @@ async fn aspa_length_matches_wire_size() {
assert_eq!(header.length(), 8 + 4 + 8);
}
#[tokio::test]
async fn aspa_read_rejects_unsorted_provider_list() {
let (mut client, mut server) = duplex(1024);
let mut bytes = vec![2, Aspa::PDU, 1, 0];
bytes.extend_from_slice(&(20u32).to_be_bytes());
bytes.extend_from_slice(&64496u32.to_be_bytes());
bytes.extend_from_slice(&64498u32.to_be_bytes());
bytes.extend_from_slice(&64497u32.to_be_bytes());
client.write_all(&bytes).await.unwrap();
let err = Aspa::read(&mut server).await.unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert!(err.to_string().contains("strictly increasing"));
}
#[tokio::test]
async fn aspa_read_rejects_withdraw_with_providers() {
let (mut client, mut server) = duplex(1024);
let mut bytes = vec![2, Aspa::PDU, 0, 0];
bytes.extend_from_slice(&(16u32).to_be_bytes());
bytes.extend_from_slice(&64496u32.to_be_bytes());
bytes.extend_from_slice(&64497u32.to_be_bytes());
client.write_all(&bytes).await.unwrap();
let err = Aspa::read(&mut server).await.unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert!(err.to_string().contains("withdrawal"));
}
#[test]
fn aspa_announcement_rejects_empty_provider_list() {
let err = PayloadAspa::new(Asn::from(64496u32), vec![])
@@ -136,6 +183,19 @@ fn aspa_announcement_rejects_empty_provider_list() {
assert!(err.to_string().contains("at least one provider"));
}
#[test]
fn router_key_payload_rejects_invalid_spki() {
let err = rpki::rtr::payload::RouterKey::new(
Ski::default(),
Asn::from(64496u32),
vec![0xde, 0xad, 0xbe, 0xef],
)
.validate()
.unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert!(err.to_string().contains("valid DER"));
}
#[test]
fn aspa_announcement_rejects_as0() {
let err = PayloadAspa::new(Asn::from(0u32), vec![Asn::from(64497u32)])

View File

@@ -1780,3 +1780,41 @@ async fn invalid_aspa_prevents_snapshot_response() {
let err = join.expect_err("session should fail on invalid ASPA");
assert!(err.to_string().contains("ASPA announcement"));
}
#[tokio::test]
async fn invalid_router_key_prevents_snapshot_response() {
let snapshot = Snapshot::from_payloads(vec![Payload::RouterKey(RouterKey::new(
Ski::default(),
Asn::from(64496u32),
vec![0xde, 0xad, 0xbe, 0xef],
))]);
let cache = RtrCacheBuilder::new()
.session_ids(SessionIds::from_array([42, 42, 42]))
.serial(100)
.timing(Timing::new(600, 600, 7200))
.snapshot(snapshot)
.build();
let server_cache = shared_cache(cache);
let (addr, shutdown_tx, server_handle) = start_session_server_returning_result(server_cache).await;
let mut client = TcpStream::connect(addr).await.unwrap();
ResetQuery::new(1).write(&mut client).await.unwrap();
let response = CacheResponse::read(&mut client).await.unwrap();
assert_eq!(response.version(), 1);
assert_eq!(response.session_id(), 42);
let read_res = timeout(Duration::from_secs(1), Header::read(&mut client))
.await
.expect("timed out waiting for server close");
assert!(read_res.is_err(), "server should close instead of sending invalid RouterKey");
let _ = shutdown_tx.send(true);
let join = timeout(Duration::from_secs(1), server_handle)
.await
.expect("server task did not exit within timeout")
.unwrap();
let err = join.expect_err("session should fail on invalid RouterKey");
assert!(err.to_string().contains("RouterKey SPKI"));
}