20260519_2 收紧CCR/CIR四文件分诊

This commit is contained in:
yuyr 2026-05-20 07:07:13 +08:00
parent 8c6fb44352
commit fcd0bac070
11 changed files with 618 additions and 748 deletions

View File

@ -4,8 +4,8 @@
"experiments": [
{
"id": "sync-ours-rsync-only",
"left": { "rpKind": "ours", "mode": "standard", "protocol": "rrdp+rsync" },
"right": { "rpKind": "ours", "mode": "standard", "protocol": "rsync-only" }
"left": { "rpKind": "ours", "mode": "standard", "protocol": "rrdp+rsync", "rsyncScope": "module-root" },
"right": { "rpKind": "ours", "mode": "standard", "protocol": "rsync-only", "rsyncScope": "module-root" }
},
{
"id": "sync-rpki-client-rsync-only",

View File

@ -302,7 +302,28 @@ def build_tool_binaries() -> None:
],
cwd=REPO_ROOT,
)
run_local(["make", "-j2"], cwd=PORTABLE_ROOT)
rpki_client_bin = PORTABLE_ROOT / "src" / "rpki-client"
if not rpki_client_bin.is_file():
run_local(["make", "-j2"], cwd=PORTABLE_ROOT)
smoke = run_local(
[str(rpki_client_bin), "-T", "invalid"],
capture=True,
check=False,
)
if "--ta-fixture requires <tal>:<path>" not in (smoke.stderr + smoke.stdout):
cache_bin = DEV_ROOT / ".cache" / "rpki-client-9.8-cir" / "rpki-client"
cache_smoke = run_local(
[str(cache_bin), "-T", "invalid"],
capture=True,
check=False,
) if cache_bin.is_file() else None
if cache_smoke and "--ta-fixture requires <tal>:<path>" in (cache_smoke.stderr + cache_smoke.stdout):
shutil.copy2(cache_bin, rpki_client_bin)
else:
raise SystemExit(
"rpki-client binary lacks local TA fixture support (-T); "
"switch to feature/cir-output-for-rp-compare and rebuild or restore the cached CIR+TA-fixture binary"
)
def build_fixture_proof(run_root: Path, rirs: list[str]) -> Path:
@ -398,6 +419,9 @@ def build_side_command(
)
if side["protocol"] == "rsync-only":
argv.append("--disable-rrdp")
rsync_scope = side.get("rsyncScope")
if rsync_scope:
argv.extend(["--rsync-scope", str(rsync_scope)])
if side["mode"] == "strict-name":
argv.extend(["--strict", "name"])
elif side["mode"] == "strict-cms-der":
@ -426,8 +450,6 @@ def build_side_command(
str(run_dir / "vrps.csv"),
"--vaps-csv-out",
str(run_dir / "vaps.csv"),
"--compare-view-trust-anchor",
compare_view_trust_anchor(rirs),
]
)
return "cd {run_dir} && {prefix} /usr/bin/time -v -o process-time.txt -- {cmd} > stdout.log 2> stderr.log".format(
@ -505,139 +527,6 @@ def cir_tal_uri_for_rir(rir: str) -> str:
}[rir]
def compare_view_trust_anchor(rirs: list[str]) -> str:
return "all5" if len(rirs) > 1 else rirs[0]
def sanitize_run_meta(
run_root: Path,
exp_id: str,
side_label: str,
step: str,
rp_kind: str,
rp_mode: str,
protocol: str,
strict_policies: str,
rirs: list[str],
run_dir: Path,
fixture_proof: Path,
result_ccr: Path,
result_cir: Path,
report_json: Path,
stage_timing_json: Path,
process_time: Path,
stdout_log: Path,
stderr_log: Path,
exit_code: int,
counts: dict[str, Any],
time_info: dict[str, Any],
fixture_pinned: bool,
) -> Path:
repo_root = run_root
run_meta_path = run_dir / "run-meta.json"
run_meta_args = [
sys.executable,
str(FEATURE_BUNDLE),
"run-meta",
"--out",
str(run_meta_path),
"--repo-root",
str(repo_root),
"--experiment-id",
exp_id,
"--side",
"left" if side_label == "A" else "right",
"--side-label",
side_label,
"--step",
step,
"--run-id",
f"{side_label}-{step}",
"--rp-kind",
rp_kind,
"--rp-binary",
"bin/rpki" if rp_kind == "ours" else "bin/rpki-client",
"--rp-version",
"portable" if rp_kind == "rpki-client" else "ours",
"--rp-mode",
rp_mode,
"--protocol-mode",
protocol,
"--strict-policies",
strict_policies,
"--rirs",
",".join(rirs),
"--argv-json",
json.dumps([]),
"--env-json",
json.dumps({}),
"--cwd",
str(run_root),
"--reset-before-run",
"--state-root",
str(run_dir.parent / "state"),
"--db",
str(run_dir.parent / "state" / "work-db"),
"--repo-bytes-db",
str(run_dir.parent / "state" / "repo-bytes.db"),
"--raw-store-db",
str(run_dir.parent / "state" / "raw-store.db"),
"--rsync-mirror-root",
str(run_dir.parent / "state" / "rsync-mirror"),
"--cache-root",
str(run_dir.parent / "state" / ("cache" if rp_kind == "rpki-client" else "work-db")),
"--ccr",
str(result_ccr),
"--cir",
str(result_cir),
"--fixture-proof",
str(fixture_proof),
"--fixture-proof-summary-json",
json.dumps(
{
"taFixturePinned": fixture_pinned,
"taOnlineFetchObserved": False,
"trustAnchorCount": len(rirs),
}
),
"--report-json",
str(report_json),
"--stage-timing-json",
str(stage_timing_json),
"--stdout-log",
str(stdout_log),
"--stderr-log",
str(stderr_log),
"--process-time",
str(process_time),
"--exit-code",
str(exit_code),
"--wall-ms",
str(time_info.get("wallMs", 0)),
"--max-rss-kb",
str(time_info.get("maxRssKb", 0)),
"--vrps",
str(counts.get("vrps", 0)),
"--vaps",
str(counts.get("aspas", 0)),
"--publication-points",
str(counts.get("publicationPoints", counts.get("repositories", 0))),
"--warnings",
str(counts.get("warnings", 0)),
"--cir-object-count",
str(counts.get("cirObjectCount", 0)),
"--cir-reject-count",
str(counts.get("cirRejectCount", 0)),
"--cir-trust-anchor-count",
str(counts.get("cirTrustAnchorCount", len(rirs))),
"--host",
os.uname().nodename,
"--platform",
sys.platform,
]
run_local(run_meta_args)
return run_meta_path
def run_remote_step(
ssh_target: str,
@ -686,6 +575,9 @@ def run_remote_step(
)
if side["protocol"] == "rsync-only":
argv.append("--disable-rrdp")
rsync_scope = side.get("rsyncScope")
if rsync_scope:
argv.extend(["--rsync-scope", str(rsync_scope)])
if side["mode"] == "strict-name":
argv.extend(["--strict", "name"])
elif side["mode"] == "strict-cms-der":
@ -714,8 +606,6 @@ def run_remote_step(
str(run_dir / "vrps.csv"),
"--vaps-csv-out",
str(run_dir / "vaps.csv"),
"--compare-view-trust-anchor",
compare_view_trust_anchor(rirs),
]
)
else:
@ -880,7 +770,7 @@ def generate_run_meta(
"--rp-mode",
side["mode"],
"--protocol-mode",
side["protocol"],
side["protocol"] + (f"/rsync-scope:{side['rsyncScope']}" if side.get("rsyncScope") else ""),
"--strict-policies",
strict_policies,
"--rirs",
@ -1046,20 +936,14 @@ def run_experiment(
str(local_exp_root / "A" / step / "result.ccr"),
"--left-cir",
str(local_exp_root / "A" / step / "result.cir"),
"--left-meta",
str(local_exp_root / "A" / step / "run-meta.json"),
"--right-ccr",
str(local_exp_root / "B" / step / "result.ccr"),
"--right-cir",
str(local_exp_root / "B" / step / "result.cir"),
"--right-meta",
str(local_exp_root / "B" / step / "run-meta.json"),
"--out-dir",
str(compare_dir),
"--sample-limit",
"200",
"--compare-view-trust-anchor",
compare_view_trust_anchor(rirs),
]
run_local(triage_cmd, cwd=local_exp_root)

View File

@ -303,9 +303,9 @@ fn build_compare_view_fallback(
fn decode_views(
content_info: &CcrContentInfo,
trust_anchor: &str,
_trust_anchor: &str,
) -> Result<(BTreeSet<VrpCompareRow>, BTreeSet<VapCompareRow>), String> {
decode_ccr_compare_views(content_info, trust_anchor)
decode_ccr_compare_views(content_info)
}
fn compare_sets<T: Ord + Clone>(ours: &BTreeSet<T>, rpki_client: &BTreeSet<T>) -> SetSummary<T> {

View File

@ -6,18 +6,14 @@ struct Args {
ccr_path: Option<std::path::PathBuf>,
vrps_out_path: Option<std::path::PathBuf>,
vaps_out_path: Option<std::path::PathBuf>,
trust_anchor: String,
}
fn usage() -> &'static str {
"Usage: ccr_to_compare_views --ccr <path> --vrps-out <path> --vaps-out <path> [--trust-anchor <name>]"
"Usage: ccr_to_compare_views --ccr <path> --vrps-out <path> --vaps-out <path> [--trust-anchor <ignored>]"
}
fn parse_args(argv: &[String]) -> Result<Args, String> {
let mut args = Args {
trust_anchor: "unknown".to_string(),
..Args::default()
};
let mut args = Args::default();
let mut i = 1usize;
while i < argv.len() {
match argv[i].as_str() {
@ -38,8 +34,7 @@ fn parse_args(argv: &[String]) -> Result<Args, String> {
}
"--trust-anchor" => {
i += 1;
let v = argv.get(i).ok_or("--trust-anchor requires a value")?;
args.trust_anchor = v.clone();
argv.get(i).ok_or("--trust-anchor requires a value")?;
}
"-h" | "--help" => return Err(usage().to_string()),
other => return Err(format!("unknown argument: {other}\n{}", usage())),
@ -64,8 +59,7 @@ fn main() -> Result<(), String> {
let bytes = std::fs::read(ccr_path)
.map_err(|e| format!("read ccr failed: {}: {e}", ccr_path.display()))?;
let content_info = decode_content_info(&bytes).map_err(|e| e.to_string())?;
let (vrps, vaps) =
decode_ccr_compare_views(&content_info, &args.trust_anchor).map_err(|e| e.to_string())?;
let (vrps, vaps) = decode_ccr_compare_views(&content_info).map_err(|e| e.to_string())?;
write_vrp_csv(args.vrps_out_path.as_ref().unwrap(), &vrps)?;
write_vap_csv(args.vaps_out_path.as_ref().unwrap(), &vaps)?;
println!(
@ -106,7 +100,6 @@ mod tests {
args.vaps_out_path.as_deref(),
Some(std::path::Path::new("vaps.csv"))
);
assert_eq!(args.trust_anchor, "apnic");
}
#[test]

View File

@ -5,18 +5,14 @@ use std::io::Write;
struct Args {
ccr_path: Option<std::path::PathBuf>,
out_path: Option<std::path::PathBuf>,
trust_anchor: String,
}
fn usage() -> &'static str {
"Usage: ccr_to_routinator_csv --ccr <path> --out <path> [--trust-anchor <name>]"
"Usage: ccr_to_routinator_csv --ccr <path> --out <path> [--trust-anchor <ignored>]"
}
fn parse_args(argv: &[String]) -> Result<Args, String> {
let mut args = Args {
trust_anchor: "unknown".to_string(),
..Args::default()
};
let mut args = Args::default();
let mut i = 1usize;
while i < argv.len() {
match argv[i].as_str() {
@ -32,8 +28,7 @@ fn parse_args(argv: &[String]) -> Result<Args, String> {
}
"--trust-anchor" => {
i += 1;
let v = argv.get(i).ok_or("--trust-anchor requires a value")?;
args.trust_anchor = v.clone();
argv.get(i).ok_or("--trust-anchor requires a value")?;
}
"-h" | "--help" => return Err(usage().to_string()),
other => return Err(format!("unknown argument: {other}\n{}", usage())),
@ -74,8 +69,7 @@ fn main() -> Result<(), String> {
);
writeln!(file, "ASN,IP Prefix,Max Length,Trust Anchor").map_err(|e| e.to_string())?;
for (asn, prefix, max_len) in rows {
writeln!(file, "AS{asn},{prefix},{max_len},{}", args.trust_anchor)
.map_err(|e| e.to_string())?;
writeln!(file, "AS{asn},{prefix},{max_len},unknown").map_err(|e| e.to_string())?;
}
println!("{}", out_path.display());
Ok(())
@ -105,7 +99,6 @@ mod tests {
args.out_path.as_deref(),
Some(std::path::Path::new("out.csv"))
);
assert_eq!(args.trust_anchor, "apnic");
}
#[test]

View File

@ -117,8 +117,8 @@ fn main() -> Result<(), String> {
&std::fs::read(&ccr_path).map_err(|e| format!("read ccr failed: {e}"))?,
)
.map_err(|e| format!("decode ccr failed: {e}"))?;
let (vrps, vaps) = decode_ccr_compare_views(&ccr, "unknown")
.map_err(|e| format!("decode compare views failed: {e}"))?;
let (vrps, vaps) =
decode_ccr_compare_views(&ccr).map_err(|e| format!("decode compare views failed: {e}"))?;
let report: serde_json::Value = serde_json::from_slice(
&std::fs::read(&report_path).map_err(|e| format!("read report failed: {e}"))?,
)

File diff suppressed because it is too large Load Diff

View File

@ -50,21 +50,18 @@ pub fn canonical_vrp_prefix(prefix: &crate::data_model::roa::IpPrefix) -> String
}
}
pub fn build_vrp_compare_rows(vrps: &[Vrp], trust_anchor: &str) -> BTreeSet<VrpCompareRow> {
pub fn build_vrp_compare_rows(vrps: &[Vrp]) -> BTreeSet<VrpCompareRow> {
vrps.iter()
.map(|vrp| VrpCompareRow {
asn: normalize_asn(vrp.asn),
ip_prefix: canonical_vrp_prefix(&vrp.prefix),
max_length: vrp.max_length.to_string(),
trust_anchor: trust_anchor.to_ascii_lowercase(),
trust_anchor: "unknown".to_string(),
})
.collect()
}
pub fn build_vap_compare_rows(
aspas: &[AspaAttestation],
trust_anchor: &str,
) -> BTreeSet<VapCompareRow> {
pub fn build_vap_compare_rows(aspas: &[AspaAttestation]) -> BTreeSet<VapCompareRow> {
aspas
.iter()
.map(|aspa| {
@ -78,7 +75,7 @@ pub fn build_vap_compare_rows(
.map(normalize_asn)
.collect::<Vec<_>>()
.join(";"),
trust_anchor: trust_anchor.to_ascii_lowercase(),
trust_anchor: "unknown".to_string(),
}
})
.collect()
@ -86,7 +83,6 @@ pub fn build_vap_compare_rows(
pub fn decode_ccr_compare_views(
content_info: &CcrContentInfo,
trust_anchor: &str,
) -> Result<(BTreeSet<VrpCompareRow>, BTreeSet<VapCompareRow>), String> {
let vrps = extract_vrp_rows(content_info)
.map_err(|e| format!("extract vrp rows from ccr failed: {e}"))?
@ -95,7 +91,7 @@ pub fn decode_ccr_compare_views(
asn: normalize_asn(asn),
ip_prefix: prefix,
max_length: max_length.to_string(),
trust_anchor: trust_anchor.to_ascii_lowercase(),
trust_anchor: "unknown".to_string(),
})
.collect::<BTreeSet<_>>();
@ -116,7 +112,7 @@ pub fn decode_ccr_compare_views(
.map(normalize_asn)
.collect::<Vec<_>>()
.join(";"),
trust_anchor: trust_anchor.to_ascii_lowercase(),
trust_anchor: "unknown".to_string(),
})
.collect::<BTreeSet<_>>()
})
@ -178,17 +174,14 @@ mod tests {
#[test]
fn build_vap_compare_rows_sorts_and_dedups_providers() {
let rows = build_vap_compare_rows(
&[AspaAttestation {
customer_as_id: 64496,
provider_as_ids: vec![64498, 64497, 64498],
}],
"APNIC",
);
let rows = build_vap_compare_rows(&[AspaAttestation {
customer_as_id: 64496,
provider_as_ids: vec![64498, 64497, 64498],
}]);
let row = rows.iter().next().expect("one row");
assert_eq!(row.customer_asn, "AS64496");
assert_eq!(row.providers, "AS64497;AS64498");
assert_eq!(row.trust_anchor, "apnic");
assert_eq!(row.trust_anchor, "unknown");
}
#[test]
@ -219,7 +212,7 @@ mod tests {
rks: None,
});
let (vrp_rows, vap_rows) =
decode_ccr_compare_views(&content, "apnic").expect("decode compare views");
decode_ccr_compare_views(&content).expect("decode compare views");
assert_eq!(vrp_rows.len(), 1);
assert_eq!(vap_rows.len(), 1);
assert_eq!(vap_rows.iter().next().unwrap().providers, "AS64497");
@ -227,18 +220,15 @@ mod tests {
#[test]
fn build_vrp_compare_rows_canonicalizes_ipv6_prefix_text() {
let rows = build_vrp_compare_rows(
&[Vrp {
asn: 64496,
prefix: IpPrefix {
afi: RoaAfi::Ipv6,
prefix_len: 32,
addr: [0x20, 0x01, 0x0d, 0xb8, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
},
max_length: 48,
}],
"APNIC",
);
let rows = build_vrp_compare_rows(&[Vrp {
asn: 64496,
prefix: IpPrefix {
afi: RoaAfi::Ipv6,
prefix_len: 32,
addr: [0x20, 0x01, 0x0d, 0xb8, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
},
max_length: 48,
}]);
let row = rows.iter().next().expect("row");
assert_eq!(row.ip_prefix, "2001:db8::/32");
}

View File

@ -123,7 +123,7 @@ fn ccr_to_routinator_csv_binary_writes_vrp_csv() {
);
let csv = std::fs::read_to_string(csv_path).expect("read csv");
assert!(csv.contains("ASN,IP Prefix,Max Length,Trust Anchor"));
assert!(csv.contains("AS64496,203.0.113.0/24,24,apnic"));
assert!(csv.contains("AS64496,203.0.113.0/24,24,unknown"));
}
#[test]
@ -193,9 +193,9 @@ fn ccr_to_compare_views_binary_writes_vrp_and_vap_csvs() {
let vrps_csv = std::fs::read_to_string(vrps_path).expect("read vrps csv");
let vaps_csv = std::fs::read_to_string(vaps_path).expect("read vaps csv");
assert!(vrps_csv.contains("ASN,IP Prefix,Max Length,Trust Anchor"));
assert!(vrps_csv.contains("AS64496,198.51.100.0/24,24,apnic"));
assert!(vrps_csv.contains("AS64496,198.51.100.0/24,24,unknown"));
assert!(vaps_csv.contains("Customer ASN,Providers,Trust Anchor"));
assert!(vaps_csv.contains("AS64496,AS64497;AS64498,apnic"));
assert!(vaps_csv.contains("AS64496,AS64497;AS64498,unknown"));
}
#[test]

View File

@ -57,7 +57,7 @@ fn run_case(
.expect("parse report");
let ccr = rpki::ccr::decode_content_info(&std::fs::read(&ccr_path).expect("read ccr"))
.expect("decode ccr");
let (vrps, vaps) = decode_ccr_compare_views(&ccr, "multi").expect("compare views");
let (vrps, vaps) = decode_ccr_compare_views(&ccr).expect("compare views");
(report, vrps, vaps, ccr)
}

View File

@ -64,10 +64,9 @@ fn offline_default_parallel_and_configured_phase2_match_compare_views() {
rpki::ccr::decode_content_info(&configured_ccr_bytes).expect("decode configured ccr");
let (default_vrps, default_vaps) =
rpki::ccr::decode_ccr_compare_views(&default_ccr, "apnic").expect("default compare view");
rpki::ccr::decode_ccr_compare_views(&default_ccr).expect("default compare view");
let (configured_vrps, configured_vaps) =
rpki::ccr::decode_ccr_compare_views(&configured_ccr, "apnic")
.expect("configured compare view");
rpki::ccr::decode_ccr_compare_views(&configured_ccr).expect("configured compare view");
assert_eq!(
default_vrps, configured_vrps,
@ -95,7 +94,7 @@ fn offline_default_parallel_and_configured_phase2_match_compare_views() {
fn offline_default_parallel_emits_online_ccr_accumulator_output() {
let (report, ccr_bytes) = run_offline_case(None);
let ccr = rpki::ccr::decode_content_info(&ccr_bytes).expect("decode ccr");
let (_vrps, _vaps) = rpki::ccr::decode_ccr_compare_views(&ccr, "apnic").expect("compare view");
let (_vrps, _vaps) = rpki::ccr::decode_ccr_compare_views(&ccr).expect("compare view");
assert!(
report["publication_points"]
.as_array()