20260410 完成五个rir 基于cir的三方回放,raw by hash 独立db,发现内存占用大,连续大rir 录制发生oom
This commit is contained in:
@@ -82,7 +82,9 @@ pub fn verify_content_info_bytes(der: &[u8]) -> Result<CcrVerifySummary, CcrVeri
|
||||
verify_content_info(&content_info)
|
||||
}
|
||||
|
||||
pub fn verify_content_info(content_info: &CcrContentInfo) -> Result<CcrVerifySummary, CcrVerifyError> {
|
||||
pub fn verify_content_info(
|
||||
content_info: &CcrContentInfo,
|
||||
) -> Result<CcrVerifySummary, CcrVerifyError> {
|
||||
content_info.validate().map_err(CcrDecodeError::Validate)?;
|
||||
let state_hashes_ok = true;
|
||||
let mut manifest_instances = 0usize;
|
||||
@@ -108,7 +110,11 @@ pub fn verify_content_info(content_info: &CcrContentInfo) -> Result<CcrVerifySum
|
||||
return Err(CcrVerifyError::RoaHashMismatch);
|
||||
}
|
||||
roa_payload_sets = vrps.rps.len();
|
||||
roa_vrp_count = vrps.rps.iter().map(|set| count_roa_block_entries(&set.ip_addr_blocks)).sum();
|
||||
roa_vrp_count = vrps
|
||||
.rps
|
||||
.iter()
|
||||
.map(|set| count_roa_block_entries(&set.ip_addr_blocks))
|
||||
.sum();
|
||||
}
|
||||
if let Some(vaps) = &content_info.content.vaps {
|
||||
let payload_der = encode_aspa_payload_state_payload_der(&vaps.aps)
|
||||
@@ -154,10 +160,11 @@ pub fn verify_against_report_json_path(
|
||||
content_info: &CcrContentInfo,
|
||||
report_json_path: &Path,
|
||||
) -> Result<(), CcrVerifyError> {
|
||||
let bytes = std::fs::read(report_json_path)
|
||||
.map_err(|e| CcrVerifyError::ReportRead(report_json_path.display().to_string(), e.to_string()))?;
|
||||
let json: serde_json::Value = serde_json::from_slice(&bytes)
|
||||
.map_err(|e| CcrVerifyError::ReportParse(e.to_string()))?;
|
||||
let bytes = std::fs::read(report_json_path).map_err(|e| {
|
||||
CcrVerifyError::ReportRead(report_json_path.display().to_string(), e.to_string())
|
||||
})?;
|
||||
let json: serde_json::Value =
|
||||
serde_json::from_slice(&bytes).map_err(|e| CcrVerifyError::ReportParse(e.to_string()))?;
|
||||
|
||||
let report_vrps = report_vrp_keys(&json)?;
|
||||
let ccr_vrps = extract_vrp_rows(content_info)?;
|
||||
@@ -198,7 +205,9 @@ pub fn verify_against_vcir_store(
|
||||
let Some(mfts) = &content_info.content.mfts else {
|
||||
return Ok(());
|
||||
};
|
||||
let vcirs = store.list_vcirs().map_err(|e| CcrVerifyError::ListVcirs(e.to_string()))?;
|
||||
let vcirs = store
|
||||
.list_vcirs()
|
||||
.map_err(|e| CcrVerifyError::ListVcirs(e.to_string()))?;
|
||||
let mut vcir_hashes = BTreeSet::new();
|
||||
for vcir in vcirs {
|
||||
if let Some(artifact) = vcir.related_artifacts.iter().find(|artifact| {
|
||||
@@ -244,7 +253,9 @@ fn verify_router_key_state_hash(state: &RouterKeyState) -> Result<(), CcrVerifyE
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn report_vrp_keys(json: &serde_json::Value) -> Result<BTreeSet<(u32, String, u16)>, CcrVerifyError> {
|
||||
fn report_vrp_keys(
|
||||
json: &serde_json::Value,
|
||||
) -> Result<BTreeSet<(u32, String, u16)>, CcrVerifyError> {
|
||||
let mut out = BTreeSet::new();
|
||||
let Some(items) = json.get("vrps").and_then(|v| v.as_array()) else {
|
||||
return Ok(out);
|
||||
@@ -253,7 +264,8 @@ fn report_vrp_keys(json: &serde_json::Value) -> Result<BTreeSet<(u32, String, u1
|
||||
let asn = item
|
||||
.get("asn")
|
||||
.and_then(|v| v.as_u64())
|
||||
.ok_or_else(|| CcrVerifyError::ReportParse("vrps[].asn missing".into()))? as u32;
|
||||
.ok_or_else(|| CcrVerifyError::ReportParse("vrps[].asn missing".into()))?
|
||||
as u32;
|
||||
let prefix = item
|
||||
.get("prefix")
|
||||
.and_then(|v| v.as_str())
|
||||
@@ -285,7 +297,11 @@ fn report_aspa_keys(json: &serde_json::Value) -> Result<BTreeSet<(u32, Vec<u32>)
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or_else(|| CcrVerifyError::ReportParse("aspas[].provider_as_ids missing".into()))?
|
||||
.iter()
|
||||
.map(|v| v.as_u64().ok_or_else(|| CcrVerifyError::ReportParse("provider_as_ids[] invalid".into())).map(|v| v as u32))
|
||||
.map(|v| {
|
||||
v.as_u64()
|
||||
.ok_or_else(|| CcrVerifyError::ReportParse("provider_as_ids[] invalid".into()))
|
||||
.map(|v| v as u32)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
providers.sort_unstable();
|
||||
providers.dedup();
|
||||
@@ -294,7 +310,9 @@ fn report_aspa_keys(json: &serde_json::Value) -> Result<BTreeSet<(u32, Vec<u32>)
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn extract_vrp_rows(content_info: &CcrContentInfo) -> Result<BTreeSet<(u32, String, u16)>, CcrVerifyError> {
|
||||
pub fn extract_vrp_rows(
|
||||
content_info: &CcrContentInfo,
|
||||
) -> Result<BTreeSet<(u32, String, u16)>, CcrVerifyError> {
|
||||
let mut out = BTreeSet::new();
|
||||
let Some(vrps) = &content_info.content.vrps else {
|
||||
return Ok(out);
|
||||
@@ -311,7 +329,9 @@ pub fn extract_vrp_rows(content_info: &CcrContentInfo) -> Result<BTreeSet<(u32,
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn ccr_aspa_keys(content_info: &CcrContentInfo) -> Result<BTreeSet<(u32, Vec<u32>)>, CcrVerifyError> {
|
||||
fn ccr_aspa_keys(
|
||||
content_info: &CcrContentInfo,
|
||||
) -> Result<BTreeSet<(u32, Vec<u32>)>, CcrVerifyError> {
|
||||
let mut out = BTreeSet::new();
|
||||
let Some(vaps) = &content_info.content.vaps else {
|
||||
return Ok(out);
|
||||
@@ -322,24 +342,43 @@ fn ccr_aspa_keys(content_info: &CcrContentInfo) -> Result<BTreeSet<(u32, Vec<u32
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn decode_roa_family_block(block: &[u8]) -> Result<(u16, Vec<(u8, Vec<u8>, Option<u16>)>), CcrVerifyError> {
|
||||
fn decode_roa_family_block(
|
||||
block: &[u8],
|
||||
) -> Result<(u16, Vec<(u8, Vec<u8>, Option<u16>)>), CcrVerifyError> {
|
||||
let mut top = crate::data_model::common::DerReader::new(block);
|
||||
let mut seq = top.take_sequence().map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let mut seq = top
|
||||
.take_sequence()
|
||||
.map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
if !top.is_empty() {
|
||||
return Err(CcrVerifyError::Decode(CcrDecodeError::Parse("trailing bytes after ROAIPAddressFamily".into())));
|
||||
return Err(CcrVerifyError::Decode(CcrDecodeError::Parse(
|
||||
"trailing bytes after ROAIPAddressFamily".into(),
|
||||
)));
|
||||
}
|
||||
let afi_bytes = seq.take_octet_string().map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let afi_bytes = seq
|
||||
.take_octet_string()
|
||||
.map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let afi = u16::from_be_bytes([afi_bytes[0], afi_bytes[1]]);
|
||||
let mut addrs = seq.take_sequence().map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let mut addrs = seq
|
||||
.take_sequence()
|
||||
.map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let mut entries = Vec::new();
|
||||
while !addrs.is_empty() {
|
||||
let mut addr_seq = addrs.take_sequence().map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let (unused_bits, content) = addr_seq.take_bit_string().map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let mut addr_seq = addrs
|
||||
.take_sequence()
|
||||
.map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let (unused_bits, content) = addr_seq
|
||||
.take_bit_string()
|
||||
.map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?;
|
||||
let prefix_len = (content.len() * 8) as u8 - unused_bits;
|
||||
let max_len = if addr_seq.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(addr_seq.take_uint_u64().map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))? as u16)
|
||||
Some(
|
||||
addr_seq
|
||||
.take_uint_u64()
|
||||
.map_err(|e| CcrVerifyError::Decode(CcrDecodeError::Parse(e)))?
|
||||
as u16,
|
||||
)
|
||||
};
|
||||
entries.push((prefix_len, content.to_vec(), max_len));
|
||||
}
|
||||
@@ -358,37 +397,43 @@ fn format_prefix(afi: u16, addr_bytes: &[u8], prefix_len: u8) -> Result<String,
|
||||
full[..addr_bytes.len()].copy_from_slice(addr_bytes);
|
||||
Ok(format!("{}/{prefix_len}", std::net::Ipv6Addr::from(full)))
|
||||
}
|
||||
other => Err(CcrVerifyError::Decode(CcrDecodeError::Parse(format!("unsupported AFI {other}")))),
|
||||
other => Err(CcrVerifyError::Decode(CcrDecodeError::Parse(format!(
|
||||
"unsupported AFI {other}"
|
||||
)))),
|
||||
}
|
||||
}
|
||||
|
||||
fn count_roa_block_entries(blocks: &[Vec<u8>]) -> usize {
|
||||
blocks
|
||||
.iter()
|
||||
.map(|block| decode_roa_family_block(block).map(|(_, entries)| entries.len()).unwrap_or(0))
|
||||
.map(|block| {
|
||||
decode_roa_family_block(block)
|
||||
.map(|(_, entries)| entries.len())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ccr::build::{build_aspa_payload_state, build_roa_payload_state};
|
||||
use crate::ccr::encode::{
|
||||
encode_manifest_state_payload_der, encode_roa_payload_state_payload_der,
|
||||
encode_router_key_state_payload_der, encode_trust_anchor_state_payload_der,
|
||||
};
|
||||
use crate::ccr::build::{build_aspa_payload_state, build_roa_payload_state};
|
||||
use crate::ccr::model::{
|
||||
CcrDigestAlgorithm, ManifestInstance, ManifestState,
|
||||
RouterKey, RouterKeySet, RpkiCanonicalCacheRepresentation,
|
||||
CcrDigestAlgorithm, ManifestInstance, ManifestState, RouterKey, RouterKeySet,
|
||||
RpkiCanonicalCacheRepresentation,
|
||||
};
|
||||
use crate::data_model::roa::{IpPrefix, RoaAfi};
|
||||
use crate::validation::objects::{AspaAttestation, Vrp};
|
||||
use crate::data_model::common::BigUnsigned;
|
||||
use crate::data_model::roa::{IpPrefix, RoaAfi};
|
||||
use crate::storage::{
|
||||
PackTime, ValidatedCaInstanceResult, ValidatedManifestMeta, VcirArtifactKind,
|
||||
VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary, VcirChildEntry,
|
||||
VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
|
||||
};
|
||||
use crate::validation::objects::{AspaAttestation, Vrp};
|
||||
|
||||
fn sample_time() -> time::OffsetDateTime {
|
||||
time::OffsetDateTime::parse(
|
||||
@@ -415,21 +460,37 @@ mod tests {
|
||||
};
|
||||
let vrps = build_roa_payload_state(&[Vrp {
|
||||
asn: 64496,
|
||||
prefix: IpPrefix { afi: RoaAfi::Ipv4, prefix_len: 0, addr: [0; 16] },
|
||||
prefix: IpPrefix {
|
||||
afi: RoaAfi::Ipv4,
|
||||
prefix_len: 0,
|
||||
addr: [0; 16],
|
||||
},
|
||||
max_length: 0,
|
||||
}]).expect("build roa state");
|
||||
}])
|
||||
.expect("build roa state");
|
||||
let vaps = build_aspa_payload_state(&[AspaAttestation {
|
||||
customer_as_id: 64496,
|
||||
provider_as_ids: vec![64497],
|
||||
}]).expect("build aspa state");
|
||||
}])
|
||||
.expect("build aspa state");
|
||||
let skis = vec![vec![0x11; 20]];
|
||||
let tas = TrustAnchorState {
|
||||
hash: crate::ccr::compute_state_hash(&encode_trust_anchor_state_payload_der(&skis).unwrap()),
|
||||
hash: crate::ccr::compute_state_hash(
|
||||
&encode_trust_anchor_state_payload_der(&skis).unwrap(),
|
||||
),
|
||||
skis,
|
||||
};
|
||||
let rksets = vec![RouterKeySet { as_id: 64496, router_keys: vec![RouterKey { ski: vec![0x22;20], spki_der: vec![0x30,0x00] }] }];
|
||||
let rksets = vec![RouterKeySet {
|
||||
as_id: 64496,
|
||||
router_keys: vec![RouterKey {
|
||||
ski: vec![0x22; 20],
|
||||
spki_der: vec![0x30, 0x00],
|
||||
}],
|
||||
}];
|
||||
let rks = RouterKeyState {
|
||||
hash: crate::ccr::compute_state_hash(&encode_router_key_state_payload_der(&rksets).unwrap()),
|
||||
hash: crate::ccr::compute_state_hash(
|
||||
&encode_router_key_state_payload_der(&rksets).unwrap(),
|
||||
),
|
||||
rksets,
|
||||
};
|
||||
CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
@@ -448,19 +509,31 @@ mod tests {
|
||||
fn verify_detects_each_state_hash_mismatch() {
|
||||
let mut ci = sample_content_info();
|
||||
ci.content.vrps.as_mut().unwrap().hash[0] ^= 0x01;
|
||||
assert!(matches!(verify_content_info(&ci), Err(CcrVerifyError::RoaHashMismatch)));
|
||||
assert!(matches!(
|
||||
verify_content_info(&ci),
|
||||
Err(CcrVerifyError::RoaHashMismatch)
|
||||
));
|
||||
|
||||
let mut ci = sample_content_info();
|
||||
ci.content.vaps.as_mut().unwrap().hash[0] ^= 0x01;
|
||||
assert!(matches!(verify_content_info(&ci), Err(CcrVerifyError::AspaHashMismatch)));
|
||||
assert!(matches!(
|
||||
verify_content_info(&ci),
|
||||
Err(CcrVerifyError::AspaHashMismatch)
|
||||
));
|
||||
|
||||
let mut ci = sample_content_info();
|
||||
ci.content.tas.as_mut().unwrap().hash[0] ^= 0x01;
|
||||
assert!(matches!(verify_content_info(&ci), Err(CcrVerifyError::TrustAnchorHashMismatch)));
|
||||
assert!(matches!(
|
||||
verify_content_info(&ci),
|
||||
Err(CcrVerifyError::TrustAnchorHashMismatch)
|
||||
));
|
||||
|
||||
let mut ci = sample_content_info();
|
||||
ci.content.rks.as_mut().unwrap().hash[0] ^= 0x01;
|
||||
assert!(matches!(verify_content_info(&ci), Err(CcrVerifyError::RouterKeyHashMismatch)));
|
||||
assert!(matches!(
|
||||
verify_content_info(&ci),
|
||||
Err(CcrVerifyError::RouterKeyHashMismatch)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -472,20 +545,28 @@ mod tests {
|
||||
});
|
||||
let report_path = td.path().join("report.json");
|
||||
std::fs::write(&report_path, serde_json::to_vec(&report).unwrap()).unwrap();
|
||||
verify_against_report_json_path(&sample_content_info(), &report_path).expect("matching report");
|
||||
verify_against_report_json_path(&sample_content_info(), &report_path)
|
||||
.expect("matching report");
|
||||
|
||||
let bad_path = td.path().join("bad.json");
|
||||
std::fs::write(&bad_path, b"not-json").unwrap();
|
||||
assert!(matches!(verify_against_report_json_path(&sample_content_info(), &bad_path), Err(CcrVerifyError::ReportParse(_))));
|
||||
assert!(matches!(
|
||||
verify_against_report_json_path(&sample_content_info(), &bad_path),
|
||||
Err(CcrVerifyError::ReportParse(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_against_report_json_rejects_missing_fields_and_aspa_mismatch() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let missing = serde_json::json!({"vrps":[{"prefix":"0.0.0.0/0","max_length":0}],"aspas":[]});
|
||||
let missing =
|
||||
serde_json::json!({"vrps":[{"prefix":"0.0.0.0/0","max_length":0}],"aspas":[]});
|
||||
let missing_path = td.path().join("missing.json");
|
||||
std::fs::write(&missing_path, serde_json::to_vec(&missing).unwrap()).unwrap();
|
||||
assert!(matches!(verify_against_report_json_path(&sample_content_info(), &missing_path), Err(CcrVerifyError::ReportParse(_))));
|
||||
assert!(matches!(
|
||||
verify_against_report_json_path(&sample_content_info(), &missing_path),
|
||||
Err(CcrVerifyError::ReportParse(_))
|
||||
));
|
||||
|
||||
let mismatch = serde_json::json!({
|
||||
"vrps": [{"asn": 64496, "prefix": "0.0.0.0/0", "max_length": 0}],
|
||||
@@ -493,7 +574,10 @@ mod tests {
|
||||
});
|
||||
let mismatch_path = td.path().join("mismatch.json");
|
||||
std::fs::write(&mismatch_path, serde_json::to_vec(&mismatch).unwrap()).unwrap();
|
||||
assert!(matches!(verify_against_report_json_path(&sample_content_info(), &mismatch_path), Err(CcrVerifyError::ReportAspaMismatch { .. })));
|
||||
assert!(matches!(
|
||||
verify_against_report_json_path(&sample_content_info(), &mismatch_path),
|
||||
Err(CcrVerifyError::ReportAspaMismatch { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -542,11 +626,26 @@ mod tests {
|
||||
object_type: Some("mft".to_string()),
|
||||
validation_status: VcirArtifactValidationStatus::Accepted,
|
||||
}],
|
||||
summary: VcirSummary { local_vrp_count: 0, local_aspa_count: 0, local_router_key_count: 0, child_count: 1, accepted_object_count: 1, rejected_object_count: 0 },
|
||||
audit_summary: VcirAuditSummary { failed_fetch_eligible: true, last_failed_fetch_reason: None, warning_count: 0, audit_flags: Vec::new() },
|
||||
summary: VcirSummary {
|
||||
local_vrp_count: 0,
|
||||
local_aspa_count: 0,
|
||||
local_router_key_count: 0,
|
||||
child_count: 1,
|
||||
accepted_object_count: 1,
|
||||
rejected_object_count: 0,
|
||||
},
|
||||
audit_summary: VcirAuditSummary {
|
||||
failed_fetch_eligible: true,
|
||||
last_failed_fetch_reason: None,
|
||||
warning_count: 0,
|
||||
audit_flags: Vec::new(),
|
||||
},
|
||||
};
|
||||
store.put_vcir(&vcir).unwrap();
|
||||
assert!(matches!(verify_against_vcir_store(&sample_content_info(), &store), Err(CcrVerifyError::VcirManifestMismatch { .. })));
|
||||
assert!(matches!(
|
||||
verify_against_vcir_store(&sample_content_info(), &store),
|
||||
Err(CcrVerifyError::VcirManifestMismatch { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -557,12 +656,27 @@ mod tests {
|
||||
hash_alg: CcrDigestAlgorithm::Sha256,
|
||||
produced_at: sample_time(),
|
||||
mfts: None,
|
||||
vrps: Some(crate::ccr::model::RoaPayloadState { rps: vec![crate::ccr::model::RoaPayloadSet { as_id: 64496, ip_addr_blocks: vec![block.clone()] }], hash: crate::ccr::compute_state_hash(&encode_roa_payload_state_payload_der(&[crate::ccr::model::RoaPayloadSet { as_id: 64496, ip_addr_blocks: vec![block] }]).unwrap()) }),
|
||||
vrps: Some(crate::ccr::model::RoaPayloadState {
|
||||
rps: vec![crate::ccr::model::RoaPayloadSet {
|
||||
as_id: 64496,
|
||||
ip_addr_blocks: vec![block.clone()],
|
||||
}],
|
||||
hash: crate::ccr::compute_state_hash(
|
||||
&encode_roa_payload_state_payload_der(&[crate::ccr::model::RoaPayloadSet {
|
||||
as_id: 64496,
|
||||
ip_addr_blocks: vec![block],
|
||||
}])
|
||||
.unwrap(),
|
||||
),
|
||||
}),
|
||||
vaps: None,
|
||||
tas: None,
|
||||
rks: None,
|
||||
});
|
||||
assert!(matches!(extract_vrp_rows(&ci), Err(CcrVerifyError::Decode(_))));
|
||||
assert!(matches!(
|
||||
extract_vrp_rows(&ci),
|
||||
Err(CcrVerifyError::Decode(_))
|
||||
));
|
||||
let bad_count = count_roa_block_entries(&[vec![0x04, 0x00]]);
|
||||
assert_eq!(bad_count, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user